From c08a690b46919e6b531c1a2bb74389323e5f5b1c Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Wed, 15 Oct 2014 10:16:54 -0700 Subject: f2fs: should truncate any allocated block for inline_data write When trying to write inline_data, we should truncate any data block allocated and pointed by the inode block. We should consider the data index is not 0. Signed-off-by: Jaegeuk Kim --- fs/f2fs/inline.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) (limited to 'fs/f2fs/inline.c') diff --git a/fs/f2fs/inline.c b/fs/f2fs/inline.c index 88036fd75797..e3abcfb54374 100644 --- a/fs/f2fs/inline.c +++ b/fs/f2fs/inline.c @@ -166,6 +166,14 @@ int f2fs_write_inline_data(struct inode *inode, return err; ipage = dn.inode_page; + /* Release any data block if it is allocated */ + if (!f2fs_has_inline_data(inode)) { + int count = ADDRS_PER_PAGE(dn.node_page, F2FS_I(inode)); + truncate_data_blocks_range(&dn, count); + set_inode_flag(F2FS_I(inode), FI_INLINE_DATA); + stat_inc_inline_inode(inode); + } + f2fs_wait_on_page_writeback(ipage, NODE); zero_user_segment(ipage, INLINE_DATA_OFFSET, INLINE_DATA_OFFSET + MAX_INLINE_DATA); @@ -174,13 +182,6 @@ int f2fs_write_inline_data(struct inode *inode, 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); - stat_inc_inline_inode(inode); - } - set_inode_flag(F2FS_I(inode), FI_APPEND_WRITE); sync_inode_page(&dn); f2fs_put_dnode(&dn); -- cgit v1.2.2 From 201a05be9628ae58efe7638e0c7ae3937ec85273 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Wed, 24 Sep 2014 18:17:53 +0800 Subject: f2fs: add key function to handle inline dir Adds Functions to implement inline dir init/lookup/insert/delete/convert ops. Signed-off-by: Chao Yu [Jaegeuk Kim: remove needless reserved area copy, pointed by Dan Carpenter] Signed-off-by: Jaegeuk Kim --- fs/f2fs/inline.c | 346 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 346 insertions(+) (limited to 'fs/f2fs/inline.c') diff --git a/fs/f2fs/inline.c b/fs/f2fs/inline.c index e3abcfb54374..b3be96ba585c 100644 --- a/fs/f2fs/inline.c +++ b/fs/f2fs/inline.c @@ -258,3 +258,349 @@ process_inline: } return false; } + +struct f2fs_dir_entry *find_in_inline_dir(struct inode *dir, + struct qstr *name, struct page **res_page) +{ + struct f2fs_sb_info *sbi = F2FS_SB(dir->i_sb); + struct page *ipage; + struct f2fs_dir_entry *de; + f2fs_hash_t namehash; + unsigned long bit_pos = 0; + struct f2fs_inline_dentry *dentry_blk; + const void *dentry_bits; + + ipage = get_node_page(sbi, dir->i_ino); + if (IS_ERR(ipage)) + return NULL; + + namehash = f2fs_dentry_hash(name); + + dentry_blk = inline_data_addr(ipage); + dentry_bits = &dentry_blk->dentry_bitmap; + + while (bit_pos < NR_INLINE_DENTRY) { + if (!test_bit_le(bit_pos, dentry_bits)) { + bit_pos++; + continue; + } + de = &dentry_blk->dentry[bit_pos]; + if (early_match_name(name->len, namehash, de)) { + if (!memcmp(dentry_blk->filename[bit_pos], + name->name, + name->len)) { + *res_page = ipage; + goto found; + } + } + + /* + * For the most part, it should be a bug when name_len is zero. + * We stop here for figuring out where the bugs are occurred. + */ + f2fs_bug_on(F2FS_P_SB(ipage), !de->name_len); + + bit_pos += GET_DENTRY_SLOTS(le16_to_cpu(de->name_len)); + } + + de = NULL; +found: + unlock_page(ipage); + return de; +} + +struct f2fs_dir_entry *f2fs_parent_inline_dir(struct inode *dir, + struct page **p) +{ + struct f2fs_sb_info *sbi = F2FS_I_SB(dir); + struct page *ipage; + struct f2fs_dir_entry *de; + struct f2fs_inline_dentry *dentry_blk; + + ipage = get_node_page(sbi, dir->i_ino); + if (IS_ERR(ipage)) + return NULL; + + dentry_blk = inline_data_addr(ipage); + de = &dentry_blk->dentry[1]; + *p = ipage; + unlock_page(ipage); + return de; +} + +int make_empty_inline_dir(struct inode *inode, struct inode *parent, + struct page *ipage) +{ + struct f2fs_inline_dentry *dentry_blk; + struct f2fs_dir_entry *de; + + dentry_blk = inline_data_addr(ipage); + + de = &dentry_blk->dentry[0]; + de->name_len = cpu_to_le16(1); + de->hash_code = 0; + de->ino = cpu_to_le32(inode->i_ino); + memcpy(dentry_blk->filename[0], ".", 1); + set_de_type(de, inode); + + de = &dentry_blk->dentry[1]; + de->hash_code = 0; + de->name_len = cpu_to_le16(2); + de->ino = cpu_to_le32(parent->i_ino); + memcpy(dentry_blk->filename[1], "..", 2); + set_de_type(de, inode); + + test_and_set_bit_le(0, &dentry_blk->dentry_bitmap); + test_and_set_bit_le(1, &dentry_blk->dentry_bitmap); + + set_page_dirty(ipage); + + /* update i_size to MAX_INLINE_DATA */ + if (i_size_read(inode) < MAX_INLINE_DATA) { + i_size_write(inode, MAX_INLINE_DATA); + set_inode_flag(F2FS_I(inode), FI_UPDATE_DIR); + } + return 0; +} + +int room_in_inline_dir(struct f2fs_inline_dentry *dentry_blk, int slots) +{ + int bit_start = 0; + int zero_start, zero_end; +next: + zero_start = find_next_zero_bit_le(&dentry_blk->dentry_bitmap, + NR_INLINE_DENTRY, + bit_start); + if (zero_start >= NR_INLINE_DENTRY) + return NR_INLINE_DENTRY; + + zero_end = find_next_bit_le(&dentry_blk->dentry_bitmap, + NR_INLINE_DENTRY, + zero_start); + if (zero_end - zero_start >= slots) + return zero_start; + + bit_start = zero_end + 1; + + if (zero_end + 1 >= NR_INLINE_DENTRY) + return NR_INLINE_DENTRY; + goto next; +} + +int f2fs_convert_inline_dir(struct inode *dir, struct page *ipage, + struct f2fs_inline_dentry *inline_dentry) +{ + struct page *page; + struct dnode_of_data dn; + struct f2fs_dentry_block *dentry_blk; + int err; + + page = grab_cache_page(dir->i_mapping, 0); + if (!page) + return -ENOMEM; + + set_new_dnode(&dn, dir, ipage, NULL, 0); + err = f2fs_reserve_block(&dn, 0); + if (err) + goto out; + + f2fs_wait_on_page_writeback(page, DATA); + zero_user_segment(page, 0, PAGE_CACHE_SIZE); + + dentry_blk = kmap(page); + + /* copy data from inline dentry block to new dentry block */ + memcpy(dentry_blk->dentry_bitmap, inline_dentry->dentry_bitmap, + INLINE_DENTRY_BITMAP_SIZE); + memcpy(dentry_blk->dentry, inline_dentry->dentry, + sizeof(struct f2fs_dir_entry) * NR_INLINE_DENTRY); + memcpy(dentry_blk->filename, inline_dentry->filename, + NR_INLINE_DENTRY * F2FS_SLOT_LEN); + + kunmap(page); + SetPageUptodate(page); + set_page_dirty(page); + + /* clear inline dir and flag after data writeback */ + zero_user_segment(ipage, INLINE_DATA_OFFSET, + INLINE_DATA_OFFSET + MAX_INLINE_DATA); + + stat_dec_inline_inode(dir); + + if (i_size_read(dir) < PAGE_CACHE_SIZE) { + i_size_write(dir, PAGE_CACHE_SIZE); + set_inode_flag(F2FS_I(dir), FI_UPDATE_DIR); + } + + sync_inode_page(&dn); +out: + f2fs_put_page(page, 1); + return err; +} + +int f2fs_add_inline_entry(struct inode *dir, const struct qstr *name, + struct inode *inode) +{ + struct f2fs_sb_info *sbi = F2FS_I_SB(dir); + struct page *ipage; + unsigned int bit_pos; + f2fs_hash_t name_hash; + struct f2fs_dir_entry *de; + size_t namelen = name->len; + struct f2fs_inline_dentry *dentry_blk = NULL; + int slots = GET_DENTRY_SLOTS(namelen); + struct page *page; + int err = 0; + int i; + + name_hash = f2fs_dentry_hash(name); + + ipage = get_node_page(sbi, dir->i_ino); + if (IS_ERR(ipage)) + return PTR_ERR(ipage); + + dentry_blk = inline_data_addr(ipage); + bit_pos = room_in_inline_dir(dentry_blk, slots); + if (bit_pos >= NR_INLINE_DENTRY) { + err = f2fs_convert_inline_dir(dir, ipage, dentry_blk); + if (!err) + err = -EAGAIN; + goto out; + } + + f2fs_wait_on_page_writeback(ipage, DATA); + + down_write(&F2FS_I(inode)->i_sem); + page = init_inode_metadata(inode, dir, name); + if (IS_ERR(page)) { + err = PTR_ERR(page); + goto fail; + } + de = &dentry_blk->dentry[bit_pos]; + de->hash_code = name_hash; + de->name_len = cpu_to_le16(namelen); + memcpy(dentry_blk->filename[bit_pos], name->name, name->len); + de->ino = cpu_to_le32(inode->i_ino); + set_de_type(de, inode); + for (i = 0; i < slots; i++) + test_and_set_bit_le(bit_pos + i, &dentry_blk->dentry_bitmap); + set_page_dirty(ipage); + + /* we don't need to mark_inode_dirty now */ + F2FS_I(inode)->i_pino = dir->i_ino; + update_inode(inode, page); + f2fs_put_page(page, 1); + + update_parent_metadata(dir, inode, 0); +fail: + up_write(&F2FS_I(inode)->i_sem); + + if (is_inode_flag_set(F2FS_I(dir), FI_UPDATE_DIR)) { + update_inode(dir, ipage); + clear_inode_flag(F2FS_I(dir), FI_UPDATE_DIR); + } +out: + f2fs_put_page(ipage, 1); + return err; +} + +void f2fs_delete_inline_entry(struct f2fs_dir_entry *dentry, struct page *page, + struct inode *dir, struct inode *inode) +{ + struct f2fs_inline_dentry *inline_dentry; + int slots = GET_DENTRY_SLOTS(le16_to_cpu(dentry->name_len)); + unsigned int bit_pos; + int i; + + lock_page(page); + f2fs_wait_on_page_writeback(page, DATA); + + inline_dentry = inline_data_addr(page); + bit_pos = dentry - inline_dentry->dentry; + for (i = 0; i < slots; i++) + test_and_clear_bit_le(bit_pos + i, + &inline_dentry->dentry_bitmap); + + set_page_dirty(page); + + dir->i_ctime = dir->i_mtime = CURRENT_TIME; + + if (inode) + f2fs_drop_nlink(dir, inode, page); + + f2fs_put_page(page, 1); +} + +bool f2fs_empty_inline_dir(struct inode *dir) +{ + struct f2fs_sb_info *sbi = F2FS_I_SB(dir); + struct page *ipage; + unsigned int bit_pos = 2; + struct f2fs_inline_dentry *dentry_blk; + + ipage = get_node_page(sbi, dir->i_ino); + if (IS_ERR(ipage)) + return false; + + dentry_blk = inline_data_addr(ipage); + bit_pos = find_next_bit_le(&dentry_blk->dentry_bitmap, + NR_INLINE_DENTRY, + bit_pos); + + f2fs_put_page(ipage, 1); + + if (bit_pos < NR_INLINE_DENTRY) + return false; + + return true; +} + +int f2fs_read_inline_dir(struct file *file, struct dir_context *ctx) +{ + struct inode *inode = file_inode(file); + struct f2fs_sb_info *sbi = F2FS_I_SB(inode); + unsigned int bit_pos = 0; + struct f2fs_inline_dentry *inline_dentry = NULL; + struct f2fs_dir_entry *de = NULL; + struct page *ipage = NULL; + unsigned char d_type = DT_UNKNOWN; + + if (ctx->pos == NR_INLINE_DENTRY) + return 0; + + ipage = get_node_page(sbi, inode->i_ino); + if (IS_ERR(ipage)) + return PTR_ERR(ipage); + + bit_pos = ((unsigned long)ctx->pos % NR_INLINE_DENTRY); + + inline_dentry = inline_data_addr(ipage); + while (bit_pos < NR_INLINE_DENTRY) { + bit_pos = find_next_bit_le(&inline_dentry->dentry_bitmap, + NR_INLINE_DENTRY, + bit_pos); + if (bit_pos >= NR_INLINE_DENTRY) + break; + + de = &inline_dentry->dentry[bit_pos]; + if (de->file_type < F2FS_FT_MAX) + d_type = f2fs_filetype_table[de->file_type]; + else + d_type = DT_UNKNOWN; + + if (!dir_emit(ctx, + inline_dentry->filename[bit_pos], + le16_to_cpu(de->name_len), + le32_to_cpu(de->ino), d_type)) + goto out; + + bit_pos += GET_DENTRY_SLOTS(le16_to_cpu(de->name_len)); + ctx->pos = bit_pos; + } + + ctx->pos = NR_INLINE_DENTRY; +out: + f2fs_put_page(ipage, 1); + + return 0; +} -- cgit v1.2.2 From 622f28ae9ba4fa89b4ff0f4a6cf75d153ea838ce Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Wed, 24 Sep 2014 18:19:10 +0800 Subject: f2fs: enable inline dir handling Add inline dir functions into normal dir ops' function to handle inline ops. Besides, we enable inline dir mode when a new dir inode is created if inline_data option is on. Signed-off-by: Chao Yu 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 b3be96ba585c..1fafc8561542 100644 --- a/fs/f2fs/inline.c +++ b/fs/f2fs/inline.c @@ -424,7 +424,7 @@ int f2fs_convert_inline_dir(struct inode *dir, struct page *ipage, /* clear inline dir and flag after data writeback */ zero_user_segment(ipage, INLINE_DATA_OFFSET, INLINE_DATA_OFFSET + MAX_INLINE_DATA); - + clear_inode_flag(F2FS_I(dir), FI_INLINE_DENTRY); stat_dec_inline_inode(dir); if (i_size_read(dir) < PAGE_CACHE_SIZE) { -- cgit v1.2.2 From a82afa20197a2ed289dd8fd18208a9e8b9af0130 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Mon, 13 Oct 2014 16:28:13 -0700 Subject: f2fs: reuse room_for_filename for inline dentry operation This patch introduces to reuse the existing room_for_filename for inline dentry operation. Signed-off-by: Jaegeuk Kim --- fs/f2fs/inline.c | 27 ++------------------------- 1 file changed, 2 insertions(+), 25 deletions(-) (limited to 'fs/f2fs/inline.c') diff --git a/fs/f2fs/inline.c b/fs/f2fs/inline.c index 1fafc8561542..29090b34c768 100644 --- a/fs/f2fs/inline.c +++ b/fs/f2fs/inline.c @@ -363,30 +363,6 @@ int make_empty_inline_dir(struct inode *inode, struct inode *parent, return 0; } -int room_in_inline_dir(struct f2fs_inline_dentry *dentry_blk, int slots) -{ - int bit_start = 0; - int zero_start, zero_end; -next: - zero_start = find_next_zero_bit_le(&dentry_blk->dentry_bitmap, - NR_INLINE_DENTRY, - bit_start); - if (zero_start >= NR_INLINE_DENTRY) - return NR_INLINE_DENTRY; - - zero_end = find_next_bit_le(&dentry_blk->dentry_bitmap, - NR_INLINE_DENTRY, - zero_start); - if (zero_end - zero_start >= slots) - return zero_start; - - bit_start = zero_end + 1; - - if (zero_end + 1 >= NR_INLINE_DENTRY) - return NR_INLINE_DENTRY; - goto next; -} - int f2fs_convert_inline_dir(struct inode *dir, struct page *ipage, struct f2fs_inline_dentry *inline_dentry) { @@ -460,7 +436,8 @@ int f2fs_add_inline_entry(struct inode *dir, const struct qstr *name, return PTR_ERR(ipage); dentry_blk = inline_data_addr(ipage); - bit_pos = room_in_inline_dir(dentry_blk, slots); + bit_pos = room_for_filename(&dentry_blk->dentry_bitmap, + slots, NR_INLINE_DENTRY); if (bit_pos >= NR_INLINE_DENTRY) { err = f2fs_convert_inline_dir(dir, ipage, dentry_blk); if (!err) -- cgit v1.2.2 From 4e6ebf6d493591403237400e94e6fc17b7cb1c62 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Mon, 13 Oct 2014 17:26:14 -0700 Subject: f2fs: reuse find_in_block code for find_in_inline_dir This patch removes redundant copied code in find_in_inline_dir. Signed-off-by: Jaegeuk Kim --- fs/f2fs/inline.c | 52 +++++++++++++++++----------------------------------- 1 file changed, 17 insertions(+), 35 deletions(-) (limited to 'fs/f2fs/inline.c') diff --git a/fs/f2fs/inline.c b/fs/f2fs/inline.c index 29090b34c768..4cdce00ed537 100644 --- a/fs/f2fs/inline.c +++ b/fs/f2fs/inline.c @@ -263,49 +263,31 @@ struct f2fs_dir_entry *find_in_inline_dir(struct inode *dir, struct qstr *name, struct page **res_page) { struct f2fs_sb_info *sbi = F2FS_SB(dir->i_sb); - struct page *ipage; + struct f2fs_inline_dentry *inline_dentry; struct f2fs_dir_entry *de; - f2fs_hash_t namehash; - unsigned long bit_pos = 0; - struct f2fs_inline_dentry *dentry_blk; - const void *dentry_bits; + struct page *ipage; + int max_slots = NR_INLINE_DENTRY; ipage = get_node_page(sbi, dir->i_ino); if (IS_ERR(ipage)) return NULL; - namehash = f2fs_dentry_hash(name); - - dentry_blk = inline_data_addr(ipage); - dentry_bits = &dentry_blk->dentry_bitmap; - - while (bit_pos < NR_INLINE_DENTRY) { - if (!test_bit_le(bit_pos, dentry_bits)) { - bit_pos++; - continue; - } - de = &dentry_blk->dentry[bit_pos]; - if (early_match_name(name->len, namehash, de)) { - if (!memcmp(dentry_blk->filename[bit_pos], - name->name, - name->len)) { - *res_page = ipage; - goto found; - } - } - - /* - * For the most part, it should be a bug when name_len is zero. - * We stop here for figuring out where the bugs are occurred. - */ - f2fs_bug_on(F2FS_P_SB(ipage), !de->name_len); - - bit_pos += GET_DENTRY_SLOTS(le16_to_cpu(de->name_len)); - } + inline_dentry = inline_data_addr(ipage); - de = NULL; -found: + de = find_target_dentry(name, &max_slots, &inline_dentry->dentry_bitmap, + inline_dentry->dentry, + inline_dentry->filename); unlock_page(ipage); + if (de) + *res_page = ipage; + else + f2fs_put_page(ipage, 0); + + /* + * For the most part, it should be a bug when name_len is zero. + * We stop here for figuring out where the bugs has occurred. + */ + f2fs_bug_on(sbi, max_slots < 0); return de; } -- cgit v1.2.2 From 59a0615540812297e4ff9673de5c1269413d0c40 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Mon, 13 Oct 2014 19:34:26 -0700 Subject: f2fs: fix to wait correct block type The inode page needs to wait NODE block io. Signed-off-by: Jaegeuk Kim --- fs/f2fs/inline.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'fs/f2fs/inline.c') diff --git a/fs/f2fs/inline.c b/fs/f2fs/inline.c index 4cdce00ed537..175f38a96700 100644 --- a/fs/f2fs/inline.c +++ b/fs/f2fs/inline.c @@ -427,7 +427,7 @@ int f2fs_add_inline_entry(struct inode *dir, const struct qstr *name, goto out; } - f2fs_wait_on_page_writeback(ipage, DATA); + f2fs_wait_on_page_writeback(ipage, NODE); down_write(&F2FS_I(inode)->i_sem); page = init_inode_metadata(inode, dir, name); @@ -472,7 +472,7 @@ void f2fs_delete_inline_entry(struct f2fs_dir_entry *dentry, struct page *page, int i; lock_page(page); - f2fs_wait_on_page_writeback(page, DATA); + f2fs_wait_on_page_writeback(page, NODE); inline_dentry = inline_data_addr(page); bit_pos = dentry - inline_dentry->dentry; -- cgit v1.2.2 From bce8d1120707c06088928b2ee52a58703d74ac29 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Mon, 13 Oct 2014 19:42:53 -0700 Subject: f2fs: avoid deadlock on init_inode_metadata Previously, init_inode_metadata does not hold any parent directory's inode page. So, f2fs_init_acl can grab its parent inode page without any problem. But, when we use inline_dentry, that page is grabbed during f2fs_add_link, so that we can fall into deadlock condition like below. INFO: task mknod:11006 blocked for more than 120 seconds. Tainted: G OE 3.17.0-rc1+ #13 "echo 0 > /proc/sys/kernel/hung_task_timeout_secs" disables this message. mknod D ffff88003fc94580 0 11006 11004 0x00000000 ffff880007717b10 0000000000000002 ffff88003c323220 ffff880007717fd8 0000000000014580 0000000000014580 ffff88003daecb30 ffff88003c323220 ffff88003fc94e80 ffff88003ffbb4e8 ffff880007717ba0 0000000000000002 Call Trace: [] ? bit_wait+0x50/0x50 [] io_schedule+0x9d/0x130 [] bit_wait_io+0x2c/0x50 [] __wait_on_bit_lock+0x4b/0xb0 [] __lock_page+0x67/0x70 [] ? autoremove_wake_function+0x40/0x40 [] pagecache_get_page+0x14c/0x1e0 [] get_node_page+0x59/0x130 [f2fs] [] read_all_xattrs+0x24d/0x430 [f2fs] [] f2fs_getxattr+0x52/0xe0 [f2fs] [] f2fs_get_acl+0x41/0x2d0 [f2fs] [] get_acl+0x47/0x70 [] posix_acl_create+0x5a/0x150 [] f2fs_init_acl+0x29/0xcb [f2fs] [] init_inode_metadata+0x5d/0x340 [f2fs] [] f2fs_add_inline_entry+0x12a/0x2e0 [f2fs] [] __f2fs_add_link+0x45/0x4a0 [f2fs] [] ? f2fs_new_inode+0x146/0x220 [f2fs] [] f2fs_mknod+0x86/0xf0 [f2fs] [] vfs_mknod+0xe1/0x160 [] SyS_mknod+0x1f6/0x200 [] tracesys+0xe1/0xe6 Signed-off-by: Jaegeuk Kim --- fs/f2fs/inline.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'fs/f2fs/inline.c') diff --git a/fs/f2fs/inline.c b/fs/f2fs/inline.c index 175f38a96700..46c554224276 100644 --- a/fs/f2fs/inline.c +++ b/fs/f2fs/inline.c @@ -427,14 +427,14 @@ int f2fs_add_inline_entry(struct inode *dir, const struct qstr *name, goto out; } - f2fs_wait_on_page_writeback(ipage, NODE); - down_write(&F2FS_I(inode)->i_sem); - page = init_inode_metadata(inode, dir, name); + page = init_inode_metadata(inode, dir, name, ipage); if (IS_ERR(page)) { err = PTR_ERR(page); goto fail; } + + f2fs_wait_on_page_writeback(ipage, NODE); de = &dentry_blk->dentry[bit_pos]; de->hash_code = name_hash; de->name_len = cpu_to_le16(namelen); -- cgit v1.2.2 From 3289c061c5aaf914c6eb7bdfadb58a7fdd611d30 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Mon, 13 Oct 2014 20:00:16 -0700 Subject: f2fs: add stat info for inline_dentry inodes This patch adds status information for inline_dentry inodes. 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 46c554224276..4219bf475d72 100644 --- a/fs/f2fs/inline.c +++ b/fs/f2fs/inline.c @@ -382,8 +382,8 @@ int f2fs_convert_inline_dir(struct inode *dir, struct page *ipage, /* clear inline dir and flag after data writeback */ zero_user_segment(ipage, INLINE_DATA_OFFSET, INLINE_DATA_OFFSET + MAX_INLINE_DATA); + stat_dec_inline_dir(dir); clear_inode_flag(F2FS_I(dir), FI_INLINE_DENTRY); - stat_dec_inline_inode(dir); if (i_size_read(dir) < PAGE_CACHE_SIZE) { i_size_write(dir, PAGE_CACHE_SIZE); -- cgit v1.2.2 From 38594de767b32db62b7213631772d050690d3803 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Wed, 15 Oct 2014 21:29:51 -0700 Subject: f2fs: reuse core function in f2fs_readdir for inline_dentry This patch introduces a core function, f2fs_fill_dentries, to remove redundant code in f2fs_readdir and f2fs_read_inline_dir. Signed-off-by: Jaegeuk Kim --- fs/f2fs/inline.c | 39 +++++++-------------------------------- 1 file changed, 7 insertions(+), 32 deletions(-) (limited to 'fs/f2fs/inline.c') diff --git a/fs/f2fs/inline.c b/fs/f2fs/inline.c index 4219bf475d72..38a6aa792534 100644 --- a/fs/f2fs/inline.c +++ b/fs/f2fs/inline.c @@ -517,49 +517,24 @@ bool f2fs_empty_inline_dir(struct inode *dir) int f2fs_read_inline_dir(struct file *file, struct dir_context *ctx) { struct inode *inode = file_inode(file); - struct f2fs_sb_info *sbi = F2FS_I_SB(inode); - unsigned int bit_pos = 0; struct f2fs_inline_dentry *inline_dentry = NULL; - struct f2fs_dir_entry *de = NULL; struct page *ipage = NULL; - unsigned char d_type = DT_UNKNOWN; if (ctx->pos == NR_INLINE_DENTRY) return 0; - ipage = get_node_page(sbi, inode->i_ino); + ipage = get_node_page(F2FS_I_SB(inode), inode->i_ino); if (IS_ERR(ipage)) return PTR_ERR(ipage); - bit_pos = ((unsigned long)ctx->pos % NR_INLINE_DENTRY); - inline_dentry = inline_data_addr(ipage); - while (bit_pos < NR_INLINE_DENTRY) { - bit_pos = find_next_bit_le(&inline_dentry->dentry_bitmap, - NR_INLINE_DENTRY, - bit_pos); - if (bit_pos >= NR_INLINE_DENTRY) - break; - - de = &inline_dentry->dentry[bit_pos]; - if (de->file_type < F2FS_FT_MAX) - d_type = f2fs_filetype_table[de->file_type]; - else - d_type = DT_UNKNOWN; - - if (!dir_emit(ctx, - inline_dentry->filename[bit_pos], - le16_to_cpu(de->name_len), - le32_to_cpu(de->ino), d_type)) - goto out; - - bit_pos += GET_DENTRY_SLOTS(le16_to_cpu(de->name_len)); - ctx->pos = bit_pos; - } - ctx->pos = NR_INLINE_DENTRY; -out: - f2fs_put_page(ipage, 1); + if (!f2fs_fill_dentries(ctx, &inline_dentry->dentry_bitmap, + inline_dentry->dentry, + inline_dentry->filename, + NR_INLINE_DENTRY, 0)) + ctx->pos = NR_INLINE_DENTRY; + f2fs_put_page(ipage, 1); return 0; } -- cgit v1.2.2 From 7b3cd7d6f026784b1a2a74b6e207b26253d9d780 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Sat, 18 Oct 2014 22:52:52 -0700 Subject: f2fs: introduce f2fs_dentry_ptr structure for code clean-up This patch introduces f2fs_dentry_ptr structure for the use of a function parameter in inline_dentry operations. Signed-off-by: Jaegeuk Kim --- fs/f2fs/inline.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) (limited to 'fs/f2fs/inline.c') diff --git a/fs/f2fs/inline.c b/fs/f2fs/inline.c index 38a6aa792534..445e99d6f6ee 100644 --- a/fs/f2fs/inline.c +++ b/fs/f2fs/inline.c @@ -265,8 +265,8 @@ struct f2fs_dir_entry *find_in_inline_dir(struct inode *dir, struct f2fs_sb_info *sbi = F2FS_SB(dir->i_sb); struct f2fs_inline_dentry *inline_dentry; struct f2fs_dir_entry *de; + struct f2fs_dentry_ptr d; struct page *ipage; - int max_slots = NR_INLINE_DENTRY; ipage = get_node_page(sbi, dir->i_ino); if (IS_ERR(ipage)) @@ -274,9 +274,9 @@ struct f2fs_dir_entry *find_in_inline_dir(struct inode *dir, inline_dentry = inline_data_addr(ipage); - de = find_target_dentry(name, &max_slots, &inline_dentry->dentry_bitmap, - inline_dentry->dentry, - inline_dentry->filename); + make_dentry_ptr(&d, (void *)inline_dentry, 2); + de = find_target_dentry(name, NULL, &d); + unlock_page(ipage); if (de) *res_page = ipage; @@ -287,7 +287,7 @@ struct f2fs_dir_entry *find_in_inline_dir(struct inode *dir, * For the most part, it should be a bug when name_len is zero. * We stop here for figuring out where the bugs has occurred. */ - f2fs_bug_on(sbi, max_slots < 0); + f2fs_bug_on(sbi, d.max < 0); return de; } @@ -519,6 +519,7 @@ int f2fs_read_inline_dir(struct file *file, struct dir_context *ctx) struct inode *inode = file_inode(file); struct f2fs_inline_dentry *inline_dentry = NULL; struct page *ipage = NULL; + struct f2fs_dentry_ptr d; if (ctx->pos == NR_INLINE_DENTRY) return 0; @@ -529,10 +530,9 @@ int f2fs_read_inline_dir(struct file *file, struct dir_context *ctx) inline_dentry = inline_data_addr(ipage); - if (!f2fs_fill_dentries(ctx, &inline_dentry->dentry_bitmap, - inline_dentry->dentry, - inline_dentry->filename, - NR_INLINE_DENTRY, 0)) + make_dentry_ptr(&d, (void *)inline_dentry, 2); + + if (!f2fs_fill_dentries(ctx, &d, 0)) ctx->pos = NR_INLINE_DENTRY; f2fs_put_page(ipage, 1); -- cgit v1.2.2 From 062a3e7ba7eebcd7d44e49e9510135f901f93f48 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Sat, 18 Oct 2014 23:06:41 -0700 Subject: f2fs: reuse make_empty_dir code for inline_dentry This patch introduces do_make_empty_dir to mitigate code redundancy for inline_dentry. Signed-off-by: Jaegeuk Kim --- fs/f2fs/inline.c | 20 +++----------------- 1 file changed, 3 insertions(+), 17 deletions(-) (limited to 'fs/f2fs/inline.c') diff --git a/fs/f2fs/inline.c b/fs/f2fs/inline.c index 445e99d6f6ee..a30984505f16 100644 --- a/fs/f2fs/inline.c +++ b/fs/f2fs/inline.c @@ -314,26 +314,12 @@ int make_empty_inline_dir(struct inode *inode, struct inode *parent, struct page *ipage) { struct f2fs_inline_dentry *dentry_blk; - struct f2fs_dir_entry *de; + struct f2fs_dentry_ptr d; dentry_blk = inline_data_addr(ipage); - de = &dentry_blk->dentry[0]; - de->name_len = cpu_to_le16(1); - de->hash_code = 0; - de->ino = cpu_to_le32(inode->i_ino); - memcpy(dentry_blk->filename[0], ".", 1); - set_de_type(de, inode); - - de = &dentry_blk->dentry[1]; - de->hash_code = 0; - de->name_len = cpu_to_le16(2); - de->ino = cpu_to_le32(parent->i_ino); - memcpy(dentry_blk->filename[1], "..", 2); - set_de_type(de, inode); - - test_and_set_bit_le(0, &dentry_blk->dentry_bitmap); - test_and_set_bit_le(1, &dentry_blk->dentry_bitmap); + make_dentry_ptr(&d, (void *)dentry_blk, 2); + do_make_empty_dir(inode, parent, &d); set_page_dirty(ipage); -- cgit v1.2.2 From f1e33a041e39535571cd19f4799c3673868f2118 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Sat, 18 Oct 2014 23:41:38 -0700 Subject: f2fs: use kmap_atomic instead of kmap For better performance, we need to use kmap_atomic instead of kmap. Signed-off-by: Jaegeuk Kim --- fs/f2fs/inline.c | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) (limited to 'fs/f2fs/inline.c') diff --git a/fs/f2fs/inline.c b/fs/f2fs/inline.c index a30984505f16..024546b80fd0 100644 --- a/fs/f2fs/inline.c +++ b/fs/f2fs/inline.c @@ -55,11 +55,10 @@ int f2fs_read_inline_data(struct inode *inode, struct page *page) /* Copy the whole inline data block */ src_addr = inline_data_addr(ipage); - dst_addr = kmap(page); + dst_addr = kmap_atomic(page); memcpy(dst_addr, src_addr, MAX_INLINE_DATA); - kunmap(page); + kunmap_atomic(dst_addr); f2fs_put_page(ipage, 1); - out: SetPageUptodate(page); unlock_page(page); @@ -105,9 +104,9 @@ static int __f2fs_convert_inline_data(struct inode *inode, struct page *page) /* Copy the whole inline data block */ src_addr = inline_data_addr(ipage); - dst_addr = kmap(page); + dst_addr = kmap_atomic(page); memcpy(dst_addr, src_addr, MAX_INLINE_DATA); - kunmap(page); + kunmap_atomic(dst_addr); SetPageUptodate(page); /* write data page to try to make data consistent */ @@ -177,10 +176,10 @@ int f2fs_write_inline_data(struct inode *inode, f2fs_wait_on_page_writeback(ipage, NODE); zero_user_segment(ipage, INLINE_DATA_OFFSET, INLINE_DATA_OFFSET + MAX_INLINE_DATA); - src_addr = kmap(page); + src_addr = kmap_atomic(page); dst_addr = inline_data_addr(ipage); memcpy(dst_addr, src_addr, size); - kunmap(page); + kunmap_atomic(src_addr); set_inode_flag(F2FS_I(inode), FI_APPEND_WRITE); sync_inode_page(&dn); @@ -351,7 +350,7 @@ int f2fs_convert_inline_dir(struct inode *dir, struct page *ipage, f2fs_wait_on_page_writeback(page, DATA); zero_user_segment(page, 0, PAGE_CACHE_SIZE); - dentry_blk = kmap(page); + dentry_blk = kmap_atomic(page); /* copy data from inline dentry block to new dentry block */ memcpy(dentry_blk->dentry_bitmap, inline_dentry->dentry_bitmap, @@ -361,7 +360,7 @@ int f2fs_convert_inline_dir(struct inode *dir, struct page *ipage, memcpy(dentry_blk->filename, inline_dentry->filename, NR_INLINE_DENTRY * F2FS_SLOT_LEN); - kunmap(page); + kunmap_atomic(dentry_blk); SetPageUptodate(page); set_page_dirty(page); -- cgit v1.2.2 From d64948a4df9f5571b0efa81f71a59bc86d7056a7 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Mon, 20 Oct 2014 20:28:49 -0700 Subject: f2fs: declare f2fs_convert_inline_dir as a static function This patch declares f2fs_convert_inline_dir as a static function, which was reported by kbuild test robot. 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 024546b80fd0..d9daf76aa9cd 100644 --- a/fs/f2fs/inline.c +++ b/fs/f2fs/inline.c @@ -330,7 +330,7 @@ int make_empty_inline_dir(struct inode *inode, struct inode *parent, return 0; } -int f2fs_convert_inline_dir(struct inode *dir, struct page *ipage, +static int f2fs_convert_inline_dir(struct inode *dir, struct page *ipage, struct f2fs_inline_dentry *inline_dentry) { struct page *page; -- cgit v1.2.2 From 427a45c8e2395e0d24cb7fecc2ebf6e5e84e59fd Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Sun, 26 Oct 2014 22:59:27 -0700 Subject: f2fs: flush_dcache_page for inline data When reading inline data, we should call flush_dcache_page. Signed-off-by: Jaegeuk Kim --- fs/f2fs/inline.c | 1 + 1 file changed, 1 insertion(+) (limited to 'fs/f2fs/inline.c') diff --git a/fs/f2fs/inline.c b/fs/f2fs/inline.c index d9daf76aa9cd..d6677d6bbd89 100644 --- a/fs/f2fs/inline.c +++ b/fs/f2fs/inline.c @@ -57,6 +57,7 @@ int f2fs_read_inline_data(struct inode *inode, struct page *page) src_addr = inline_data_addr(ipage); dst_addr = kmap_atomic(page); memcpy(dst_addr, src_addr, MAX_INLINE_DATA); + flush_dcache_page(page); kunmap_atomic(dst_addr); f2fs_put_page(ipage, 1); out: -- cgit v1.2.2 From b3d208f96d6bb21247108a956dead6a028d5cdb2 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Thu, 23 Oct 2014 19:48:09 -0700 Subject: f2fs: revisit inline_data to avoid data races and potential bugs This patch simplifies the inline_data usage with the following rule. 1. inline_data is set during the file creation. 2. If new data is requested to be written ranges out of inline_data, f2fs converts that inode permanently. 3. There is no cases which converts non-inline_data inode to inline_data. 4. The inline_data flag should be changed under inode page lock. Signed-off-by: Jaegeuk Kim --- fs/f2fs/inline.c | 203 +++++++++++++++++++++++++++---------------------------- 1 file changed, 98 insertions(+), 105 deletions(-) (limited to 'fs/f2fs/inline.c') diff --git a/fs/f2fs/inline.c b/fs/f2fs/inline.c index d6677d6bbd89..8b6610906ea5 100644 --- a/fs/f2fs/inline.c +++ b/fs/f2fs/inline.c @@ -15,41 +15,26 @@ bool f2fs_may_inline(struct inode *inode) { - block_t nr_blocks; - loff_t i_size; - if (!test_opt(F2FS_I_SB(inode), INLINE_DATA)) return false; if (f2fs_is_atomic_file(inode)) 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) + if (!S_ISREG(inode->i_mode)) return false; return true; } -int f2fs_read_inline_data(struct inode *inode, struct page *page) +void read_inline_data(struct page *page, struct page *ipage) { - struct page *ipage; void *src_addr, *dst_addr; - if (page->index) { - zero_user_segment(page, 0, PAGE_CACHE_SIZE); - goto out; - } + if (PageUptodate(page)) + return; - ipage = get_node_page(F2FS_I_SB(inode), inode->i_ino); - if (IS_ERR(ipage)) { - unlock_page(page); - return PTR_ERR(ipage); - } + f2fs_bug_on(F2FS_P_SB(page), page->index); zero_user_segment(page, MAX_INLINE_DATA, PAGE_CACHE_SIZE); @@ -59,104 +44,120 @@ int f2fs_read_inline_data(struct inode *inode, struct page *page) memcpy(dst_addr, src_addr, MAX_INLINE_DATA); flush_dcache_page(page); kunmap_atomic(dst_addr); - f2fs_put_page(ipage, 1); -out: SetPageUptodate(page); - unlock_page(page); +} +int f2fs_read_inline_data(struct inode *inode, struct page *page) +{ + struct page *ipage; + + ipage = get_node_page(F2FS_I_SB(inode), inode->i_ino); + if (IS_ERR(ipage)) { + unlock_page(page); + return PTR_ERR(ipage); + } + + if (!f2fs_has_inline_data(inode)) { + f2fs_put_page(ipage, 1); + return -EAGAIN; + } + + if (page->index) + zero_user_segment(page, 0, PAGE_CACHE_SIZE); + else + read_inline_data(page, ipage); + + SetPageUptodate(page); + f2fs_put_page(ipage, 1); + unlock_page(page); return 0; } -static int __f2fs_convert_inline_data(struct inode *inode, struct page *page) +int f2fs_convert_inline_page(struct dnode_of_data *dn, struct page *page) { - int err = 0; - struct page *ipage; - struct dnode_of_data dn; void *src_addr, *dst_addr; block_t new_blk_addr; - struct f2fs_sb_info *sbi = F2FS_I_SB(inode); struct f2fs_io_info fio = { .type = DATA, .rw = WRITE_SYNC | REQ_PRIO, }; + int err; - f2fs_lock_op(sbi); - ipage = get_node_page(sbi, inode->i_ino); - if (IS_ERR(ipage)) { - err = PTR_ERR(ipage); - goto out; - } + f2fs_bug_on(F2FS_I_SB(dn->inode), page->index); - /* someone else converted inline_data already */ - if (!f2fs_has_inline_data(inode)) - goto out; + if (!f2fs_exist_data(dn->inode)) + goto clear_out; - /* - * i_addr[0] is not used for inline data, - * so reserving new block will not destroy inline data - */ - set_new_dnode(&dn, inode, ipage, NULL, 0); - err = f2fs_reserve_block(&dn, 0); + err = f2fs_reserve_block(dn, 0); if (err) - goto out; + return err; f2fs_wait_on_page_writeback(page, DATA); + + if (PageUptodate(page)) + goto no_update; + zero_user_segment(page, MAX_INLINE_DATA, PAGE_CACHE_SIZE); /* Copy the whole inline data block */ - src_addr = inline_data_addr(ipage); + src_addr = inline_data_addr(dn->inode_page); dst_addr = kmap_atomic(page); memcpy(dst_addr, src_addr, MAX_INLINE_DATA); kunmap_atomic(dst_addr); SetPageUptodate(page); - +no_update: /* 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); + + write_data_page(page, dn, &new_blk_addr, &fio); + update_extent_cache(new_blk_addr, dn); f2fs_wait_on_page_writeback(page, DATA); /* 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); - stat_dec_inline_inode(inode); - - sync_inode_page(&dn); - f2fs_put_dnode(&dn); -out: - f2fs_unlock_op(sbi); - return err; + truncate_inline_data(dn->inode_page, 0); +clear_out: + f2fs_clear_inline_inode(dn->inode); + stat_dec_inline_inode(dn->inode); + sync_inode_page(dn); + f2fs_put_dnode(dn); + return 0; } -int f2fs_convert_inline_data(struct inode *inode, pgoff_t to_size, - struct page *page) +int f2fs_convert_inline_inode(struct inode *inode) { - struct page *new_page = page; - int err; + struct f2fs_sb_info *sbi = F2FS_I_SB(inode); + struct dnode_of_data dn; + struct page *ipage, *page; + int err = 0; - if (!f2fs_has_inline_data(inode)) - return 0; - else if (to_size <= MAX_INLINE_DATA) - return 0; + page = grab_cache_page(inode->i_mapping, 0); + if (!page) + return -ENOMEM; - if (!page || page->index != 0) { - new_page = grab_cache_page(inode->i_mapping, 0); - if (!new_page) - return -ENOMEM; + f2fs_lock_op(sbi); + + ipage = get_node_page(sbi, inode->i_ino); + if (IS_ERR(ipage)) { + f2fs_unlock_op(sbi); + return PTR_ERR(ipage); } - err = __f2fs_convert_inline_data(inode, new_page); - if (!page || page->index != 0) - f2fs_put_page(new_page, 1); + set_new_dnode(&dn, inode, ipage, ipage, 0); + + if (f2fs_has_inline_data(inode)) + err = f2fs_convert_inline_page(&dn, page); + + f2fs_put_dnode(&dn); + + f2fs_unlock_op(sbi); + + f2fs_put_page(page, 1); return err; } -int f2fs_write_inline_data(struct inode *inode, - struct page *page, unsigned size) +int f2fs_write_inline_data(struct inode *inode, struct page *page) { void *src_addr, *dst_addr; - struct page *ipage; struct dnode_of_data dn; int err; @@ -164,48 +165,39 @@ int f2fs_write_inline_data(struct inode *inode, err = get_dnode_of_data(&dn, 0, LOOKUP_NODE); if (err) return err; - ipage = dn.inode_page; - /* Release any data block if it is allocated */ if (!f2fs_has_inline_data(inode)) { - int count = ADDRS_PER_PAGE(dn.node_page, F2FS_I(inode)); - truncate_data_blocks_range(&dn, count); - set_inode_flag(F2FS_I(inode), FI_INLINE_DATA); - stat_inc_inline_inode(inode); + f2fs_put_dnode(&dn); + return -EAGAIN; } - f2fs_wait_on_page_writeback(ipage, NODE); - zero_user_segment(ipage, INLINE_DATA_OFFSET, - INLINE_DATA_OFFSET + MAX_INLINE_DATA); + f2fs_bug_on(F2FS_I_SB(inode), page->index); + + f2fs_wait_on_page_writeback(dn.inode_page, NODE); src_addr = kmap_atomic(page); - dst_addr = inline_data_addr(ipage); - memcpy(dst_addr, src_addr, size); + dst_addr = inline_data_addr(dn.inode_page); + memcpy(dst_addr, src_addr, MAX_INLINE_DATA); kunmap_atomic(src_addr); set_inode_flag(F2FS_I(inode), FI_APPEND_WRITE); + set_inode_flag(F2FS_I(inode), FI_DATA_EXIST); + sync_inode_page(&dn); f2fs_put_dnode(&dn); - return 0; } -void truncate_inline_data(struct inode *inode, u64 from) +void truncate_inline_data(struct page *ipage, u64 from) { - struct page *ipage; + void *addr; if (from >= MAX_INLINE_DATA) return; - ipage = get_node_page(F2FS_I_SB(inode), inode->i_ino); - if (IS_ERR(ipage)) - return; - f2fs_wait_on_page_writeback(ipage, NODE); - zero_user_segment(ipage, INLINE_DATA_OFFSET + from, - INLINE_DATA_OFFSET + MAX_INLINE_DATA); - set_page_dirty(ipage); - f2fs_put_page(ipage, 1); + addr = inline_data_addr(ipage); + memset(addr + from, 0, MAX_INLINE_DATA - from); } bool recover_inline_data(struct inode *inode, struct page *npage) @@ -237,6 +229,10 @@ process_inline: src_addr = inline_data_addr(npage); dst_addr = inline_data_addr(ipage); memcpy(dst_addr, src_addr, MAX_INLINE_DATA); + + set_inode_flag(F2FS_I(inode), FI_INLINE_DATA); + set_inode_flag(F2FS_I(inode), FI_DATA_EXIST); + update_inode(inode, ipage); f2fs_put_page(ipage, 1); return true; @@ -245,15 +241,12 @@ process_inline: if (f2fs_has_inline_data(inode)) { ipage = get_node_page(sbi, inode->i_ino); f2fs_bug_on(sbi, IS_ERR(ipage)); - f2fs_wait_on_page_writeback(ipage, NODE); - zero_user_segment(ipage, INLINE_DATA_OFFSET, - INLINE_DATA_OFFSET + MAX_INLINE_DATA); - clear_inode_flag(F2FS_I(inode), FI_INLINE_DATA); + truncate_inline_data(ipage, 0); + f2fs_clear_inline_inode(inode); update_inode(inode, ipage); f2fs_put_page(ipage, 1); } else if (ri && (ri->i_inline & F2FS_INLINE_DATA)) { truncate_blocks(inode, 0, false); - set_inode_flag(F2FS_I(inode), FI_INLINE_DATA); goto process_inline; } return false; @@ -366,8 +359,8 @@ static int f2fs_convert_inline_dir(struct inode *dir, struct page *ipage, set_page_dirty(page); /* clear inline dir and flag after data writeback */ - zero_user_segment(ipage, INLINE_DATA_OFFSET, - INLINE_DATA_OFFSET + MAX_INLINE_DATA); + truncate_inline_data(ipage, 0); + stat_dec_inline_dir(dir); clear_inode_flag(F2FS_I(dir), FI_INLINE_DENTRY); -- cgit v1.2.2 From 57e2a2c0a698406e6206ab707bd7c3dcf248c738 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Mon, 10 Nov 2014 16:29:14 -0800 Subject: f2fs: reduce the number of inline_data inode before clearing it The # of inline_data inode is decreased only when it has inline_data. After clearing the flag, we can't decreased the number. 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 8b6610906ea5..231067027745 100644 --- a/fs/f2fs/inline.c +++ b/fs/f2fs/inline.c @@ -116,8 +116,8 @@ no_update: /* clear inline data and flag after data writeback */ truncate_inline_data(dn->inode_page, 0); clear_out: - f2fs_clear_inline_inode(dn->inode); stat_dec_inline_inode(dn->inode); + f2fs_clear_inline_inode(dn->inode); sync_inode_page(dn); f2fs_put_dnode(dn); return 0; -- cgit v1.2.2 From 92dffd01790a5219d234fc83c3ba854f4490b7f4 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Tue, 11 Nov 2014 14:10:01 -0800 Subject: f2fs: convert inline_data when i_size becomes large If i_size becomes large outside of MAX_INLINE_DATA, we shoud convert the inode. Otherwise, we can make some dirty pages during the truncation, and those pages will be written through f2fs_write_data_page. At that moment, the inode has still inline_data, so that it tries to write non- zero pages into inline_data area. Signed-off-by: Jaegeuk Kim --- fs/f2fs/inline.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'fs/f2fs/inline.c') diff --git a/fs/f2fs/inline.c b/fs/f2fs/inline.c index 231067027745..053d114f0528 100644 --- a/fs/f2fs/inline.c +++ b/fs/f2fs/inline.c @@ -24,6 +24,9 @@ bool f2fs_may_inline(struct inode *inode) if (!S_ISREG(inode->i_mode)) return false; + if (i_size_read(inode) > MAX_INLINE_DATA) + return false; + return true; } -- cgit v1.2.2 From 6d20aff83c24e9a7f15fffe4be36ed33d24465da Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Mon, 17 Nov 2014 16:06:55 -0800 Subject: f2fs: fix to call put_page at the error handling routine The locked page should be released before returning the function. Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/inline.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'fs/f2fs/inline.c') diff --git a/fs/f2fs/inline.c b/fs/f2fs/inline.c index 053d114f0528..f26fb87da163 100644 --- a/fs/f2fs/inline.c +++ b/fs/f2fs/inline.c @@ -141,8 +141,8 @@ int f2fs_convert_inline_inode(struct inode *inode) ipage = get_node_page(sbi, inode->i_ino); if (IS_ERR(ipage)) { - f2fs_unlock_op(sbi); - return PTR_ERR(ipage); + err = PTR_ERR(ipage); + goto out; } set_new_dnode(&dn, inode, ipage, ipage, 0); @@ -151,7 +151,7 @@ int f2fs_convert_inline_inode(struct inode *inode) err = f2fs_convert_inline_page(&dn, page); f2fs_put_dnode(&dn); - +out: f2fs_unlock_op(sbi); f2fs_put_page(page, 1); -- cgit v1.2.2 From 09b8b3c83971c82fd1a0ae32625b3be2aa891688 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Tue, 18 Nov 2014 10:50:21 -0800 Subject: f2fs: call flush_dcache_page when the page was updated Whenever f2fs updates mapped pages, it needs to call flush_dcache_page. Signed-off-by: Jaegeuk Kim --- fs/f2fs/inline.c | 1 + 1 file changed, 1 insertion(+) (limited to 'fs/f2fs/inline.c') diff --git a/fs/f2fs/inline.c b/fs/f2fs/inline.c index f26fb87da163..914b6d37c5cf 100644 --- a/fs/f2fs/inline.c +++ b/fs/f2fs/inline.c @@ -106,6 +106,7 @@ int f2fs_convert_inline_page(struct dnode_of_data *dn, struct page *page) src_addr = inline_data_addr(dn->inode_page); dst_addr = kmap_atomic(page); memcpy(dst_addr, src_addr, MAX_INLINE_DATA); + flush_dcache_page(page); kunmap_atomic(dst_addr); SetPageUptodate(page); no_update: -- cgit v1.2.2 From 158c194c375be1b82149f9de80fd90e522979dc5 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Tue, 25 Nov 2014 11:34:02 -0800 Subject: f2fs: make clean the page before writing If a page is set to be written to the disk, we can make clean the page. Signed-off-by: Jaegeuk Kim --- fs/f2fs/inline.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'fs/f2fs/inline.c') diff --git a/fs/f2fs/inline.c b/fs/f2fs/inline.c index 914b6d37c5cf..e27f290cb3ef 100644 --- a/fs/f2fs/inline.c +++ b/fs/f2fs/inline.c @@ -84,7 +84,7 @@ int f2fs_convert_inline_page(struct dnode_of_data *dn, struct page *page) .type = DATA, .rw = WRITE_SYNC | REQ_PRIO, }; - int err; + int dirty, err; f2fs_bug_on(F2FS_I_SB(dn->inode), page->index); @@ -110,12 +110,17 @@ int f2fs_convert_inline_page(struct dnode_of_data *dn, struct page *page) kunmap_atomic(dst_addr); SetPageUptodate(page); no_update: + /* clear dirty state */ + dirty = clear_page_dirty_for_io(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); + if (dirty) + inode_dec_dirty_pages(dn->inode); /* clear inline data and flag after data writeback */ truncate_inline_data(dn->inode_page, 0); -- cgit v1.2.2 From 95f5b0fc5e25d94ace84dbf5bb4acb8b80b4f062 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Tue, 25 Nov 2014 17:27:38 -0800 Subject: f2fs: fix to recover converted inline_data If an inode has converted inline_data which was written to the disk, we should set its inode flag for further fsync so that this inline_data can be recovered from sudden power off. Signed-off-by: Jaegeuk Kim --- fs/f2fs/inline.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'fs/f2fs/inline.c') diff --git a/fs/f2fs/inline.c b/fs/f2fs/inline.c index e27f290cb3ef..f2d3c581e776 100644 --- a/fs/f2fs/inline.c +++ b/fs/f2fs/inline.c @@ -122,6 +122,9 @@ no_update: if (dirty) inode_dec_dirty_pages(dn->inode); + /* this converted inline_data should be recovered. */ + set_inode_flag(F2FS_I(dn->inode), FI_APPEND_WRITE); + /* clear inline data and flag after data writeback */ truncate_inline_data(dn->inode_page, 0); clear_out: -- cgit v1.2.2