summaryrefslogtreecommitdiffstats
path: root/fs/f2fs/file.c
diff options
context:
space:
mode:
authorHuajun Li <huajun.li@intel.com>2013-11-10 23:13:20 +0800
committerJaegeuk Kim <jaegeuk.kim@samsung.com>2013-12-26 20:40:41 +0900
commit9ffe0fb5f3bbd01c766162bb17a62dee0df7e125 (patch)
tree055460e4451d562f1a59792eeed3cb840e6c7d22 /fs/f2fs/file.c
parente18c65b2ac91aa59f89333da595d5155184f76cf (diff)
downloadop-kernel-dev-9ffe0fb5f3bbd01c766162bb17a62dee0df7e125.zip
op-kernel-dev-9ffe0fb5f3bbd01c766162bb17a62dee0df7e125.tar.gz
f2fs: handle inline data operations
Hook inline data read/write, truncate, fallocate, setattr, etc. Files need meet following 2 requirement to inline: 1) file size is not greater than MAX_INLINE_DATA; 2) file doesn't pre-allocate data blocks by fallocate(). FI_INLINE_DATA will not be set while creating a new regular inode because most of the files are bigger than ~3.4K. Set FI_INLINE_DATA only when data is submitted to block layer, ranther than set it while creating a new inode, this also avoids converting data from inline to normal data block and vice versa. While writting inline data to inode block, the first data block should be released if the file has a block indexed by i_addr[0]. On the other hand, when a file operation is appied to a file with inline data, we need to test if this file can remain inline by doing this operation, otherwise it should be convert into normal file by reserving a new data block, copying inline data to this new block and clear FI_INLINE_DATA flag. Because reserve a new data block here will make use of i_addr[0], if we save inline data in i_addr[0..872], then the first 4 bytes would be overwriten. This problem can be avoided simply by not using i_addr[0] for inline data. Signed-off-by: Huajun Li <huajun.li@intel.com> Signed-off-by: Haicheng Li <haicheng.li@linux.intel.com> Signed-off-by: Weihong Xu <weihong.xu@intel.com> Signed-off-by: Jaegeuk Kim <jaegeuk.kim@samsung.com>
Diffstat (limited to 'fs/f2fs/file.c')
-rw-r--r--fs/f2fs/file.c45
1 files changed, 42 insertions, 3 deletions
diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c
index 5accc96..dd80e72 100644
--- a/fs/f2fs/file.c
+++ b/fs/f2fs/file.c
@@ -257,8 +257,7 @@ static int truncate_blocks(struct inode *inode, u64 from)
unsigned int blocksize = inode->i_sb->s_blocksize;
struct dnode_of_data dn;
pgoff_t free_from;
- int count = 0;
- int err;
+ int count = 0, err = 0;
trace_f2fs_truncate_blocks_enter(inode, from);
@@ -266,6 +265,10 @@ static int truncate_blocks(struct inode *inode, u64 from)
((from + blocksize - 1) >> (sbi->log_blocksize));
f2fs_lock_op(sbi);
+
+ if (f2fs_has_inline_data(inode))
+ goto done;
+
set_new_dnode(&dn, inode, NULL, NULL, 0);
err = get_dnode_of_data(&dn, free_from, LOOKUP_NODE);
if (err) {
@@ -292,6 +295,7 @@ static int truncate_blocks(struct inode *inode, u64 from)
f2fs_put_dnode(&dn);
free_next:
err = truncate_inode_blocks(inode, free_from);
+done:
f2fs_unlock_op(sbi);
/* lastly zero out the first data page */
@@ -367,8 +371,17 @@ int f2fs_setattr(struct dentry *dentry, struct iattr *attr)
if ((attr->ia_valid & ATTR_SIZE) &&
attr->ia_size != i_size_read(inode)) {
+ if (f2fs_has_inline_data(inode) &&
+ (attr->ia_size > MAX_INLINE_DATA)) {
+ unsigned flags = AOP_FLAG_NOFS;
+ err = f2fs_convert_inline_data(inode, NULL, flags);
+ if (err)
+ return err;
+ }
+
truncate_setsize(inode, attr->ia_size);
- f2fs_truncate(inode);
+ if (!f2fs_has_inline_data(inode))
+ f2fs_truncate(inode);
f2fs_balance_fs(F2FS_SB(inode->i_sb));
}
@@ -450,6 +463,26 @@ static int punch_hole(struct inode *inode, loff_t offset, loff_t len)
loff_t off_start, off_end;
int ret = 0;
+ if (f2fs_has_inline_data(inode)) {
+ struct page *page;
+ unsigned flags = AOP_FLAG_NOFS;
+ page = grab_cache_page_write_begin(inode->i_mapping, 0, flags);
+ if (IS_ERR(page))
+ return PTR_ERR(page);
+ if (offset + len > MAX_INLINE_DATA) {
+ ret = f2fs_convert_inline_data(inode, page, flags);
+ f2fs_put_page(page, 1);
+ if (ret)
+ return ret;
+ } else {
+ zero_user_segment(page, offset, offset + len);
+ SetPageUptodate(page);
+ set_page_dirty(page);
+ f2fs_put_page(page, 1);
+ return ret;
+ }
+ }
+
pg_start = ((unsigned long long) offset) >> PAGE_CACHE_SHIFT;
pg_end = ((unsigned long long) offset + len) >> PAGE_CACHE_SHIFT;
@@ -496,6 +529,12 @@ static int expand_inode_data(struct inode *inode, loff_t offset,
loff_t off_start, off_end;
int ret = 0;
+ if (f2fs_has_inline_data(inode) && (offset + len > MAX_INLINE_DATA)) {
+ ret = f2fs_convert_inline_data(inode, NULL, AOP_FLAG_NOFS);
+ if (ret)
+ return ret;
+ }
+
ret = inode_newsize_ok(inode, (len + offset));
if (ret)
return ret;
OpenPOWER on IntegriCloud