diff options
author | Jaegeuk Kim <jaegeuk.kim@samsung.com> | 2014-04-28 11:12:36 +0200 |
---|---|---|
committer | Jaegeuk Kim <jaegeuk.kim@samsung.com> | 2014-05-07 03:21:57 +0200 |
commit | 7f7670fe9fe47e7e56db658eb8831febe47627f2 (patch) | |
tree | 17c349d23d99166cbf91753703a240b97c64cb1c /fs/f2fs/file.c | |
parent | f2fs: return i_size if the hole is outside of i_size (diff) | |
download | linux-7f7670fe9fe47e7e56db658eb8831febe47627f2.tar.xz linux-7f7670fe9fe47e7e56db658eb8831febe47627f2.zip |
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 '')
-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 @@ #include <linux/compat.h> #include <linux/uaccess.h> #include <linux/mount.h> +#include <linux/pagevec.h> #include "f2fs.h" #include "node.h" @@ -194,13 +195,48 @@ out: return ret; } +static pgoff_t __get_first_dirty_index(struct address_space *mapping, + pgoff_t pgofs, int whence) +{ + struct pagevec pvec; + int nr_pages; + + if (whence != SEEK_DATA) + return 0; + + /* find first dirty page index */ + pagevec_init(&pvec, 0); + nr_pages = pagevec_lookup_tag(&pvec, mapping, &pgofs, PAGECACHE_TAG_DIRTY, 1); + pgofs = nr_pages ? pvec.pages[0]->index: LONG_MAX; + pagevec_release(&pvec); + return pgofs; +} + +static bool __found_offset(block_t blkaddr, pgoff_t dirty, pgoff_t pgofs, + int whence) +{ + switch (whence) { + case SEEK_DATA: + if ((blkaddr == NEW_ADDR && dirty == pgofs) || + (blkaddr != NEW_ADDR && blkaddr != NULL_ADDR)) + return true; + break; + case SEEK_HOLE: + if (blkaddr == NULL_ADDR) + return true; + break; + } + return false; +} + static loff_t f2fs_seek_block(struct file *file, loff_t offset, int whence) { struct inode *inode = file->f_mapping->host; loff_t maxbytes = inode->i_sb->s_maxbytes; struct dnode_of_data dn; - pgoff_t pgofs, end_offset; - loff_t data_ofs = offset, isize; + pgoff_t pgofs, end_offset, dirty; + loff_t data_ofs = offset; + loff_t isize; int err = 0; mutex_lock(&inode->i_mutex); @@ -218,6 +254,8 @@ static loff_t f2fs_seek_block(struct file *file, loff_t offset, int whence) pgofs = (pgoff_t)(offset >> PAGE_CACHE_SHIFT); + dirty = __get_first_dirty_index(inode->i_mapping, pgofs, whence); + for (; data_ofs < isize; data_ofs = pgofs << PAGE_CACHE_SHIFT) { set_new_dnode(&dn, inode, NULL, NULL, 0); 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) block_t blkaddr; blkaddr = datablock_addr(dn.node_page, dn.ofs_in_node); - if ((whence == SEEK_DATA && blkaddr != NULL_ADDR) || - (whence == SEEK_HOLE && blkaddr == NULL_ADDR)) { + if (__found_offset(blkaddr, dirty, pgofs, whence)) { f2fs_put_dnode(&dn); goto found; } |