mirror of
https://github.com/torvalds/linux.git
synced 2026-11-07 06:17:02 +01:00
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>
This commit is contained in:
parent
6dc2b804a0
commit
3de6b80941
|
|
@ -1002,3 +1002,13 @@ Description: It can be used to tune priority of f2fs critical task, e.g. f2fs_ck
|
||||||
threads, limitation as below:
|
threads, limitation as below:
|
||||||
- it requires user has CAP_SYS_NICE capability.
|
- it requires user has CAP_SYS_NICE capability.
|
||||||
- the range is [100, 139], by default the value is 120.
|
- 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.
|
||||||
|
|
|
||||||
|
|
@ -411,13 +411,32 @@ static void f2fs_write_end_bio(struct bio *bio)
|
||||||
bio_put(bio);
|
bio_put(bio);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void f2fs_write_end_io(struct bio *bio)
|
static void f2fs_write_end_io_work(struct work_struct *work)
|
||||||
{
|
{
|
||||||
iostat_update_and_unbind_ctx(bio);
|
struct bio *bio = &container_of(work, struct f2fs_bio, work)->bio;
|
||||||
|
|
||||||
f2fs_write_end_bio(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);
|
||||||
|
|
||||||
|
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
|
#ifdef CONFIG_BLK_DEV_ZONED
|
||||||
static void f2fs_zone_write_end_io(struct bio *bio)
|
static void f2fs_zone_write_end_io(struct bio *bio)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -1800,6 +1800,8 @@ struct f2fs_sb_info {
|
||||||
struct f2fs_sm_info *sm_info; /* segment manager */
|
struct f2fs_sm_info *sm_info; /* segment manager */
|
||||||
|
|
||||||
/* for bio operations */
|
/* 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 */
|
struct f2fs_bio_info *write_io[NR_PAGE_TYPE]; /* for write bios */
|
||||||
/* keep migration IO order for LFS mode */
|
/* keep migration IO order for LFS mode */
|
||||||
struct f2fs_rwsem io_order_lock;
|
struct f2fs_rwsem io_order_lock;
|
||||||
|
|
|
||||||
|
|
@ -5068,6 +5068,7 @@ static int f2fs_fill_super(struct super_block *sb, struct fs_context *fc)
|
||||||
|
|
||||||
sb->s_fs_info = sbi;
|
sb->s_fs_info = sbi;
|
||||||
sbi->raw_super = raw_super;
|
sbi->raw_super = raw_super;
|
||||||
|
sbi->max_atc_write_bio_size = UINT_MAX;
|
||||||
|
|
||||||
INIT_WORK(&sbi->s_error_work, f2fs_record_error_work);
|
INIT_WORK(&sbi->s_error_work, f2fs_record_error_work);
|
||||||
memcpy(sbi->errors, raw_super->s_errors, MAX_F2FS_ERRORS);
|
memcpy(sbi->errors, raw_super->s_errors, MAX_F2FS_ERRORS);
|
||||||
|
|
|
||||||
|
|
@ -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(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_pin_file_thresh, gc_pin_file_threshold);
|
||||||
F2FS_SBI_RW_ATTR(gc_reclaimed_segments, gc_reclaimed_segs);
|
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(max_victim_search);
|
||||||
F2FS_SBI_GENERAL_RW_ATTR(migration_granularity);
|
F2FS_SBI_GENERAL_RW_ATTR(migration_granularity);
|
||||||
F2FS_SBI_GENERAL_RW_ATTR(migration_window_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(seq_file_ra_mul),
|
||||||
ATTR_LIST(gc_segment_mode),
|
ATTR_LIST(gc_segment_mode),
|
||||||
ATTR_LIST(gc_reclaimed_segments),
|
ATTR_LIST(gc_reclaimed_segments),
|
||||||
|
ATTR_LIST(max_atc_write_bio_size),
|
||||||
ATTR_LIST(max_fragment_chunk),
|
ATTR_LIST(max_fragment_chunk),
|
||||||
ATTR_LIST(max_fragment_hole),
|
ATTR_LIST(max_fragment_hole),
|
||||||
ATTR_LIST(current_atomic_write),
|
ATTR_LIST(current_atomic_write),
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user