diff options
author | Jaegeuk Kim <jaegeuk.kim@samsung.com> | 2014-04-28 05:12:36 -0400 |
---|---|---|
committer | Jaegeuk Kim <jaegeuk.kim@samsung.com> | 2014-05-06 21:21:57 -0400 |
commit | 7f7670fe9fe47e7e56db658eb8831febe47627f2 (patch) | |
tree | 17c349d23d99166cbf91753703a240b97c64cb1c /fs/f2fs/file.c | |
parent | fe369bc8ba205537864cb86ba08b390ad20201c4 (diff) |
f2fs: consider fallocated space for SEEK_DATA
If an amount of data are allocated though fallocate and user writes a couple of
data among the space, f2fs should return the data offset made by user when
SEEK_DATA is requested.
For example, (N: NEW_ADDR by fallocate, X: NEW_ADDR by user)
1) fallocate 0 ~ 10MB
f -> N N N N N N N N N N N N ... N
2) write 4KB at 5MB offset
f -> N N N N N X N N N N N N ... N
3) SEEK_DATA from 0 should return 5MB offset
So, this patch adds a routine to search the first dirty page to handle that.
Then, the SEEK_DATA flow skips NEW_ADDR offsets until any dirty page is found.
Signed-off-by: Jaegeuk Kim <jaegeuk.kim@samsung.com>
Diffstat (limited to 'fs/f2fs/file.c')
-rw-r--r-- | fs/f2fs/file.c | 45 |
1 files changed, 41 insertions, 4 deletions
diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index 31128571e284..b9f4fbf5c07e 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c | |||
@@ -19,6 +19,7 @@ | |||
19 | #include <linux/compat.h> | 19 | #include <linux/compat.h> |
20 | #include <linux/uaccess.h> | 20 | #include <linux/uaccess.h> |
21 | #include <linux/mount.h> | 21 | #include <linux/mount.h> |
22 | #include <linux/pagevec.h> | ||
22 | 23 | ||
23 | #include "f2fs.h" | 24 | #include "f2fs.h" |
24 | #include "node.h" | 25 | #include "node.h" |
@@ -194,13 +195,48 @@ out: | |||
194 | return ret; | 195 | return ret; |
195 | } | 196 | } |
196 | 197 | ||
198 | static pgoff_t __get_first_dirty_index(struct address_space *mapping, | ||
199 | pgoff_t pgofs, int whence) | ||
200 | { | ||
201 | struct pagevec pvec; | ||
202 | int nr_pages; | ||
203 | |||
204 | if (whence != SEEK_DATA) | ||
205 | return 0; | ||
206 | |||
207 | /* find first dirty page index */ | ||
208 | pagevec_init(&pvec, 0); | ||
209 | nr_pages = pagevec_lookup_tag(&pvec, mapping, &pgofs, PAGECACHE_TAG_DIRTY, 1); | ||
210 | pgofs = nr_pages ? pvec.pages[0]->index: LONG_MAX; | ||
211 | pagevec_release(&pvec); | ||
212 | return pgofs; | ||
213 | } | ||
214 | |||
215 | static bool __found_offset(block_t blkaddr, pgoff_t dirty, pgoff_t pgofs, | ||
216 | int whence) | ||
217 | { | ||
218 | switch (whence) { | ||
219 | case SEEK_DATA: | ||
220 | if ((blkaddr == NEW_ADDR && dirty == pgofs) || | ||
221 | (blkaddr != NEW_ADDR && blkaddr != NULL_ADDR)) | ||
222 | return true; | ||
223 | break; | ||
224 | case SEEK_HOLE: | ||
225 | if (blkaddr == NULL_ADDR) | ||
226 | return true; | ||
227 | break; | ||
228 | } | ||
229 | return false; | ||
230 | } | ||
231 | |||
197 | static loff_t f2fs_seek_block(struct file *file, loff_t offset, int whence) | 232 | static loff_t f2fs_seek_block(struct file *file, loff_t offset, int whence) |
198 | { | 233 | { |
199 | struct inode *inode = file->f_mapping->host; | 234 | struct inode *inode = file->f_mapping->host; |
200 | loff_t maxbytes = inode->i_sb->s_maxbytes; | 235 | loff_t maxbytes = inode->i_sb->s_maxbytes; |
201 | struct dnode_of_data dn; | 236 | struct dnode_of_data dn; |
202 | pgoff_t pgofs, end_offset; | 237 | pgoff_t pgofs, end_offset, dirty; |
203 | loff_t data_ofs = offset, isize; | 238 | loff_t data_ofs = offset; |
239 | loff_t isize; | ||
204 | int err = 0; | 240 | int err = 0; |
205 | 241 | ||
206 | mutex_lock(&inode->i_mutex); | 242 | mutex_lock(&inode->i_mutex); |
@@ -218,6 +254,8 @@ static loff_t f2fs_seek_block(struct file *file, loff_t offset, int whence) | |||
218 | 254 | ||
219 | pgofs = (pgoff_t)(offset >> PAGE_CACHE_SHIFT); | 255 | pgofs = (pgoff_t)(offset >> PAGE_CACHE_SHIFT); |
220 | 256 | ||
257 | dirty = __get_first_dirty_index(inode->i_mapping, pgofs, whence); | ||
258 | |||
221 | for (; data_ofs < isize; data_ofs = pgofs << PAGE_CACHE_SHIFT) { | 259 | for (; data_ofs < isize; data_ofs = pgofs << PAGE_CACHE_SHIFT) { |
222 | set_new_dnode(&dn, inode, NULL, NULL, 0); | 260 | set_new_dnode(&dn, inode, NULL, NULL, 0); |
223 | err = get_dnode_of_data(&dn, pgofs, LOOKUP_NODE_RA); | 261 | err = get_dnode_of_data(&dn, pgofs, LOOKUP_NODE_RA); |
@@ -244,8 +282,7 @@ static loff_t f2fs_seek_block(struct file *file, loff_t offset, int whence) | |||
244 | block_t blkaddr; | 282 | block_t blkaddr; |
245 | blkaddr = datablock_addr(dn.node_page, dn.ofs_in_node); | 283 | blkaddr = datablock_addr(dn.node_page, dn.ofs_in_node); |
246 | 284 | ||
247 | if ((whence == SEEK_DATA && blkaddr != NULL_ADDR) || | 285 | if (__found_offset(blkaddr, dirty, pgofs, whence)) { |
248 | (whence == SEEK_HOLE && blkaddr == NULL_ADDR)) { | ||
249 | f2fs_put_dnode(&dn); | 286 | f2fs_put_dnode(&dn); |
250 | goto found; | 287 | goto found; |
251 | } | 288 | } |