]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
f2fs: avoid race between zero_range and background GC
authorChao Yu <yuchao0@huawei.com>
Sun, 5 Aug 2018 15:02:22 +0000 (23:02 +0800)
committerJaegeuk Kim <jaegeuk@kernel.org>
Mon, 13 Aug 2018 17:48:17 +0000 (10:48 -0700)
Thread A Background GC
- f2fs_zero_range
 - truncate_pagecache_range
- gc_data_segment
 - get_read_data_page
  - move_data_page
   - set_page_dirty
   - set_cold_data
 - f2fs_do_zero_range
  - dn->data_blkaddr = NEW_ADDR;
  - f2fs_set_data_blkaddr

Actually, we don't need to set dirty & checked flag on the page, since
all valid data in the page should be zeroed by zero_range().

Use i_gc_rwsem[WRITE] to avoid such race condition.

Signed-off-by: Chao Yu <yuchao0@huawei.com>
Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
fs/f2fs/file.c

index 67c9c2d4e2d99e280ef8cc4ab49e2ac89fd7d3e5..4cb02027827ef0a920fa4f8c408530210839cae6 100644 (file)
@@ -1314,8 +1314,6 @@ static int f2fs_zero_range(struct inode *inode, loff_t offset, loff_t len,
        if (ret)
                goto out_sem;
 
-       truncate_pagecache_range(inode, offset, offset + len - 1);
-
        pg_start = ((unsigned long long) offset) >> PAGE_SHIFT;
        pg_end = ((unsigned long long) offset + len) >> PAGE_SHIFT;
 
@@ -1345,12 +1343,19 @@ static int f2fs_zero_range(struct inode *inode, loff_t offset, loff_t len,
                        unsigned int end_offset;
                        pgoff_t end;
 
+                       down_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]);
+
+                       truncate_pagecache_range(inode,
+                               (loff_t)index << PAGE_SHIFT,
+                               ((loff_t)pg_end << PAGE_SHIFT) - 1);
+
                        f2fs_lock_op(sbi);
 
                        set_new_dnode(&dn, inode, NULL, NULL, 0);
                        ret = f2fs_get_dnode_of_data(&dn, index, ALLOC_NODE);
                        if (ret) {
                                f2fs_unlock_op(sbi);
+                               up_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]);
                                goto out;
                        }
 
@@ -1359,7 +1364,9 @@ static int f2fs_zero_range(struct inode *inode, loff_t offset, loff_t len,
 
                        ret = f2fs_do_zero_range(&dn, index, end);
                        f2fs_put_dnode(&dn);
+
                        f2fs_unlock_op(sbi);
+                       up_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]);
 
                        f2fs_balance_fs(sbi, dn.node_changed);