Skip to content

Commit 8d2e73e

Browse files
chaseyugregkh
authored andcommitted
f2fs: atomic: fix to avoid racing w/ GC
[ Upstream commit 1a0bd28 ] Case #1: SQLite App GC Thread Kworker Shrinker - f2fs_ioc_start_atomic_write - f2fs_ioc_commit_atomic_write - f2fs_commit_atomic_write - filemap_write_and_wait_range : write atomic_file's data to cow_inode echo 3 > drop_caches to drop atomic_file's cache. - f2fs_gc - gc_data_segment - move_data_page - set_page_dirty - writepages - f2fs_do_write_data_page : overwrite atomic_file's data to cow_inode - f2fs_down_write(&fi->i_gc_rwsem[WRITE]) - __f2fs_commit_atomic_write - f2fs_up_write(&fi->i_gc_rwsem[WRITE]) Case #2: SQLite App GC Thread Kworker - f2fs_ioc_start_atomic_write - __writeback_single_inode - do_writepages - f2fs_write_cache_pages - f2fs_write_single_data_page - f2fs_do_write_data_page : write atomic_file's data to cow_inode - f2fs_gc - gc_data_segment - move_data_page - set_page_dirty - writepages - f2fs_do_write_data_page : overwrite atomic_file's data to cow_inode - f2fs_ioc_commit_atomic_write In above cases racing in between atomic_write and GC, previous data in atomic_file may be overwrited to cow_file, result in data corruption. This patch introduces PAGE_PRIVATE_ATOMIC_WRITE bit flag in page.private, and use it to indicate that there is last dirty data in atomic file, and the data should be writebacked into cow_file, if the flag is not tagged in page, we should never write data across files. Fixes: 3db1de0 ("f2fs: change the current atomic write way") Cc: Daeho Jeong <[email protected]> Signed-off-by: Chao Yu <[email protected]> Signed-off-by: Jaegeuk Kim <[email protected]> Signed-off-by: Sasha Levin <[email protected]>
1 parent 82e71e9 commit 8d2e73e

File tree

2 files changed

+16
-2
lines changed

2 files changed

+16
-2
lines changed

fs/f2fs/data.c

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2650,10 +2650,13 @@ int f2fs_do_write_data_page(struct f2fs_io_info *fio)
26502650
struct dnode_of_data dn;
26512651
struct node_info ni;
26522652
bool ipu_force = false;
2653+
bool atomic_commit;
26532654
int err = 0;
26542655

26552656
/* Use COW inode to make dnode_of_data for atomic write */
2656-
if (f2fs_is_atomic_file(inode))
2657+
atomic_commit = f2fs_is_atomic_file(inode) &&
2658+
page_private_atomic(fio->page);
2659+
if (atomic_commit)
26572660
set_new_dnode(&dn, F2FS_I(inode)->cow_inode, NULL, NULL, 0);
26582661
else
26592662
set_new_dnode(&dn, inode, NULL, NULL, 0);
@@ -2752,6 +2755,8 @@ int f2fs_do_write_data_page(struct f2fs_io_info *fio)
27522755
f2fs_outplace_write_data(&dn, fio);
27532756
trace_f2fs_do_write_data_page(page_folio(page), OPU);
27542757
set_inode_flag(inode, FI_APPEND_WRITE);
2758+
if (atomic_commit)
2759+
clear_page_private_atomic(page);
27552760
out_writepage:
27562761
f2fs_put_dnode(&dn);
27572762
out:
@@ -3721,6 +3726,9 @@ static int f2fs_write_end(struct file *file,
37213726

37223727
set_page_dirty(page);
37233728

3729+
if (f2fs_is_atomic_file(inode))
3730+
set_page_private_atomic(page);
3731+
37243732
if (pos + copied > i_size_read(inode) &&
37253733
!f2fs_verity_in_progress(inode)) {
37263734
f2fs_i_size_write(inode, pos + copied);

fs/f2fs/f2fs.h

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1418,7 +1418,8 @@ static inline void f2fs_clear_bit(unsigned int nr, char *addr);
14181418
* bit 1 PAGE_PRIVATE_ONGOING_MIGRATION
14191419
* bit 2 PAGE_PRIVATE_INLINE_INODE
14201420
* bit 3 PAGE_PRIVATE_REF_RESOURCE
1421-
* bit 4- f2fs private data
1421+
* bit 4 PAGE_PRIVATE_ATOMIC_WRITE
1422+
* bit 5- f2fs private data
14221423
*
14231424
* Layout B: lowest bit should be 0
14241425
* page.private is a wrapped pointer.
@@ -1428,6 +1429,7 @@ enum {
14281429
PAGE_PRIVATE_ONGOING_MIGRATION, /* data page which is on-going migrating */
14291430
PAGE_PRIVATE_INLINE_INODE, /* inode page contains inline data */
14301431
PAGE_PRIVATE_REF_RESOURCE, /* dirty page has referenced resources */
1432+
PAGE_PRIVATE_ATOMIC_WRITE, /* data page from atomic write path */
14311433
PAGE_PRIVATE_MAX
14321434
};
14331435

@@ -2396,14 +2398,17 @@ static inline void clear_page_private_##name(struct page *page) \
23962398
PAGE_PRIVATE_GET_FUNC(nonpointer, NOT_POINTER);
23972399
PAGE_PRIVATE_GET_FUNC(inline, INLINE_INODE);
23982400
PAGE_PRIVATE_GET_FUNC(gcing, ONGOING_MIGRATION);
2401+
PAGE_PRIVATE_GET_FUNC(atomic, ATOMIC_WRITE);
23992402

24002403
PAGE_PRIVATE_SET_FUNC(reference, REF_RESOURCE);
24012404
PAGE_PRIVATE_SET_FUNC(inline, INLINE_INODE);
24022405
PAGE_PRIVATE_SET_FUNC(gcing, ONGOING_MIGRATION);
2406+
PAGE_PRIVATE_SET_FUNC(atomic, ATOMIC_WRITE);
24032407

24042408
PAGE_PRIVATE_CLEAR_FUNC(reference, REF_RESOURCE);
24052409
PAGE_PRIVATE_CLEAR_FUNC(inline, INLINE_INODE);
24062410
PAGE_PRIVATE_CLEAR_FUNC(gcing, ONGOING_MIGRATION);
2411+
PAGE_PRIVATE_CLEAR_FUNC(atomic, ATOMIC_WRITE);
24072412

24082413
static inline unsigned long get_page_private_data(struct page *page)
24092414
{
@@ -2435,6 +2440,7 @@ static inline void clear_page_private_all(struct page *page)
24352440
clear_page_private_reference(page);
24362441
clear_page_private_gcing(page);
24372442
clear_page_private_inline(page);
2443+
clear_page_private_atomic(page);
24382444

24392445
f2fs_bug_on(F2FS_P_SB(page), page_private(page));
24402446
}

0 commit comments

Comments
 (0)