aboutsummaryrefslogtreecommitdiffstats
path: root/fs/f2fs
diff options
context:
space:
mode:
authorChao Yu <chao2.yu@samsung.com>2015-03-10 01:16:25 -0400
committerJaegeuk Kim <jaegeuk@kernel.org>2015-04-10 18:08:41 -0400
commit0bfcfcca3d4351f129b8c8a73c114c7ffa99228e (patch)
tree38607e684f2e18a458a69af0908f7259fe8b7adc /fs/f2fs
parent83dfe53c185e3554c102708c70dc1e5ff4bcac2c (diff)
f2fs: fix to truncate inline data past EOF
Previously if inode is with inline data, we will try to invalid partial inline data in page #0 when we truncate size of inode in truncate_partial_data_page(). And then we set page #0 to dirty, after this we can synchronize inode page with page #0 at ->writepage(). But sometimes we will fail to operate page #0 in truncate_partial_data_page() due to below reason: a) if offset is zero, we will skip setting page #0 to dirty. b) if page #0 is not uptodate, we will fail to update it as it has no mapping data. So with following operations, we will meet recent data which should be truncated. 1.write inline data to file 2.sync first data page to inode page 3.truncate file size to 0 4.truncate file size to max_inline_size 5.echo 1 > /proc/sys/vm/drop_caches 6.read file --> meet original inline data which is remained in inode page. This patch renames truncate_inline_data() to truncate_inline_inode() for code readability, then use truncate_inline_inode() to truncate inline data in inode page in truncate_blocks() and truncate page #0 in truncate_partial_data_page() for fixing. v2: o truncate partially #0 page in truncate_partial_data_page to avoid keeping old data in #0 page. Signed-off-by: Chao Yu <chao2.yu@samsung.com> Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
Diffstat (limited to 'fs/f2fs')
-rw-r--r--fs/f2fs/f2fs.h1
-rw-r--r--fs/f2fs/file.c16
-rw-r--r--fs/f2fs/inline.c26
3 files changed, 33 insertions, 10 deletions
diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h
index ca7da0464379..147ef316f0ff 100644
--- a/fs/f2fs/f2fs.h
+++ b/fs/f2fs/f2fs.h
@@ -1763,6 +1763,7 @@ extern struct kmem_cache *inode_entry_slab;
1763 */ 1763 */
1764bool f2fs_may_inline(struct inode *); 1764bool f2fs_may_inline(struct inode *);
1765void read_inline_data(struct page *, struct page *); 1765void read_inline_data(struct page *, struct page *);
1766bool truncate_inline_inode(struct page *, u64);
1766int f2fs_read_inline_data(struct inode *, struct page *); 1767int f2fs_read_inline_data(struct inode *, struct page *);
1767int f2fs_convert_inline_page(struct dnode_of_data *, struct page *); 1768int f2fs_convert_inline_page(struct dnode_of_data *, struct page *);
1768int f2fs_convert_inline_inode(struct inode *); 1769int f2fs_convert_inline_inode(struct inode *);
diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c
index 5e8850edc5cc..36dc7581a28b 100644
--- a/fs/f2fs/file.c
+++ b/fs/f2fs/file.c
@@ -456,15 +456,16 @@ void truncate_data_blocks(struct dnode_of_data *dn)
456 truncate_data_blocks_range(dn, ADDRS_PER_BLOCK); 456 truncate_data_blocks_range(dn, ADDRS_PER_BLOCK);
457} 457}
458 458
459static int truncate_partial_data_page(struct inode *inode, u64 from) 459static int truncate_partial_data_page(struct inode *inode, u64 from,
460 bool force)
460{ 461{
461 unsigned offset = from & (PAGE_CACHE_SIZE - 1); 462 unsigned offset = from & (PAGE_CACHE_SIZE - 1);
462 struct page *page; 463 struct page *page;
463 464
464 if (!offset) 465 if (!offset && !force)
465 return 0; 466 return 0;
466 467
467 page = find_data_page(inode, from >> PAGE_CACHE_SHIFT, false); 468 page = find_data_page(inode, from >> PAGE_CACHE_SHIFT, force);
468 if (IS_ERR(page)) 469 if (IS_ERR(page))
469 return 0; 470 return 0;
470 471
@@ -475,7 +476,8 @@ static int truncate_partial_data_page(struct inode *inode, u64 from)
475 476
476 f2fs_wait_on_page_writeback(page, DATA); 477 f2fs_wait_on_page_writeback(page, DATA);
477 zero_user(page, offset, PAGE_CACHE_SIZE - offset); 478 zero_user(page, offset, PAGE_CACHE_SIZE - offset);
478 set_page_dirty(page); 479 if (!force)
480 set_page_dirty(page);
479out: 481out:
480 f2fs_put_page(page, 1); 482 f2fs_put_page(page, 1);
481 return 0; 483 return 0;
@@ -489,6 +491,7 @@ int truncate_blocks(struct inode *inode, u64 from, bool lock)
489 pgoff_t free_from; 491 pgoff_t free_from;
490 int count = 0, err = 0; 492 int count = 0, err = 0;
491 struct page *ipage; 493 struct page *ipage;
494 bool truncate_page = false;
492 495
493 trace_f2fs_truncate_blocks_enter(inode, from); 496 trace_f2fs_truncate_blocks_enter(inode, from);
494 497
@@ -504,7 +507,10 @@ int truncate_blocks(struct inode *inode, u64 from, bool lock)
504 } 507 }
505 508
506 if (f2fs_has_inline_data(inode)) { 509 if (f2fs_has_inline_data(inode)) {
510 if (truncate_inline_inode(ipage, from))
511 set_page_dirty(ipage);
507 f2fs_put_page(ipage, 1); 512 f2fs_put_page(ipage, 1);
513 truncate_page = true;
508 goto out; 514 goto out;
509 } 515 }
510 516
@@ -535,7 +541,7 @@ out:
535 541
536 /* lastly zero out the first data page */ 542 /* lastly zero out the first data page */
537 if (!err) 543 if (!err)
538 err = truncate_partial_data_page(inode, from); 544 err = truncate_partial_data_page(inode, from, truncate_page);
539 545
540 trace_f2fs_truncate_blocks_exit(inode, err); 546 trace_f2fs_truncate_blocks_exit(inode, err);
541 return err; 547 return err;
diff --git a/fs/f2fs/inline.c b/fs/f2fs/inline.c
index 4ba97320d194..153c5e7a0bef 100644
--- a/fs/f2fs/inline.c
+++ b/fs/f2fs/inline.c
@@ -50,10 +50,26 @@ void read_inline_data(struct page *page, struct page *ipage)
50 SetPageUptodate(page); 50 SetPageUptodate(page);
51} 51}
52 52
53static void truncate_inline_data(struct page *ipage) 53bool truncate_inline_inode(struct page *ipage, u64 from)
54{ 54{
55 void *addr;
56
57 /*
58 * we should never truncate inline data past max inline data size,
59 * because we always convert inline inode to normal one before
60 * truncating real data if new size is past max inline data size.
61 */
62 f2fs_bug_on(F2FS_P_SB(ipage), from > MAX_INLINE_DATA);
63
64 if (from >= MAX_INLINE_DATA)
65 return false;
66
67 addr = inline_data_addr(ipage);
68
55 f2fs_wait_on_page_writeback(ipage, NODE); 69 f2fs_wait_on_page_writeback(ipage, NODE);
56 memset(inline_data_addr(ipage), 0, MAX_INLINE_DATA); 70 memset(addr + from, 0, MAX_INLINE_DATA - from);
71
72 return true;
57} 73}
58 74
59int f2fs_read_inline_data(struct inode *inode, struct page *page) 75int f2fs_read_inline_data(struct inode *inode, struct page *page)
@@ -131,7 +147,7 @@ no_update:
131 set_inode_flag(F2FS_I(dn->inode), FI_APPEND_WRITE); 147 set_inode_flag(F2FS_I(dn->inode), FI_APPEND_WRITE);
132 148
133 /* clear inline data and flag after data writeback */ 149 /* clear inline data and flag after data writeback */
134 truncate_inline_data(dn->inode_page); 150 truncate_inline_inode(dn->inode_page, 0);
135clear_out: 151clear_out:
136 stat_dec_inline_inode(dn->inode); 152 stat_dec_inline_inode(dn->inode);
137 f2fs_clear_inline_inode(dn->inode); 153 f2fs_clear_inline_inode(dn->inode);
@@ -245,7 +261,7 @@ process_inline:
245 if (f2fs_has_inline_data(inode)) { 261 if (f2fs_has_inline_data(inode)) {
246 ipage = get_node_page(sbi, inode->i_ino); 262 ipage = get_node_page(sbi, inode->i_ino);
247 f2fs_bug_on(sbi, IS_ERR(ipage)); 263 f2fs_bug_on(sbi, IS_ERR(ipage));
248 truncate_inline_data(ipage); 264 truncate_inline_inode(ipage, 0);
249 f2fs_clear_inline_inode(inode); 265 f2fs_clear_inline_inode(inode);
250 update_inode(inode, ipage); 266 update_inode(inode, ipage);
251 f2fs_put_page(ipage, 1); 267 f2fs_put_page(ipage, 1);
@@ -363,7 +379,7 @@ static int f2fs_convert_inline_dir(struct inode *dir, struct page *ipage,
363 set_page_dirty(page); 379 set_page_dirty(page);
364 380
365 /* clear inline dir and flag after data writeback */ 381 /* clear inline dir and flag after data writeback */
366 truncate_inline_data(ipage); 382 truncate_inline_inode(ipage, 0);
367 383
368 stat_dec_inline_dir(dir); 384 stat_dec_inline_dir(dir);
369 clear_inode_flag(F2FS_I(dir), FI_INLINE_DENTRY); 385 clear_inode_flag(F2FS_I(dir), FI_INLINE_DENTRY);