From 1001b3479ce96e37aed5e4fcdc3c60126e034d08 Mon Sep 17 00:00:00 2001 From: Huajun Li Date: Sun, 10 Nov 2013 23:13:16 +0800 Subject: f2fs: add flags and helpers to support inline data Add new inode flags F2FS_INLINE_DATA and FI_INLINE_DATA to indicate whether the inode has inline data. Inline data makes use of inode block's data indices region to save small file. Currently there are 923 data indices in an inode block. Since inline xattr has made use of the last 50 indices to save its data, there are 873 indices left which can be used for inline data. When FI_INLINE_DATA is set, the layout of inode block's indices region is like below: +-----------------+ | | Reserved. reserve_new_block() will make use of | i_addr[0] | i_addr[0] when we need to reserve a new data block | | to convert inline data into regular one's. |-----------------| | | Used by inline data. A file whose size is less than | i_addr[1~872] | 3488 bytes(~3.4k) and doesn't reserve extra | | blocks by fallocate() can be saved here. |-----------------| | | | i_addr[873~922] | Reserved for inline xattr | | +-----------------+ Signed-off-by: Haicheng Li Signed-off-by: Huajun Li Signed-off-by: Weihong Xu Signed-off-by: Jaegeuk Kim --- include/linux/f2fs_fs.h | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'include/linux') diff --git a/include/linux/f2fs_fs.h b/include/linux/f2fs_fs.h index bb942f6d5702..aea5eedbb1ca 100644 --- a/include/linux/f2fs_fs.h +++ b/include/linux/f2fs_fs.h @@ -153,6 +153,14 @@ struct f2fs_extent { #define NODE_DIND_BLOCK (DEF_ADDRS_PER_INODE + 5) #define F2FS_INLINE_XATTR 0x01 /* file inline xattr flag */ +#define F2FS_INLINE_DATA 0x02 /* file inline data flag */ + + +#define MAX_INLINE_DATA (sizeof(__le32) * (DEF_ADDRS_PER_INODE - \ + F2FS_INLINE_XATTR_ADDRS - 1)) + +#define INLINE_DATA_OFFSET (PAGE_CACHE_SIZE - sizeof(struct node_footer) \ + - sizeof(__le32)*(DEF_ADDRS_PER_INODE + 5 - 1)) struct f2fs_inode { __le16 i_mode; /* file mode */ -- cgit v1.2.3 From 1e1bb4baf10be371f72150e2801d97a04d40b3b9 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Thu, 26 Dec 2013 12:49:48 +0900 Subject: f2fs: add inline_data recovery routine This patch adds a inline_data recovery routine with the following policy. [prev.] [next] of inline_data flag o o -> recover inline_data o x -> remove inline_data, and then recover data blocks x o -> remove inline_data, and then recover inline_data x x -> recover data blocks Signed-off-by: Jaegeuk Kim --- fs/f2fs/f2fs.h | 2 ++ fs/f2fs/file.c | 2 +- fs/f2fs/inline.c | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ fs/f2fs/recovery.c | 8 ++++++-- include/linux/f2fs_fs.h | 3 +-- 5 files changed, 58 insertions(+), 5 deletions(-) (limited to 'include/linux') diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 0b6badadbfd8..07a7ae0d4413 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -1015,6 +1015,7 @@ static inline int f2fs_readonly(struct super_block *sb) */ int f2fs_sync_file(struct file *, loff_t, loff_t, int); void truncate_data_blocks(struct dnode_of_data *); +int truncate_blocks(struct inode *, u64); void f2fs_truncate(struct inode *); int f2fs_getattr(struct vfsmount *, struct dentry *, struct kstat *); int f2fs_setattr(struct dentry *, struct iattr *); @@ -1322,4 +1323,5 @@ bool f2fs_may_inline(struct inode *); int f2fs_read_inline_data(struct inode *, struct page *); int f2fs_convert_inline_data(struct inode *, pgoff_t); int f2fs_write_inline_data(struct inode *, struct page *, unsigned int); +int recover_inline_data(struct inode *, struct page *); #endif diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index 68dd7bfce1a1..c77ad4d8b564 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -251,7 +251,7 @@ static void truncate_partial_data_page(struct inode *inode, u64 from) f2fs_put_page(page, 1); } -static int truncate_blocks(struct inode *inode, u64 from) +int truncate_blocks(struct inode *inode, u64 from) { struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb); unsigned int blocksize = inode->i_sb->s_blocksize; diff --git a/fs/f2fs/inline.c b/fs/f2fs/inline.c index 3c9261cd215f..2a756e57aed9 100644 --- a/fs/f2fs/inline.c +++ b/fs/f2fs/inline.c @@ -169,3 +169,51 @@ int f2fs_write_inline_data(struct inode *inode, return 0; } + +int recover_inline_data(struct inode *inode, struct page *npage) +{ + struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb); + struct f2fs_inode *ri = NULL; + void *src_addr, *dst_addr; + struct page *ipage; + + /* + * The inline_data recovery policy is as follows. + * [prev.] [next] of inline_data flag + * o o -> recover inline_data + * o x -> remove inline_data, and then recover data blocks + * x o -> remove inline_data, and then recover inline_data + * x x -> recover data blocks + */ + if (IS_INODE(npage)) + ri = F2FS_INODE(npage); + + if (f2fs_has_inline_data(inode) && + ri && ri->i_inline & F2FS_INLINE_DATA) { +process_inline: + ipage = get_node_page(sbi, inode->i_ino); + f2fs_bug_on(IS_ERR(ipage)); + + src_addr = inline_data_addr(npage); + dst_addr = inline_data_addr(ipage); + memcpy(dst_addr, src_addr, MAX_INLINE_DATA); + update_inode(inode, ipage); + f2fs_put_page(ipage, 1); + return -1; + } + + if (f2fs_has_inline_data(inode)) { + ipage = get_node_page(sbi, inode->i_ino); + f2fs_bug_on(IS_ERR(ipage)); + zero_user_segment(ipage, INLINE_DATA_OFFSET, + INLINE_DATA_OFFSET + MAX_INLINE_DATA); + clear_inode_flag(F2FS_I(inode), FI_INLINE_DATA); + update_inode(inode, ipage); + f2fs_put_page(ipage, 1); + } else if (ri && ri->i_inline & F2FS_INLINE_DATA) { + truncate_blocks(inode, 0); + set_inode_flag(F2FS_I(inode), FI_INLINE_DATA); + goto process_inline; + } + return 0; +} diff --git a/fs/f2fs/recovery.c b/fs/f2fs/recovery.c index 96e690b6f0fa..655791e518cf 100644 --- a/fs/f2fs/recovery.c +++ b/fs/f2fs/recovery.c @@ -298,6 +298,9 @@ static int do_recover_data(struct f2fs_sb_info *sbi, struct inode *inode, struct node_info ni; int err = 0, recovered = 0; + if (recover_inline_data(inode, page)) + goto out; + start = start_bidx_of_node(ofs_of_node(page), fi); if (IS_INODE(page)) end = start + ADDRS_PER_INODE(fi); @@ -305,12 +308,13 @@ static int do_recover_data(struct f2fs_sb_info *sbi, struct inode *inode, end = start + ADDRS_PER_BLOCK; f2fs_lock_op(sbi); + set_new_dnode(&dn, inode, NULL, NULL, 0); err = get_dnode_of_data(&dn, start, ALLOC_NODE); if (err) { f2fs_unlock_op(sbi); - return err; + goto out; } wait_on_page_writeback(dn.node_page); @@ -361,7 +365,7 @@ static int do_recover_data(struct f2fs_sb_info *sbi, struct inode *inode, err: f2fs_put_dnode(&dn); f2fs_unlock_op(sbi); - +out: f2fs_msg(sbi->sb, KERN_NOTICE, "recover_data: ino = %lx, " "recovered_data = %d blocks, err = %d", inode->i_ino, recovered, err); diff --git a/include/linux/f2fs_fs.h b/include/linux/f2fs_fs.h index aea5eedbb1ca..da74d878dc4f 100644 --- a/include/linux/f2fs_fs.h +++ b/include/linux/f2fs_fs.h @@ -155,12 +155,11 @@ struct f2fs_extent { #define F2FS_INLINE_XATTR 0x01 /* file inline xattr flag */ #define F2FS_INLINE_DATA 0x02 /* file inline data flag */ - #define MAX_INLINE_DATA (sizeof(__le32) * (DEF_ADDRS_PER_INODE - \ F2FS_INLINE_XATTR_ADDRS - 1)) #define INLINE_DATA_OFFSET (PAGE_CACHE_SIZE - sizeof(struct node_footer) \ - - sizeof(__le32)*(DEF_ADDRS_PER_INODE + 5 - 1)) + - sizeof(__le32) * (DEF_ADDRS_PER_INODE + 5 - 1)) struct f2fs_inode { __le16 i_mode; /* file mode */ -- cgit v1.2.3