From e18c65b2ac91aa59f89333da595d5155184f76cf Mon Sep 17 00:00:00 2001 From: Huajun Li Date: Sun, 10 Nov 2013 23:13:19 +0800 Subject: f2fs: key functions to handle inline data Functions to implement inline data read/write, and move inline data to normal data block when file size exceeds inline data limitation. Signed-off-by: Huajun Li Signed-off-by: Haicheng Li Signed-off-by: Weihong Xu Signed-off-by: Jaegeuk Kim --- fs/f2fs/inline.c | 176 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 176 insertions(+) create mode 100644 fs/f2fs/inline.c (limited to 'fs/f2fs/inline.c') diff --git a/fs/f2fs/inline.c b/fs/f2fs/inline.c new file mode 100644 index 000000000000..62c72aa84acc --- /dev/null +++ b/fs/f2fs/inline.c @@ -0,0 +1,176 @@ +/* + * fs/f2fs/inline.c + * Copyright (c) 2013, Intel Corporation + * Authors: Huajun Li + * Haicheng Li + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include + +#include "f2fs.h" + +inline int f2fs_has_inline_data(struct inode *inode) +{ + return is_inode_flag_set(F2FS_I(inode), FI_INLINE_DATA); +} + +bool f2fs_may_inline(struct inode *inode) +{ + struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb); + block_t nr_blocks; + loff_t i_size; + + if (!test_opt(sbi, INLINE_DATA)) + return false; + + nr_blocks = F2FS_I(inode)->i_xattr_nid ? 3 : 2; + if (inode->i_blocks > nr_blocks) + return false; + + i_size = i_size_read(inode); + if (i_size > MAX_INLINE_DATA) + return false; + + return true; +} + +int f2fs_read_inline_data(struct inode *inode, struct page *page) +{ + struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb); + struct page *ipage; + void *src_addr, *dst_addr; + + ipage = get_node_page(sbi, inode->i_ino); + if (IS_ERR(ipage)) + return PTR_ERR(ipage); + + zero_user_segment(page, INLINE_DATA_OFFSET, + INLINE_DATA_OFFSET + MAX_INLINE_DATA); + + /* Copy the whole inline data block */ + src_addr = inline_data_addr(ipage); + dst_addr = kmap(page); + memcpy(dst_addr, src_addr, MAX_INLINE_DATA); + kunmap(page); + f2fs_put_page(ipage, 1); + + SetPageUptodate(page); + unlock_page(page); + + return 0; +} + +static int __f2fs_convert_inline_data(struct inode *inode, struct page *page) +{ + int err; + struct page *ipage; + struct dnode_of_data dn; + void *src_addr, *dst_addr; + block_t new_blk_addr; + struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb); + struct f2fs_io_info fio = { + .type = DATA, + .rw = WRITE_SYNC | REQ_PRIO, + }; + + f2fs_lock_op(sbi); + ipage = get_node_page(sbi, inode->i_ino); + if (IS_ERR(ipage)) + return PTR_ERR(ipage); + + /* + * i_addr[0] is not used for inline data, + * so reserving new block will not destroy inline data + */ + set_new_dnode(&dn, inode, ipage, ipage, 0); + err = f2fs_reserve_block(&dn, 0); + if (err) { + f2fs_put_page(ipage, 1); + f2fs_unlock_op(sbi); + return err; + } + + zero_user_segment(page, 0, PAGE_CACHE_SIZE); + + /* Copy the whole inline data block */ + src_addr = inline_data_addr(ipage); + dst_addr = kmap(page); + memcpy(dst_addr, src_addr, MAX_INLINE_DATA); + kunmap(page); + + /* write data page to try to make data consistent */ + set_page_writeback(page); + write_data_page(page, &dn, &new_blk_addr, &fio); + update_extent_cache(new_blk_addr, &dn); + f2fs_wait_on_page_writeback(page, DATA, true); + + /* clear inline data and flag after data writeback */ + zero_user_segment(ipage, INLINE_DATA_OFFSET, + INLINE_DATA_OFFSET + MAX_INLINE_DATA); + clear_inode_flag(F2FS_I(inode), FI_INLINE_DATA); + + sync_inode_page(&dn); + f2fs_put_page(ipage, 1); + f2fs_unlock_op(sbi); + + return err; +} + +int f2fs_convert_inline_data(struct inode *inode, + struct page *p, unsigned flags) +{ + int err; + struct page *page; + + if (!p || p->index) { + page = grab_cache_page_write_begin(inode->i_mapping, 0, flags); + if (IS_ERR(page)) + return PTR_ERR(page); + } else { + page = p; + } + + err = __f2fs_convert_inline_data(inode, page); + + if (!p || p->index) + f2fs_put_page(page, 1); + + return err; +} + +int f2fs_write_inline_data(struct inode *inode, + struct page *page, unsigned size) +{ + void *src_addr, *dst_addr; + struct page *ipage; + struct dnode_of_data dn; + int err; + + set_new_dnode(&dn, inode, NULL, NULL, 0); + err = get_dnode_of_data(&dn, 0, LOOKUP_NODE); + if (err) + return err; + ipage = dn.inode_page; + + zero_user_segment(ipage, INLINE_DATA_OFFSET, + INLINE_DATA_OFFSET + MAX_INLINE_DATA); + src_addr = kmap(page); + dst_addr = inline_data_addr(ipage); + memcpy(dst_addr, src_addr, size); + kunmap(page); + + /* Release the first data block if it is allocated */ + if (!f2fs_has_inline_data(inode)) { + truncate_data_blocks_range(&dn, 1); + set_inode_flag(F2FS_I(inode), FI_INLINE_DATA); + } + + sync_inode_page(&dn); + f2fs_put_dnode(&dn); + + return 0; +} -- cgit v1.2.3 From 9e09fc855dd6f6ed510b3db7f3c3c1dd73631ac7 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Fri, 27 Dec 2013 12:28:59 +0900 Subject: f2fs: refactor f2fs_convert_inline_data Change log from v1: o handle NULL pointer of grab_cache_page_write_begin() pointed by Chao Yu. This patch refactors f2fs_convert_inline_data to check a couple of conditions internally for deciding whether it needs to convert inline_data or not. So, the new f2fs_convert_inline_data initially checks: 1) f2fs_has_inline_data(), and 2) the data size to be changed. If the inode has inline_data but the size to fill is less than MAX_INLINE_DATA, then we don't need to convert the inline_data with data allocation. Signed-off-by: Jaegeuk Kim --- fs/f2fs/inline.c | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) (limited to 'fs/f2fs/inline.c') diff --git a/fs/f2fs/inline.c b/fs/f2fs/inline.c index 62c72aa84acc..e8891aa3ab8c 100644 --- a/fs/f2fs/inline.c +++ b/fs/f2fs/inline.c @@ -101,6 +101,7 @@ static int __f2fs_convert_inline_data(struct inode *inode, struct page *page) dst_addr = kmap(page); memcpy(dst_addr, src_addr, MAX_INLINE_DATA); kunmap(page); + SetPageUptodate(page); /* write data page to try to make data consistent */ set_page_writeback(page); @@ -120,25 +121,22 @@ static int __f2fs_convert_inline_data(struct inode *inode, struct page *page) return err; } -int f2fs_convert_inline_data(struct inode *inode, - struct page *p, unsigned flags) +int f2fs_convert_inline_data(struct inode *inode, pgoff_t to_size) { - int err; struct page *page; + int err; - if (!p || p->index) { - page = grab_cache_page_write_begin(inode->i_mapping, 0, flags); - if (IS_ERR(page)) - return PTR_ERR(page); - } else { - page = p; - } - - err = __f2fs_convert_inline_data(inode, page); + if (!f2fs_has_inline_data(inode)) + return 0; + else if (to_size <= MAX_INLINE_DATA) + return 0; - if (!p || p->index) - f2fs_put_page(page, 1); + page = grab_cache_page_write_begin(inode->i_mapping, 0, AOP_FLAG_NOFS); + if (!page) + return -ENOMEM; + err = __f2fs_convert_inline_data(inode, page); + f2fs_put_page(page, 1); return err; } -- cgit v1.2.3 From 0dbdc2ae9bba0a358816cc4a22e41a6ef16db8a2 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Tue, 26 Nov 2013 11:08:57 +0900 Subject: f2fs: add the number of inline_data files to status info This patch adds the number of inline_data files into the status information. Note that the number is reset whenever the filesystem is newly mounted. Signed-off-by: Jaegeuk Kim --- fs/f2fs/inline.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) (limited to 'fs/f2fs/inline.c') diff --git a/fs/f2fs/inline.c b/fs/f2fs/inline.c index e8891aa3ab8c..3c9261cd215f 100644 --- a/fs/f2fs/inline.c +++ b/fs/f2fs/inline.c @@ -13,11 +13,6 @@ #include "f2fs.h" -inline int f2fs_has_inline_data(struct inode *inode) -{ - return is_inode_flag_set(F2FS_I(inode), FI_INLINE_DATA); -} - bool f2fs_may_inline(struct inode *inode) { struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb); @@ -113,6 +108,7 @@ static int __f2fs_convert_inline_data(struct inode *inode, struct page *page) zero_user_segment(ipage, INLINE_DATA_OFFSET, INLINE_DATA_OFFSET + MAX_INLINE_DATA); clear_inode_flag(F2FS_I(inode), FI_INLINE_DATA); + stat_dec_inline_inode(inode); sync_inode_page(&dn); f2fs_put_page(ipage, 1); @@ -165,6 +161,7 @@ int f2fs_write_inline_data(struct inode *inode, if (!f2fs_has_inline_data(inode)) { truncate_data_blocks_range(&dn, 1); set_inode_flag(F2FS_I(inode), FI_INLINE_DATA); + stat_inc_inline_inode(inode); } sync_inode_page(&dn); -- 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/inline.c | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) (limited to 'fs/f2fs/inline.c') 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; +} -- cgit v1.2.3 From a8865372a8414298982e07f4ac8d6dc0ab1e0a3d Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Fri, 27 Dec 2013 17:04:17 +0900 Subject: f2fs: handle errors correctly during f2fs_reserve_block The get_dnode_of_data nullifies inode and node page when error is occurred. There are two cases that passes inode page into get_dnode_of_data(). 1. make_empty_dir() -> get_new_data_page() -> f2fs_reserve_block(ipage) -> get_dnode_of_data() 2. f2fs_convert_inline_data() -> __f2fs_convert_inline_data() -> f2fs_reserve_block(ipage) -> get_dnode_of_data() This patch adds correct error handling codes when get_dnode_of_data() returns an error. At first, f2fs_reserve_block() calls f2fs_put_dnode() whenever reserve_new_block returns an error. So, the rule of f2fs_reserve_block() is to nullify inode page when there is any error internally. Finally, two callers of f2fs_reserve_block() should call f2fs_put_dnode() appropriately if they got an error since successful f2fs_reserve_block(). Signed-off-by: Jaegeuk Kim --- fs/f2fs/inline.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'fs/f2fs/inline.c') diff --git a/fs/f2fs/inline.c b/fs/f2fs/inline.c index 2a756e57aed9..688305afbc74 100644 --- a/fs/f2fs/inline.c +++ b/fs/f2fs/inline.c @@ -81,10 +81,9 @@ static int __f2fs_convert_inline_data(struct inode *inode, struct page *page) * i_addr[0] is not used for inline data, * so reserving new block will not destroy inline data */ - set_new_dnode(&dn, inode, ipage, ipage, 0); + set_new_dnode(&dn, inode, ipage, NULL, 0); err = f2fs_reserve_block(&dn, 0); if (err) { - f2fs_put_page(ipage, 1); f2fs_unlock_op(sbi); return err; } @@ -111,9 +110,8 @@ static int __f2fs_convert_inline_data(struct inode *inode, struct page *page) stat_dec_inline_inode(inode); sync_inode_page(&dn); - f2fs_put_page(ipage, 1); + f2fs_put_dnode(&dn); f2fs_unlock_op(sbi); - return err; } -- cgit v1.2.3 From 18309aaa41909cfddb93e932b16a7d14ec425c9b Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Mon, 30 Dec 2013 09:29:06 +0800 Subject: f2fs: avoid to left uninitialized data in page when read inline data Change log from v1: o reduce unneeded memset in __f2fs_convert_inline_data >From 58796be2bd2becbe8d52305210fb2a64e7dd80b6 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Mon, 30 Dec 2013 09:21:33 +0800 Subject: [PATCH] f2fs: avoid to left uninitialized data in page when read inline data We left uninitialized data in the tail of page when we read an inline data page. So let's initialize left part of the page excluding inline data region. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/inline.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'fs/f2fs/inline.c') diff --git a/fs/f2fs/inline.c b/fs/f2fs/inline.c index 688305afbc74..89f0c18cd73c 100644 --- a/fs/f2fs/inline.c +++ b/fs/f2fs/inline.c @@ -43,8 +43,7 @@ int f2fs_read_inline_data(struct inode *inode, struct page *page) if (IS_ERR(ipage)) return PTR_ERR(ipage); - zero_user_segment(page, INLINE_DATA_OFFSET, - INLINE_DATA_OFFSET + MAX_INLINE_DATA); + zero_user_segment(page, MAX_INLINE_DATA, PAGE_CACHE_SIZE); /* Copy the whole inline data block */ src_addr = inline_data_addr(ipage); @@ -88,7 +87,7 @@ static int __f2fs_convert_inline_data(struct inode *inode, struct page *page) return err; } - zero_user_segment(page, 0, PAGE_CACHE_SIZE); + zero_user_segment(page, MAX_INLINE_DATA, PAGE_CACHE_SIZE); /* Copy the whole inline data block */ src_addr = inline_data_addr(ipage); -- cgit v1.2.3 From 04a17fb17fafada39f96bfb41ceb2dc1c11b2af6 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Mon, 30 Dec 2013 18:36:23 +0800 Subject: f2fs: avoid to read inline data except first page Here is a case which could read inline page data not from first page. 1. write inline data 2. lseek to offset 4096 3. read 4096 bytes from offset 4096 (read_inline_data read inline data page to non-first page, And previously VFS has add this page to page cache) 4. ftruncate offset 8192 5. read 4096 bytes from offset 4096 (we meet this updated page with inline data in cache) So we should leave this page with inited data and uptodate flag for this case. Change log from v1: o fix a deadlock bug Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/inline.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'fs/f2fs/inline.c') diff --git a/fs/f2fs/inline.c b/fs/f2fs/inline.c index 89f0c18cd73c..e0d800a1d79f 100644 --- a/fs/f2fs/inline.c +++ b/fs/f2fs/inline.c @@ -39,6 +39,11 @@ int f2fs_read_inline_data(struct inode *inode, struct page *page) struct page *ipage; void *src_addr, *dst_addr; + if (page->index) { + zero_user_segment(page, 0, PAGE_CACHE_SIZE); + goto out; + } + ipage = get_node_page(sbi, inode->i_ino); if (IS_ERR(ipage)) return PTR_ERR(ipage); @@ -52,6 +57,7 @@ int f2fs_read_inline_data(struct inode *inode, struct page *page) kunmap(page); f2fs_put_page(ipage, 1); +out: SetPageUptodate(page); unlock_page(page); -- cgit v1.2.3 From 5514f0aadddcdfaaaea697b60203f5402552eb7b Mon Sep 17 00:00:00 2001 From: Yuan Zhong Date: Fri, 10 Jan 2014 07:26:14 +0000 Subject: f2fs: remove the needless parameter of f2fs_wait_on_page_writeback "boo sync" parameter is never referenced in f2fs_wait_on_page_writeback. We should remove this parameter. Signed-off-by: Yuan Zhong Signed-off-by: Jaegeuk Kim --- fs/f2fs/inline.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'fs/f2fs/inline.c') diff --git a/fs/f2fs/inline.c b/fs/f2fs/inline.c index e0d800a1d79f..31ee5b164ff9 100644 --- a/fs/f2fs/inline.c +++ b/fs/f2fs/inline.c @@ -106,7 +106,7 @@ static int __f2fs_convert_inline_data(struct inode *inode, struct page *page) set_page_writeback(page); write_data_page(page, &dn, &new_blk_addr, &fio); update_extent_cache(new_blk_addr, &dn); - f2fs_wait_on_page_writeback(page, DATA, true); + f2fs_wait_on_page_writeback(page, DATA); /* clear inline data and flag after data writeback */ zero_user_segment(ipage, INLINE_DATA_OFFSET, -- cgit v1.2.3