f2fs: compress: fix potential deadlock
authorChao Yu <yuchao0@huawei.com>
Mon, 11 Jan 2021 09:42:53 +0000 (17:42 +0800)
committerJaegeuk Kim <jaegeuk@kernel.org>
Wed, 27 Jan 2021 23:20:05 +0000 (15:20 -0800)
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: 4c8ff7095bef ("f2fs: support data compression")
Signed-off-by: Chao Yu <yuchao0@huawei.com>
Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
fs/f2fs/compress.c
fs/f2fs/data.c
fs/f2fs/f2fs.h

index 1696f91..77fa342 100644 (file)
@@ -1455,7 +1455,7 @@ retry_write:
 
                ret = f2fs_write_single_data_page(cc->rpages[i], &_submitted,
                                                NULL, NULL, wbc, io_type,
-                                               compr_blocks);
+                                               compr_blocks, false);
                if (ret) {
                        if (ret == AOP_WRITEPAGE_ACTIVATE) {
                                unlock_page(cc->rpages[i]);
@@ -1490,6 +1490,9 @@ retry_write:
 
                *submitted += _submitted;
        }
+
+       f2fs_balance_fs(F2FS_M_SB(mapping), true);
+
        return 0;
 out_err:
        for (++i; i < cc->cluster_size; i++) {
index 4d80f00..c7bb07d 100644 (file)
@@ -2671,7 +2671,8 @@ int f2fs_write_single_data_page(struct page *page, int *submitted,
                                sector_t *last_block,
                                struct writeback_control *wbc,
                                enum iostat_type io_type,
-                               int compr_blocks)
+                               int compr_blocks,
+                               bool allow_balance)
 {
        struct inode *inode = page->mapping->host;
        struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
@@ -2809,7 +2810,7 @@ out:
        }
        unlock_page(page);
        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);
 
        if (unlikely(f2fs_cp_error(sbi))) {
@@ -2856,7 +2857,7 @@ out:
 #endif
 
        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 @@ continue_unlock:
                        }
 #endif
                        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)
                                unlock_page(page);
 #ifdef CONFIG_F2FS_FS_COMPRESSION
index 980e061..6385240 100644 (file)
@@ -3502,7 +3502,7 @@ int f2fs_write_single_data_page(struct page *page, int *submitted,
                                struct bio **bio, sector_t *last_block,
                                struct writeback_control *wbc,
                                enum iostat_type io_type,
-                               int compr_blocks);
+                               int compr_blocks, bool allow_balance);
 void f2fs_invalidate_page(struct page *page, unsigned int offset,
                        unsigned int length);
 int f2fs_release_page(struct page *page, gfp_t wait);