mirror of https://gitee.com/openkylin/linux.git
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:
parent
794c43f716
commit
3afae09ffe
|
@ -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++) {
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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);
|
||||||
|
|
Loading…
Reference in New Issue