Compressed writeback is always charged as F2FS_WB_CP_DATA today because WB_DATA_TYPE() is called with compressed_page set. That is wrong for normal compressed file writeback; the CP guarantee belongs to the raw pagecache folio, not the compressed bounce page.
Use the raw folio for the writeback count type. Also keep one raw page under writeback until the compressed end_io callback is done using sbi, as normal compressed writeback is no longer protected by the CP-data counter. Signed-off-by: Wenjie Qi <[email protected]> --- QEMU/KASAN tested with compress_mode=fs and compress_mode=user, using concurrent 1MiB compressed writes and a background sync loop. The user mode case also ran F2FS_IOC_COMPRESS_FILE and fsync. No dmesg splat was reported and fsck.f2fs passed after unmount. fs/f2fs/compress.c | 34 +++++++++++++++++++++++++++------- fs/f2fs/data.c | 2 +- 2 files changed, 28 insertions(+), 8 deletions(-) diff --git a/fs/f2fs/compress.c b/fs/f2fs/compress.c index 91855d91bbdd..be9483b9a401 100644 --- a/fs/f2fs/compress.c +++ b/fs/f2fs/compress.c @@ -1487,9 +1487,19 @@ void f2fs_compress_write_end_io(struct bio *bio, struct folio *folio) struct page *page = &folio->page; struct f2fs_sb_info *sbi = bio->bi_private; struct compress_io_ctx *cic = folio->private; - enum count_type type = WB_DATA_TYPE(folio, true); + unsigned int offset = folio->index & (cic->nr_rpages - 1); + struct page *last_page = NULL; + enum count_type type; int i; + if (unlikely(!offset || offset >= cic->nr_rpages || + !cic->rpages[offset])) { + f2fs_bug_on(sbi, 1); + type = F2FS_WB_CP_DATA; + } else { + type = WB_DATA_TYPE(page_folio(cic->rpages[offset]), false); + } + if (unlikely(bio->bi_status != BLK_STS_OK)) mapping_set_error(cic->inode->i_mapping, -EIO); @@ -1501,21 +1511,31 @@ void f2fs_compress_write_end_io(struct bio *bio, struct folio *folio) } for (i = 0; i < cic->nr_rpages; i++) { - WARN_ON(!cic->rpages[i]); + if (WARN_ON(!cic->rpages[i])) + continue; + last_page = cic->rpages[i]; + } + + for (i = 0; i < cic->nr_rpages; i++) { + if (!cic->rpages[i]) + continue; clear_page_private_gcing(cic->rpages[i]); - end_page_writeback(cic->rpages[i]); + + if (cic->rpages[i] != last_page) + end_page_writeback(cic->rpages[i]); } page_array_free(sbi, cic->rpages, cic->nr_rpages); kmem_cache_free(cic_entry_slab, cic); /* - * Make sure dec_page_count() is the last access to sbi. - * Once it drops the F2FS_WB_CP_DATA counter to zero, the - * unmount thread can proceed to destroy sbi and - * sbi->page_array_slab. + * Keep all sbi accesses before the last raw page writeback is + * released, so an unmount thread cannot free sbi while this callback + * is still using it. */ dec_page_count(sbi, type); + if (last_page) + end_page_writeback(last_page); } static int f2fs_write_raw_pages(struct compress_ctx *cc, diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index a765fda71536..1466a7825dc5 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -1092,7 +1092,7 @@ void f2fs_submit_page_write(struct f2fs_io_info *fio) /* set submitted = true as a return value */ fio->submitted = 1; - type = WB_DATA_TYPE(bio_folio, fio->compressed_page); + type = WB_DATA_TYPE(fio->compressed_page ? fio->folio : bio_folio, false); inc_page_count(sbi, type); if (io->bio && base-commit: 7cc48ead1a8a96f2dd1eabd87c2f22947dc84cdf -- 2.43.0 _______________________________________________ Linux-f2fs-devel mailing list [email protected] https://lists.sourceforge.net/lists/listinfo/linux-f2fs-devel
