diff options
author | Chao Yu <yuchao0@huawei.com> | 2018-08-05 11:02:22 -0400 |
---|---|---|
committer | Jaegeuk Kim <jaegeuk@kernel.org> | 2018-08-13 13:48:17 -0400 |
commit | c7079853c859c910b9d047a37891b4aafb8f8dd7 (patch) | |
tree | 9d5f827b0c96b80858080012618f1d634c1f3e1e | |
parent | 91291e9998d208370eb8156c760691b873bd7522 (diff) |
f2fs: avoid race between zero_range and background GC
Thread A Background GC
- f2fs_zero_range
- truncate_pagecache_range
- gc_data_segment
- get_read_data_page
- move_data_page
- set_page_dirty
- set_cold_data
- f2fs_do_zero_range
- dn->data_blkaddr = NEW_ADDR;
- f2fs_set_data_blkaddr
Actually, we don't need to set dirty & checked flag on the page, since
all valid data in the page should be zeroed by zero_range().
Use i_gc_rwsem[WRITE] to avoid such race condition.
Signed-off-by: Chao Yu <yuchao0@huawei.com>
Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
-rw-r--r-- | fs/f2fs/file.c | 11 |
1 files changed, 9 insertions, 2 deletions
diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index 67c9c2d4e2d9..4cb02027827e 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c | |||
@@ -1314,8 +1314,6 @@ static int f2fs_zero_range(struct inode *inode, loff_t offset, loff_t len, | |||
1314 | if (ret) | 1314 | if (ret) |
1315 | goto out_sem; | 1315 | goto out_sem; |
1316 | 1316 | ||
1317 | truncate_pagecache_range(inode, offset, offset + len - 1); | ||
1318 | |||
1319 | pg_start = ((unsigned long long) offset) >> PAGE_SHIFT; | 1317 | pg_start = ((unsigned long long) offset) >> PAGE_SHIFT; |
1320 | pg_end = ((unsigned long long) offset + len) >> PAGE_SHIFT; | 1318 | pg_end = ((unsigned long long) offset + len) >> PAGE_SHIFT; |
1321 | 1319 | ||
@@ -1345,12 +1343,19 @@ static int f2fs_zero_range(struct inode *inode, loff_t offset, loff_t len, | |||
1345 | unsigned int end_offset; | 1343 | unsigned int end_offset; |
1346 | pgoff_t end; | 1344 | pgoff_t end; |
1347 | 1345 | ||
1346 | down_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]); | ||
1347 | |||
1348 | truncate_pagecache_range(inode, | ||
1349 | (loff_t)index << PAGE_SHIFT, | ||
1350 | ((loff_t)pg_end << PAGE_SHIFT) - 1); | ||
1351 | |||
1348 | f2fs_lock_op(sbi); | 1352 | f2fs_lock_op(sbi); |
1349 | 1353 | ||
1350 | set_new_dnode(&dn, inode, NULL, NULL, 0); | 1354 | set_new_dnode(&dn, inode, NULL, NULL, 0); |
1351 | ret = f2fs_get_dnode_of_data(&dn, index, ALLOC_NODE); | 1355 | ret = f2fs_get_dnode_of_data(&dn, index, ALLOC_NODE); |
1352 | if (ret) { | 1356 | if (ret) { |
1353 | f2fs_unlock_op(sbi); | 1357 | f2fs_unlock_op(sbi); |
1358 | up_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]); | ||
1354 | goto out; | 1359 | goto out; |
1355 | } | 1360 | } |
1356 | 1361 | ||
@@ -1359,7 +1364,9 @@ static int f2fs_zero_range(struct inode *inode, loff_t offset, loff_t len, | |||
1359 | 1364 | ||
1360 | ret = f2fs_do_zero_range(&dn, index, end); | 1365 | ret = f2fs_do_zero_range(&dn, index, end); |
1361 | f2fs_put_dnode(&dn); | 1366 | f2fs_put_dnode(&dn); |
1367 | |||
1362 | f2fs_unlock_op(sbi); | 1368 | f2fs_unlock_op(sbi); |
1369 | up_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]); | ||
1363 | 1370 | ||
1364 | f2fs_balance_fs(sbi, dn.node_changed); | 1371 | f2fs_balance_fs(sbi, dn.node_changed); |
1365 | 1372 | ||