]> git.ipfire.org Git - thirdparty/kernel/stable.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)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Sat, 15 Sep 2018 07:46:50 +0000 (09:46 +0200)
[ Upstream commit c7079853c859c910b9d047a37891b4aafb8f8dd7 ]

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>
Signed-off-by: Sasha Levin <alexander.levin@microsoft.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
fs/f2fs/file.c

index 6880c6f78d58d0670bf28863d53de0c1e6afb3b0..abb0feffff11a08db749d200b162fcc25a8f36e8 100644 (file)
@@ -1295,8 +1295,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;
 
@@ -1326,12 +1324,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;
                        }
 
@@ -1340,7 +1345,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);