]> asedeno.scripts.mit.edu Git - linux.git/commitdiff
f2fs: fix avoid race between truncate and background GC
authorChao Yu <yuchao0@huawei.com>
Sun, 5 Aug 2018 15:04:25 +0000 (23:04 +0800)
committerJaegeuk Kim <jaegeuk@kernel.org>
Mon, 13 Aug 2018 17:48:17 +0000 (10:48 -0700)
Thread A Background GC
- f2fs_setattr isize to 0
 - truncate_setsize
- gc_data_segment
 - f2fs_get_read_data_page page #0
  - set_page_dirty
  - set_cold_data
 - f2fs_truncate

- f2fs_setattr isize to 4k
- read 4k <--- hit data in cached page #0

Above race condition can cause read out invalid data in a truncated
page, fix it by i_gc_rwsem[WRITE] lock.

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

index 1e6cc68fb7c472c0add53a386cd38587f56b60ca..45f043ee48bdb73972783ee6e62e6fd55c6685dd 100644 (file)
@@ -2199,8 +2199,12 @@ static void f2fs_write_failed(struct address_space *mapping, loff_t to)
 
        if (to > i_size) {
                down_write(&F2FS_I(inode)->i_mmap_sem);
+               down_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]);
+
                truncate_pagecache(inode, i_size);
                f2fs_truncate_blocks(inode, i_size, true);
+
+               up_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]);
                up_write(&F2FS_I(inode)->i_mmap_sem);
        }
 }
index 4cb02027827ef0a920fa4f8c408530210839cae6..8c4694b9af27ebc3ae500ab6437771393c31c75d 100644 (file)
@@ -796,22 +796,26 @@ int f2fs_setattr(struct dentry *dentry, struct iattr *attr)
        }
 
        if (attr->ia_valid & ATTR_SIZE) {
-               if (attr->ia_size <= i_size_read(inode)) {
-                       down_write(&F2FS_I(inode)->i_mmap_sem);
-                       truncate_setsize(inode, attr->ia_size);
+               bool to_smaller = (attr->ia_size <= i_size_read(inode));
+
+               down_write(&F2FS_I(inode)->i_mmap_sem);
+               down_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]);
+
+               truncate_setsize(inode, attr->ia_size);
+
+               if (to_smaller)
                        err = f2fs_truncate(inode);
-                       up_write(&F2FS_I(inode)->i_mmap_sem);
-                       if (err)
-                               return err;
-               } else {
-                       /*
-                        * do not trim all blocks after i_size if target size is
-                        * larger than i_size.
-                        */
-                       down_write(&F2FS_I(inode)->i_mmap_sem);
-                       truncate_setsize(inode, attr->ia_size);
-                       up_write(&F2FS_I(inode)->i_mmap_sem);
+               /*
+                * do not trim all blocks after i_size if target size is
+                * larger than i_size.
+                */
+               up_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]);
+               up_write(&F2FS_I(inode)->i_mmap_sem);
 
+               if (err)
+                       return err;
+
+               if (!to_smaller) {
                        /* should convert inline inode here */
                        if (!f2fs_may_inline_data(inode)) {
                                err = f2fs_convert_inline_inode(inode);
@@ -958,13 +962,18 @@ static int punch_hole(struct inode *inode, loff_t offset, loff_t len)
 
                        blk_start = (loff_t)pg_start << PAGE_SHIFT;
                        blk_end = (loff_t)pg_end << PAGE_SHIFT;
+
                        down_write(&F2FS_I(inode)->i_mmap_sem);
+                       down_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]);
+
                        truncate_inode_pages_range(mapping, blk_start,
                                        blk_end - 1);
 
                        f2fs_lock_op(sbi);
                        ret = f2fs_truncate_hole(inode, pg_start, pg_end);
                        f2fs_unlock_op(sbi);
+
+                       up_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]);
                        up_write(&F2FS_I(inode)->i_mmap_sem);
                }
        }