Commit 2866fb16 authored by Sheng Yong's avatar Sheng Yong Committed by Jaegeuk Kim

f2fs: fix race between write_checkpoint and write_begin

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: default avatarSheng Yong <shengyong1@huawei.com>
Reviewed-by: default avatarChao Yu <yuchao0@huawei.com>
Signed-off-by: default avatarJaegeuk Kim <jaegeuk@kernel.org>
parent 4e240d1b
...@@ -2322,6 +2322,7 @@ static int prepare_write_begin(struct f2fs_sb_info *sbi, ...@@ -2322,6 +2322,7 @@ static int prepare_write_begin(struct f2fs_sb_info *sbi,
bool locked = false; bool locked = false;
struct extent_info ei = {0,0,0}; struct extent_info ei = {0,0,0};
int err = 0; int err = 0;
int flag;
/* /*
* we already allocated all the blocks, so we don't need to get * we already allocated all the blocks, so we don't need to get
...@@ -2331,9 +2332,15 @@ static int prepare_write_begin(struct f2fs_sb_info *sbi, ...@@ -2331,9 +2332,15 @@ static int prepare_write_begin(struct f2fs_sb_info *sbi,
!is_inode_flag_set(inode, FI_NO_PREALLOC)) !is_inode_flag_set(inode, FI_NO_PREALLOC))
return 0; 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) || if (f2fs_has_inline_data(inode) ||
(pos & PAGE_MASK) >= i_size_read(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; locked = true;
} }
restart: restart:
...@@ -2371,6 +2378,7 @@ static int prepare_write_begin(struct f2fs_sb_info *sbi, ...@@ -2371,6 +2378,7 @@ static int prepare_write_begin(struct f2fs_sb_info *sbi,
f2fs_put_dnode(&dn); f2fs_put_dnode(&dn);
__do_map_lock(sbi, F2FS_GET_BLOCK_PRE_AIO, __do_map_lock(sbi, F2FS_GET_BLOCK_PRE_AIO,
true); true);
WARN_ON(flag != F2FS_GET_BLOCK_PRE_AIO);
locked = true; locked = true;
goto restart; goto restart;
} }
...@@ -2384,7 +2392,7 @@ static int prepare_write_begin(struct f2fs_sb_info *sbi, ...@@ -2384,7 +2392,7 @@ static int prepare_write_begin(struct f2fs_sb_info *sbi,
f2fs_put_dnode(&dn); f2fs_put_dnode(&dn);
unlock_out: unlock_out:
if (locked) if (locked)
__do_map_lock(sbi, F2FS_GET_BLOCK_PRE_AIO, false); __do_map_lock(sbi, flag, false);
return err; return err;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment