4.18-stable review patch.  If anyone has any objections, please let me know.

------------------

From: Chao Yu <yuch...@huawei.com>

[ Upstream commit a33c150237a20d97a174243bc658c86502f9d370 ]

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 <yuch...@huawei.com>
Signed-off-by: Jaegeuk Kim <jaeg...@kernel.org>
Signed-off-by: Sasha Levin <alexander.le...@microsoft.com>
Signed-off-by: Greg Kroah-Hartman <gre...@linuxfoundation.org>
---
 fs/f2fs/data.c |    4 ++++
 fs/f2fs/file.c |   37 +++++++++++++++++++++++--------------
 2 files changed, 27 insertions(+), 14 deletions(-)

--- a/fs/f2fs/data.c
+++ b/fs/f2fs/data.c
@@ -2149,8 +2149,12 @@ static void f2fs_write_failed(struct add
 
        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);
        }
 }
--- a/fs/f2fs/file.c
+++ b/fs/f2fs/file.c
@@ -782,22 +782,26 @@ int f2fs_setattr(struct dentry *dentry,
        }
 
        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);
@@ -944,13 +948,18 @@ static int punch_hole(struct inode *inod
 
                        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);
                }
        }


Reply via email to