diff options
| author | Bart Van Assche <bvanassche@acm.org> | 2026-08-03 09:52:22 -0700 |
|---|---|---|
| committer | Jaegeuk Kim <jaegeuk@kernel.org> | 2026-08-04 00:18:37 +0000 |
| commit | 3de6b80941152a384ee1b9cf88ac1c9dd4eec6dd (patch) | |
| tree | af6debcaf8cd1560ab03fac6c176c03cb8960f73 | |
| parent | 6dc2b804a042d2ff0fdf4c8e3db78669c36b688d (diff) | |
| download | linux-3de6b80941152a384ee1b9cf88ac1c9dd4eec6dd.tar.gz linux-3de6b80941152a384ee1b9cf88ac1c9dd4eec6dd.zip | |
f2fs: Run f2fs_write_end_io() asynchronously
The bio_for_each_segment_all() loop can take more than 10 ms for a large
bio on an ARM little core. This is too much for interrupt context. Hence
perform the write bio completion work asynchronously if a bio is large and
if f2fs_write_end_io() is called from atomic context. This patch reduces
the time spent in f2fs_write_end_io() from about 10 ms to about 150
microseconds on an Arm Cortex-A520 core if the max_atc_write_bio_size
parameter is changed to 16384.
Signed-off-by: Bart Van Assche <bvanassche@acm.org>
Reviewed-by: Chao Yu <chao@kernel.org>
Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
| -rw-r--r-- | Documentation/ABI/testing/sysfs-fs-f2fs | 10 | ||||
| -rw-r--r-- | fs/f2fs/data.c | 21 | ||||
| -rw-r--r-- | fs/f2fs/f2fs.h | 2 | ||||
| -rw-r--r-- | fs/f2fs/super.c | 1 | ||||
| -rw-r--r-- | fs/f2fs/sysfs.c | 2 |
5 files changed, 35 insertions, 1 deletions
diff --git a/Documentation/ABI/testing/sysfs-fs-f2fs b/Documentation/ABI/testing/sysfs-fs-f2fs index 1b58c029abd0..f4e6a7415cde 100644 --- a/Documentation/ABI/testing/sysfs-fs-f2fs +++ b/Documentation/ABI/testing/sysfs-fs-f2fs @@ -1002,3 +1002,13 @@ Description: It can be used to tune priority of f2fs critical task, e.g. f2fs_ck threads, limitation as below: - it requires user has CAP_SYS_NICE capability. - the range is [100, 139], by default the value is 120. + +What: /sys/fs/f2fs/<disk>/max_atc_write_bio_size +Date: June 2026 +Contact: Bart Van Assche <bvanassche@acm.org> +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 size of 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. diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 042ed8ad9cc3..ef2a567acdac 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -411,11 +411,30 @@ static void f2fs_write_end_bio(struct bio *bio) bio_put(bio); } +static void f2fs_write_end_io_work(struct work_struct *work) +{ + struct bio *bio = &container_of(work, struct f2fs_bio, work)->bio; + + f2fs_write_end_bio(bio); +} + static void f2fs_write_end_io(struct bio *bio) { + struct f2fs_sb_info *sbi; + iostat_update_and_unbind_ctx(bio); - f2fs_write_end_bio(bio); + sbi = bio->bi_private; + + if (in_atomic() && bio->bi_iter.bi_size > sbi->max_atc_write_bio_size) { + struct work_struct *w; + + w = &container_of(bio, struct f2fs_bio, bio)->work; + INIT_WORK(w, f2fs_write_end_io_work); + queue_work(sbi->wq, w); + } else { + f2fs_write_end_bio(bio); + } } #ifdef CONFIG_BLK_DEV_ZONED diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 8011bbdf2c68..8e2fb0bda467 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -1800,6 +1800,8 @@ struct f2fs_sb_info { struct f2fs_sm_info *sm_info; /* segment manager */ /* for bio operations */ + /* Largest write bio size completed in atomic context (atc). */ + u32 max_atc_write_bio_size; 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 90a47cf86378..25309e6d4156 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -5068,6 +5068,7 @@ try_onemore: sb->s_fs_info = sbi; sbi->raw_super = raw_super; + sbi->max_atc_write_bio_size = 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 be92c05a5420..d9f81edca04a 100644 --- a/fs/f2fs/sysfs.c +++ b/fs/f2fs/sysfs.c @@ -1266,6 +1266,7 @@ F2FS_SBI_RW_ATTR(gc_idle_interval, interval_time[GC_TIME]); 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_GENERAL_RW_ATTR(max_victim_search); F2FS_SBI_GENERAL_RW_ATTR(migration_granularity); F2FS_SBI_GENERAL_RW_ATTR(migration_window_granularity); @@ -1509,6 +1510,7 @@ static struct attribute *f2fs_attrs[] = { ATTR_LIST(seq_file_ra_mul), ATTR_LIST(gc_segment_mode), ATTR_LIST(gc_reclaimed_segments), + ATTR_LIST(max_atc_write_bio_size), ATTR_LIST(max_fragment_chunk), ATTR_LIST(max_fragment_hole), ATTR_LIST(current_atomic_write), |
