diff --git a/Documentation/ABI/testing/sysfs-fs-f2fs b/Documentation/ABI/testing/sysfs-fs-f2fs index 3d2e78a8bb7d..0d8ef1fafc1f 100644 --- a/Documentation/ABI/testing/sysfs-fs-f2fs +++ b/Documentation/ABI/testing/sysfs-fs-f2fs @@ -1014,6 +1014,17 @@ Description: Every time a write operation completes f2fs_write_end_io() is (atc) context. The default value for this attribute is UINT_MAX which means that this functionality is disabled by default. +What: /sys/fs/f2fs//max_atc_write_bio_entry_cnt +Date: October 2026 +Contact: Chao Yu +Description: Every time a write operation completes f2fs_write_end_io() is + called. This function may be called from an atomic context, + e.g. from inside an interrupt handler. This attribute controls + the maximum number of entries (folios or cached blocks) in a + write bio that is completed in atomic (atc) context. The default + value for this attribute is UINT_MAX which means that this + functionality is disabled by default. + What: /sys/fs/f2fs//pinned_area_max_secno Date: August 2026 Contact: "Daeho Jeong" diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 1b0395b45fb7..2613e8821af6 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -398,7 +398,8 @@ static void f2fs_write_end_io(struct bio *bio) sbi = bio->bi_private; - if (in_atomic() && bio->bi_iter.bi_size > sbi->max_atc_write_bio_size) { + if (in_atomic() && (bio->bi_iter.bi_size > sbi->max_atc_write_bio_size || + F2FS_BIO(bio)->entry_cnt > sbi->max_atc_write_bio_entry_cnt)) { struct work_struct *w; w = &container_of(bio, struct f2fs_bio, bio)->work; @@ -555,6 +556,7 @@ static struct bio *__bio_alloc(struct f2fs_io_info *fio, int npages) fio->type, fio->temp); bio->bi_write_stream = f2fs_io_type_to_write_stream(bdev, fio->type, fio->temp); + F2FS_BIO(bio)->entry_cnt = 0; } iostat_alloc_and_bind_ctx(sbi, bio, NULL); @@ -1234,6 +1236,8 @@ void f2fs_submit_page_write(struct f2fs_io_info *fio) goto alloc_new; } + F2FS_BIO(io->bio)->entry_cnt++; + if (fio->io_wbc) wbc_account_cgroup_owner(fio->io_wbc, fio->folio, folio_size(fio->folio)); @@ -1340,6 +1344,7 @@ void f2fs_submit_cache_write(struct f2fs_io_info *fio) } f2fs_bio_add_cache(fio, io->bio); + F2FS_BIO(io->bio)->entry_cnt++; io->last_block_in_bio = fio->new_blkaddr; diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 362f06cb398d..631d8ce15203 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -1778,6 +1778,7 @@ struct f2fs_gc_kthread { struct f2fs_bio { struct work_struct work; struct f2fs_cached_block *entry; + unsigned int entry_cnt; struct bio bio; }; @@ -1807,6 +1808,8 @@ struct f2fs_sb_info { /* for bio operations */ /* Largest write bio size completed in atomic context (atc). */ u32 max_atc_write_bio_size; + /* Largest write bio entry count completed in atomic context (atc). */ + u32 max_atc_write_bio_entry_cnt; struct f2fs_bio_info *write_io[NR_PAGE_TYPE]; /* for write bios */ /* keep migration IO order for LFS mode */ struct f2fs_rwsem io_order_lock; diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 0e468a06c7f7..f2d1de6e89d3 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -5184,6 +5184,7 @@ static int f2fs_fill_super(struct super_block *sb, struct fs_context *fc) goto free_sb_buf; } sbi->max_atc_write_bio_size = UINT_MAX; + sbi->max_atc_write_bio_entry_cnt = UINT_MAX; INIT_WORK(&sbi->s_error_work, f2fs_record_error_work); memcpy(sbi->errors, raw_super->s_errors, MAX_F2FS_ERRORS); diff --git a/fs/f2fs/sysfs.c b/fs/f2fs/sysfs.c index 95f8dc0c4218..32dfd564094b 100644 --- a/fs/f2fs/sysfs.c +++ b/fs/f2fs/sysfs.c @@ -1285,6 +1285,7 @@ F2FS_SBI_RW_ATTR(umount_discard_timeout, interval_time[UMOUNT_DISCARD_TIMEOUT]); F2FS_SBI_RW_ATTR(gc_pin_file_thresh, gc_pin_file_threshold); F2FS_SBI_RW_ATTR(gc_reclaimed_segments, gc_reclaimed_segs); F2FS_SBI_RW_ATTR(max_atc_write_bio_size, max_atc_write_bio_size); +F2FS_SBI_RW_ATTR(max_atc_write_bio_entry_cnt, max_atc_write_bio_entry_cnt); F2FS_SBI_GENERAL_RW_ATTR(max_victim_search); F2FS_SBI_GENERAL_RW_ATTR(migration_granularity); F2FS_SBI_GENERAL_RW_ATTR(migration_window_granularity); @@ -1536,6 +1537,7 @@ static struct attribute *f2fs_attrs[] = { ATTR_LIST(gc_segment_mode), ATTR_LIST(gc_reclaimed_segments), ATTR_LIST(max_atc_write_bio_size), + ATTR_LIST(max_atc_write_bio_entry_cnt), ATTR_LIST(max_fragment_chunk), ATTR_LIST(max_fragment_hole), ATTR_LIST(current_atomic_write),