f2fs: compress: fix potential deadlock

generic/269 reports a hangtask issue, the root cause is ABBA deadlock
described as below:

Thread A			Thread B
- down_write(&sbi->gc_lock) -- A
				- f2fs_write_data_pages
				 - lock all pages in cluster -- B
				 - f2fs_write_multi_pages
				  - f2fs_write_raw_pages
				   - f2fs_write_single_data_page
				    - f2fs_balance_fs
				     - down_write(&sbi->gc_lock) -- A
- f2fs_gc
 - do_garbage_collect
  - ra_data_block
   - pagecache_get_page -- B

To fix this, it needs to avoid calling f2fs_balance_fs() if there is
still cluster pages been locked in context of cluster writeback, so
instead, let's call f2fs_balance_fs() in the end of
f2fs_write_raw_pages() when all cluster pages were unlocked.

Fixes: 4c8ff7095b ("f2fs: support data compression")
Signed-off-by: Chao Yu <yuchao0@huawei.com>
Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
This commit is contained in:
Chao Yu 2021-01-11 17:42:53 +08:00 committed by Jaegeuk Kim
parent 794c43f716
commit 3afae09ffe
3 changed files with 11 additions and 6 deletions

View File

@ -1455,7 +1455,7 @@ static int f2fs_write_raw_pages(struct compress_ctx *cc,
ret = f2fs_write_single_data_page(cc->rpages[i], &_submitted, ret = f2fs_write_single_data_page(cc->rpages[i], &_submitted,
NULL, NULL, wbc, io_type, NULL, NULL, wbc, io_type,
compr_blocks); compr_blocks, false);
if (ret) { if (ret) {
if (ret == AOP_WRITEPAGE_ACTIVATE) { if (ret == AOP_WRITEPAGE_ACTIVATE) {
unlock_page(cc->rpages[i]); unlock_page(cc->rpages[i]);
@ -1490,6 +1490,9 @@ static int f2fs_write_raw_pages(struct compress_ctx *cc,
*submitted += _submitted; *submitted += _submitted;
} }
f2fs_balance_fs(F2FS_M_SB(mapping), true);
return 0; return 0;
out_err: out_err:
for (++i; i < cc->cluster_size; i++) { for (++i; i < cc->cluster_size; i++) {

View File

@ -2671,7 +2671,8 @@ int f2fs_write_single_data_page(struct page *page, int *submitted,
sector_t *last_block, sector_t *last_block,
struct writeback_control *wbc, struct writeback_control *wbc,
enum iostat_type io_type, enum iostat_type io_type,
int compr_blocks) int compr_blocks,
bool allow_balance)
{ {
struct inode *inode = page->mapping->host; struct inode *inode = page->mapping->host;
struct f2fs_sb_info *sbi = F2FS_I_SB(inode); struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
@ -2809,7 +2810,7 @@ int f2fs_write_single_data_page(struct page *page, int *submitted,
} }
unlock_page(page); unlock_page(page);
if (!S_ISDIR(inode->i_mode) && !IS_NOQUOTA(inode) && if (!S_ISDIR(inode->i_mode) && !IS_NOQUOTA(inode) &&
!F2FS_I(inode)->cp_task) !F2FS_I(inode)->cp_task && allow_balance)
f2fs_balance_fs(sbi, need_balance_fs); f2fs_balance_fs(sbi, need_balance_fs);
if (unlikely(f2fs_cp_error(sbi))) { if (unlikely(f2fs_cp_error(sbi))) {
@ -2856,7 +2857,7 @@ static int f2fs_write_data_page(struct page *page,
#endif #endif
return f2fs_write_single_data_page(page, NULL, NULL, NULL, return f2fs_write_single_data_page(page, NULL, NULL, NULL,
wbc, FS_DATA_IO, 0); wbc, FS_DATA_IO, 0, true);
} }
/* /*
@ -3024,7 +3025,8 @@ static int f2fs_write_cache_pages(struct address_space *mapping,
} }
#endif #endif
ret = f2fs_write_single_data_page(page, &submitted, ret = f2fs_write_single_data_page(page, &submitted,
&bio, &last_block, wbc, io_type, 0); &bio, &last_block, wbc, io_type,
0, true);
if (ret == AOP_WRITEPAGE_ACTIVATE) if (ret == AOP_WRITEPAGE_ACTIVATE)
unlock_page(page); unlock_page(page);
#ifdef CONFIG_F2FS_FS_COMPRESSION #ifdef CONFIG_F2FS_FS_COMPRESSION

View File

@ -3502,7 +3502,7 @@ int f2fs_write_single_data_page(struct page *page, int *submitted,
struct bio **bio, sector_t *last_block, struct bio **bio, sector_t *last_block,
struct writeback_control *wbc, struct writeback_control *wbc,
enum iostat_type io_type, enum iostat_type io_type,
int compr_blocks); int compr_blocks, bool allow_balance);
void f2fs_invalidate_page(struct page *page, unsigned int offset, void f2fs_invalidate_page(struct page *page, unsigned int offset,
unsigned int length); unsigned int length);
int f2fs_release_page(struct page *page, gfp_t wait); int f2fs_release_page(struct page *page, gfp_t wait);