This patch adds fi->commit_lock to avoid the case that GCed node pages are committed but GCed data pages are not committed. This can avoid the db file run into inconsistent state when sudden-power-off happens if data pages of atomic file is allowed to be GCed before.
Signed-off-by: Yunlong Song <yunlong.s...@huawei.com> --- fs/f2fs/f2fs.h | 1 + fs/f2fs/file.c | 15 ++++++++++++-- fs/f2fs/gc.c | 61 +++++++++++++++++++++++++++++++++++++-------------------- fs/f2fs/super.c | 1 + 4 files changed, 55 insertions(+), 23 deletions(-) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index dbe87c7..b58a8f2 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -628,6 +628,7 @@ struct f2fs_inode_info { struct list_head inmem_pages; /* inmemory pages managed by f2fs */ struct task_struct *inmem_task; /* store inmemory task */ struct mutex inmem_lock; /* lock for inmemory pages */ + struct mutex commit_lock; /* lock for commit GCed pages */ struct extent_tree *extent_tree; /* cached extent_tree entry */ struct rw_semaphore dio_rwsem[2];/* avoid racing between dio and gc */ struct rw_semaphore i_mmap_sem; diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index 672a542..7e14724 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -202,6 +202,7 @@ static int f2fs_do_sync_file(struct file *file, loff_t start, loff_t end, { struct inode *inode = file->f_mapping->host; struct f2fs_sb_info *sbi = F2FS_I_SB(inode); + struct f2fs_inode_info *fi = F2FS_I(inode); nid_t ino = inode->i_ino; int ret = 0; enum cp_reason_type cp_reason = 0; @@ -219,11 +220,13 @@ static int f2fs_do_sync_file(struct file *file, loff_t start, loff_t end, /* if fdatasync is triggered, let's do in-place-update */ if (datasync || get_dirty_pages(inode) <= SM_I(sbi)->min_fsync_blocks) set_inode_flag(inode, FI_NEED_IPU); + mutex_lock(&fi->commit_lock); ret = file_write_and_wait_range(file, start, end); clear_inode_flag(inode, FI_NEED_IPU); if (ret) { trace_f2fs_sync_file_exit(inode, cp_reason, datasync, ret); + mutex_unlock(&fi->commit_lock); return ret; } @@ -244,8 +247,11 @@ static int f2fs_do_sync_file(struct file *file, loff_t start, loff_t end, goto go_write; if (is_inode_flag_set(inode, FI_UPDATE_WRITE) || - exist_written_data(sbi, ino, UPDATE_INO)) + exist_written_data(sbi, ino, UPDATE_INO)) { + mutex_unlock(&fi->commit_lock); goto flush_out; + } + mutex_unlock(&fi->commit_lock); goto out; } go_write: @@ -268,16 +274,20 @@ static int f2fs_do_sync_file(struct file *file, loff_t start, loff_t end, try_to_fix_pino(inode); clear_inode_flag(inode, FI_APPEND_WRITE); clear_inode_flag(inode, FI_UPDATE_WRITE); + mutex_unlock(&fi->commit_lock); goto out; } sync_nodes: ret = fsync_node_pages(sbi, inode, &wbc, atomic); - if (ret) + if (ret) { + mutex_unlock(&fi->commit_lock); goto out; + } /* if cp_error was enabled, we should avoid infinite loop */ if (unlikely(f2fs_cp_error(sbi))) { ret = -EIO; + mutex_unlock(&fi->commit_lock); goto out; } @@ -286,6 +296,7 @@ static int f2fs_do_sync_file(struct file *file, loff_t start, loff_t end, f2fs_write_inode(inode, NULL); goto sync_nodes; } + mutex_unlock(&fi->commit_lock); /* * If it's atomic_write, it's just fine to keep write ordering. So diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c index 9d54ddb..b98aff5 100644 --- a/fs/f2fs/gc.c +++ b/fs/f2fs/gc.c @@ -676,13 +676,20 @@ static void move_data_block(struct inode *inode, block_t bidx, goto put_page_out; } - if (f2fs_is_atomic_file(inode) && - !f2fs_is_commit_atomic_write(inode) && - !IS_GC_WRITTEN_PAGE(fio.encrypted_page)) { - set_page_private(fio.encrypted_page, (unsigned long)GC_WRITTEN_PAGE); - SetPagePrivate(fio.encrypted_page); - } - set_page_dirty(fio.encrypted_page); + if (f2fs_is_atomic_file(inode)) { + struct f2fs_inode_info *fi = F2FS_I(inode); + + mutex_lock(&fi->commit_lock); + if (!f2fs_is_commit_atomic_write(inode) && + !IS_GC_WRITTEN_PAGE(fio.encrypted_page)) { + set_page_private(fio.encrypted_page, + (unsigned long)GC_WRITTEN_PAGE); + SetPagePrivate(fio.encrypted_page); + } + set_page_dirty(fio.encrypted_page); + mutex_unlock(&fi->commit_lock); + } else + set_page_dirty(fio.encrypted_page); f2fs_wait_on_page_writeback(fio.encrypted_page, DATA, true); if (clear_page_dirty_for_io(fio.encrypted_page)) dec_page_count(fio.sbi, F2FS_DIRTY_META); @@ -741,13 +748,19 @@ static void move_data_page(struct inode *inode, block_t bidx, int gc_type, if (gc_type == BG_GC) { if (PageWriteback(page)) goto out; - if (f2fs_is_atomic_file(inode) && - !f2fs_is_commit_atomic_write(inode) && - !IS_GC_WRITTEN_PAGE(page)) { - set_page_private(page, (unsigned long)GC_WRITTEN_PAGE); - SetPagePrivate(page); - } - set_page_dirty(page); + if (f2fs_is_atomic_file(inode)) { + struct f2fs_inode_info *fi = F2FS_I(inode); + + mutex_lock(&fi->commit_lock); + if (!f2fs_is_commit_atomic_write(inode) && + !IS_GC_WRITTEN_PAGE(page)) { + set_page_private(page, (unsigned long)GC_WRITTEN_PAGE); + SetPagePrivate(page); + } + set_page_dirty(page); + mutex_unlock(&fi->commit_lock); + } else + set_page_dirty(page); set_cold_data(page); } else { struct f2fs_io_info fio = { @@ -767,13 +780,19 @@ static void move_data_page(struct inode *inode, block_t bidx, int gc_type, int err; retry: - if (f2fs_is_atomic_file(inode) && - !f2fs_is_commit_atomic_write(inode) && - !IS_GC_WRITTEN_PAGE(page)) { - set_page_private(page, (unsigned long)GC_WRITTEN_PAGE); - SetPagePrivate(page); - } - set_page_dirty(page); + if (f2fs_is_atomic_file(inode)) { + struct f2fs_inode_info *fi = F2FS_I(inode); + + mutex_lock(&fi->commit_lock); + if (!f2fs_is_commit_atomic_write(inode) && + !IS_GC_WRITTEN_PAGE(page)) { + set_page_private(page, (unsigned long)GC_WRITTEN_PAGE); + SetPagePrivate(page); + } + set_page_dirty(page); + mutex_unlock(&fi->commit_lock); + } else + set_page_dirty(page); f2fs_wait_on_page_writeback(page, DATA, true); if (clear_page_dirty_for_io(page)) { inode_dec_dirty_pages(inode); diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 7966cf7..ed6afd1 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -772,6 +772,7 @@ static struct inode *f2fs_alloc_inode(struct super_block *sb) INIT_LIST_HEAD(&fi->inmem_ilist); INIT_LIST_HEAD(&fi->inmem_pages); mutex_init(&fi->inmem_lock); + mutex_init(&fi->commit_lock); init_rwsem(&fi->dio_rwsem[READ]); init_rwsem(&fi->dio_rwsem[WRITE]); init_rwsem(&fi->i_mmap_sem); -- 1.8.5.2 ------------------------------------------------------------------------------ Check out the vibrant tech community on one of the world's most engaging tech sites, Slashdot.org! http://sdm.link/slashdot _______________________________________________ Linux-f2fs-devel mailing list Linux-f2fs-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/linux-f2fs-devel