lkml.org 
[lkml]   [2019]   [Jan]   [28]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH AUTOSEL 4.19 074/258] f2fs: fix race between write_checkpoint and write_begin
    Date
    From: Sheng Yong <shengyong1@huawei.com>

    [ Upstream commit 2866fb16d67992195b0526d19e65acb6640fb87f ]

    The following race could lead to inconsistent SIT bitmap:

    Task A Task B
    ====== ======
    f2fs_write_checkpoint
    block_operations
    f2fs_lock_all
    down_write(node_change)
    down_write(node_write)
    ... sync ...
    up_write(node_change)
    f2fs_file_write_iter
    set_inode_flag(FI_NO_PREALLOC)
    ......
    f2fs_write_begin(index=0, has inline data)
    prepare_write_begin
    __do_map_lock(AIO) => down_read(node_change)
    f2fs_convert_inline_page => update SIT
    __do_map_lock(AIO) => up_read(node_change)
    f2fs_flush_sit_entries <= inconsistent SIT
    finish write checkpoint
    sudden-power-off

    If SPO occurs after checkpoint is finished, SIT bitmap will be set
    incorrectly.

    Signed-off-by: Sheng Yong <shengyong1@huawei.com>
    Reviewed-by: Chao Yu <yuchao0@huawei.com>
    Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
    Signed-off-by: Sasha Levin <sashal@kernel.org>
    ---
    fs/f2fs/data.c | 12 ++++++++++--
    1 file changed, 10 insertions(+), 2 deletions(-)

    diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c
    index 11f28342f641..08314fb42652 100644
    --- a/fs/f2fs/data.c
    +++ b/fs/f2fs/data.c
    @@ -2259,6 +2259,7 @@ static int prepare_write_begin(struct f2fs_sb_info *sbi,
    bool locked = false;
    struct extent_info ei = {0,0,0};
    int err = 0;
    + int flag;

    /*
    * we already allocated all the blocks, so we don't need to get
    @@ -2268,9 +2269,15 @@ static int prepare_write_begin(struct f2fs_sb_info *sbi,
    !is_inode_flag_set(inode, FI_NO_PREALLOC))
    return 0;

    + /* f2fs_lock_op avoids race between write CP and convert_inline_page */
    + if (f2fs_has_inline_data(inode) && pos + len > MAX_INLINE_DATA(inode))
    + flag = F2FS_GET_BLOCK_DEFAULT;
    + else
    + flag = F2FS_GET_BLOCK_PRE_AIO;
    +
    if (f2fs_has_inline_data(inode) ||
    (pos & PAGE_MASK) >= i_size_read(inode)) {
    - __do_map_lock(sbi, F2FS_GET_BLOCK_PRE_AIO, true);
    + __do_map_lock(sbi, flag, true);
    locked = true;
    }
    restart:
    @@ -2308,6 +2315,7 @@ static int prepare_write_begin(struct f2fs_sb_info *sbi,
    f2fs_put_dnode(&dn);
    __do_map_lock(sbi, F2FS_GET_BLOCK_PRE_AIO,
    true);
    + WARN_ON(flag != F2FS_GET_BLOCK_PRE_AIO);
    locked = true;
    goto restart;
    }
    @@ -2321,7 +2329,7 @@ static int prepare_write_begin(struct f2fs_sb_info *sbi,
    f2fs_put_dnode(&dn);
    unlock_out:
    if (locked)
    - __do_map_lock(sbi, F2FS_GET_BLOCK_PRE_AIO, false);
    + __do_map_lock(sbi, flag, false);
    return err;
    }

    --
    2.19.1
    \
     
     \ /
      Last update: 2019-01-28 18:24    [W:4.033 / U:0.040 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site