mirror of
https://github.com/nxp-imx/linux-imx.git
synced 2025-07-06 17:35:20 +02:00
UPSTREAM: f2fs: atomic: fix to avoid racing w/ GC
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:3db1de0e58
("f2fs: change the current atomic write way") Cc: Daeho Jeong <daehojeong@google.com> Change-Id: I7e6c2a790b7f57c41d3a955cd9ba519d4dba12f9 Signed-off-by: Chao Yu <chao@kernel.org> Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org> (cherry picked from commit1a0bd289a5
)
This commit is contained in:
parent
aa2602f6ab
commit
60f02a0bbc
|
@ -2672,10 +2672,13 @@ int f2fs_do_write_data_page(struct f2fs_io_info *fio)
|
|||
struct dnode_of_data dn;
|
||||
struct node_info ni;
|
||||
bool ipu_force = false;
|
||||
bool atomic_commit;
|
||||
int err = 0;
|
||||
|
||||
/* Use COW inode to make dnode_of_data for atomic write */
|
||||
if (f2fs_is_atomic_file(inode))
|
||||
atomic_commit = f2fs_is_atomic_file(inode) &&
|
||||
page_private_atomic(fio->page);
|
||||
if (atomic_commit)
|
||||
set_new_dnode(&dn, F2FS_I(inode)->cow_inode, NULL, NULL, 0);
|
||||
else
|
||||
set_new_dnode(&dn, inode, NULL, NULL, 0);
|
||||
|
@ -2774,6 +2777,8 @@ got_it:
|
|||
f2fs_outplace_write_data(&dn, fio);
|
||||
trace_f2fs_do_write_data_page(page_folio(page), OPU);
|
||||
set_inode_flag(inode, FI_APPEND_WRITE);
|
||||
if (atomic_commit)
|
||||
clear_page_private_atomic(page);
|
||||
out_writepage:
|
||||
f2fs_put_dnode(&dn);
|
||||
out:
|
||||
|
@ -3743,6 +3748,9 @@ static int f2fs_write_end(struct file *file,
|
|||
|
||||
set_page_dirty(page);
|
||||
|
||||
if (f2fs_is_atomic_file(inode))
|
||||
set_page_private_atomic(page);
|
||||
|
||||
if (pos + copied > i_size_read(inode) &&
|
||||
!f2fs_verity_in_progress(inode)) {
|
||||
f2fs_i_size_write(inode, pos + copied);
|
||||
|
|
|
@ -1423,7 +1423,8 @@ static inline void f2fs_clear_bit(unsigned int nr, char *addr);
|
|||
* bit 1 PAGE_PRIVATE_ONGOING_MIGRATION
|
||||
* bit 2 PAGE_PRIVATE_INLINE_INODE
|
||||
* bit 3 PAGE_PRIVATE_REF_RESOURCE
|
||||
* bit 4- f2fs private data
|
||||
* bit 4 PAGE_PRIVATE_ATOMIC_WRITE
|
||||
* bit 5- f2fs private data
|
||||
*
|
||||
* Layout B: lowest bit should be 0
|
||||
* page.private is a wrapped pointer.
|
||||
|
@ -1433,6 +1434,7 @@ enum {
|
|||
PAGE_PRIVATE_ONGOING_MIGRATION, /* data page which is on-going migrating */
|
||||
PAGE_PRIVATE_INLINE_INODE, /* inode page contains inline data */
|
||||
PAGE_PRIVATE_REF_RESOURCE, /* dirty page has referenced resources */
|
||||
PAGE_PRIVATE_ATOMIC_WRITE, /* data page from atomic write path */
|
||||
PAGE_PRIVATE_MAX
|
||||
};
|
||||
|
||||
|
@ -2403,14 +2405,17 @@ static inline void clear_page_private_##name(struct page *page) \
|
|||
PAGE_PRIVATE_GET_FUNC(nonpointer, NOT_POINTER);
|
||||
PAGE_PRIVATE_GET_FUNC(inline, INLINE_INODE);
|
||||
PAGE_PRIVATE_GET_FUNC(gcing, ONGOING_MIGRATION);
|
||||
PAGE_PRIVATE_GET_FUNC(atomic, ATOMIC_WRITE);
|
||||
|
||||
PAGE_PRIVATE_SET_FUNC(reference, REF_RESOURCE);
|
||||
PAGE_PRIVATE_SET_FUNC(inline, INLINE_INODE);
|
||||
PAGE_PRIVATE_SET_FUNC(gcing, ONGOING_MIGRATION);
|
||||
PAGE_PRIVATE_SET_FUNC(atomic, ATOMIC_WRITE);
|
||||
|
||||
PAGE_PRIVATE_CLEAR_FUNC(reference, REF_RESOURCE);
|
||||
PAGE_PRIVATE_CLEAR_FUNC(inline, INLINE_INODE);
|
||||
PAGE_PRIVATE_CLEAR_FUNC(gcing, ONGOING_MIGRATION);
|
||||
PAGE_PRIVATE_CLEAR_FUNC(atomic, ATOMIC_WRITE);
|
||||
|
||||
static inline unsigned long get_page_private_data(struct page *page)
|
||||
{
|
||||
|
@ -2442,6 +2447,7 @@ static inline void clear_page_private_all(struct page *page)
|
|||
clear_page_private_reference(page);
|
||||
clear_page_private_gcing(page);
|
||||
clear_page_private_inline(page);
|
||||
clear_page_private_atomic(page);
|
||||
|
||||
f2fs_bug_on(F2FS_P_SB(page), page_private(page));
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user