* [f2fs-dev] [PATCH 1/4] f2fs: Prepare for supporting delayed bio completion
2026-05-28 21:20 [f2fs-dev] [PATCH 0/4] Reduce the time spent in interrupt context Bart Van Assche via Linux-f2fs-devel
@ 2026-05-28 21:20 ` Bart Van Assche via Linux-f2fs-devel
2026-05-28 21:20 ` [f2fs-dev] [PATCH 2/4] f2fs: Rename f2fs_post_read_wq into f2fs_wq Bart Van Assche via Linux-f2fs-devel
` (2 subsequent siblings)
3 siblings, 0 replies; 5+ messages in thread
From: Bart Van Assche via Linux-f2fs-devel @ 2026-05-28 21:20 UTC (permalink / raw)
To: Jaegeuk Kim; +Cc: Bart Van Assche, linux-f2fs-devel
Use bio frontpadding to allocate memory for a work_struct when
allocating a bio.
Signed-off-by: Bart Van Assche <bvanassche@acm.org>
---
fs/f2fs/data.c | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c
index 8d4f1e75dee3..1659a57a6d5b 100644
--- a/fs/f2fs/data.c
+++ b/fs/f2fs/data.c
@@ -40,12 +40,17 @@ struct f2fs_folio_state {
unsigned int read_pages_pending;
};
+struct f2fs_bio {
+ struct work_struct work;
+ struct bio bio;
+};
+
#define F2FS_BIO_POOL_SIZE NR_CURSEG_TYPE
int __init f2fs_init_bioset(void)
{
return bioset_init(&f2fs_bioset, F2FS_BIO_POOL_SIZE,
- 0, BIOSET_NEED_BVECS);
+ offsetof(struct f2fs_bio, bio), BIOSET_NEED_BVECS);
}
void f2fs_destroy_bioset(void)
_______________________________________________
Linux-f2fs-devel mailing list
Linux-f2fs-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/linux-f2fs-devel
^ permalink raw reply related [flat|nested] 5+ messages in thread* [f2fs-dev] [PATCH 2/4] f2fs: Rename f2fs_post_read_wq into f2fs_wq
2026-05-28 21:20 [f2fs-dev] [PATCH 0/4] Reduce the time spent in interrupt context Bart Van Assche via Linux-f2fs-devel
2026-05-28 21:20 ` [f2fs-dev] [PATCH 1/4] f2fs: Prepare for supporting delayed bio completion Bart Van Assche via Linux-f2fs-devel
@ 2026-05-28 21:20 ` Bart Van Assche via Linux-f2fs-devel
2026-05-28 21:20 ` [f2fs-dev] [PATCH 3/4] f2fs: Split f2fs_write_end_io() Bart Van Assche via Linux-f2fs-devel
2026-05-28 21:20 ` [f2fs-dev] [PATCH 4/4] f2fs: Run f2fs_write_end_io() asynchronously Bart Van Assche via Linux-f2fs-devel
3 siblings, 0 replies; 5+ messages in thread
From: Bart Van Assche via Linux-f2fs-devel @ 2026-05-28 21:20 UTC (permalink / raw)
To: Jaegeuk Kim; +Cc: Bart Van Assche, linux-f2fs-devel
Rename f2fs_post_read_wq into f2fs_wq. Create it unconditionally.
Prepare for using this workqueue for completing write bios.
Signed-off-by: Bart Van Assche <bvanassche@acm.org>
---
fs/f2fs/compress.c | 2 +-
fs/f2fs/data.c | 22 ++++++++--------------
fs/f2fs/f2fs.h | 6 +++---
fs/f2fs/super.c | 8 ++++----
4 files changed, 16 insertions(+), 22 deletions(-)
diff --git a/fs/f2fs/compress.c b/fs/f2fs/compress.c
index 881e76158b96..b3c016c4094b 100644
--- a/fs/f2fs/compress.c
+++ b/fs/f2fs/compress.c
@@ -1807,7 +1807,7 @@ static void f2fs_put_dic(struct decompress_io_ctx *dic, bool in_task)
f2fs_free_dic(dic, false);
} else {
INIT_WORK(&dic->free_work, f2fs_late_free_dic);
- queue_work(dic->sbi->post_read_wq, &dic->free_work);
+ queue_work(dic->sbi->wq, &dic->free_work);
}
}
}
diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c
index 1659a57a6d5b..080d69fa8cd1 100644
--- a/fs/f2fs/data.c
+++ b/fs/f2fs/data.c
@@ -341,7 +341,7 @@ static void f2fs_read_end_io(struct bio *bio)
f2fs_handle_step_decompress(ctx, intask);
} else if (enabled_steps) {
INIT_WORK(&ctx->work, f2fs_post_read_work);
- queue_work(ctx->sbi->post_read_wq, &ctx->work);
+ queue_work(ctx->sbi->wq, &ctx->work);
return;
}
}
@@ -4477,23 +4477,17 @@ void f2fs_destroy_post_read_processing(void)
kmem_cache_destroy(bio_post_read_ctx_cache);
}
-int f2fs_init_post_read_wq(struct f2fs_sb_info *sbi)
+int f2fs_init_wq(struct f2fs_sb_info *sbi)
{
- if (!f2fs_sb_has_encrypt(sbi) &&
- !f2fs_sb_has_verity(sbi) &&
- !f2fs_sb_has_compression(sbi))
- return 0;
-
- sbi->post_read_wq = alloc_workqueue("f2fs_post_read_wq",
- WQ_UNBOUND | WQ_HIGHPRI,
- num_online_cpus());
- return sbi->post_read_wq ? 0 : -ENOMEM;
+ sbi->wq = alloc_workqueue("f2fs_wq", WQ_UNBOUND | WQ_HIGHPRI,
+ num_online_cpus());
+ return sbi->wq ? 0 : -ENOMEM;
}
-void f2fs_destroy_post_read_wq(struct f2fs_sb_info *sbi)
+void f2fs_destroy_wq(struct f2fs_sb_info *sbi)
{
- if (sbi->post_read_wq)
- destroy_workqueue(sbi->post_read_wq);
+ if (sbi->wq)
+ destroy_workqueue(sbi->wq);
}
int __init f2fs_init_bio_entry_cache(void)
diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h
index 91f506e7c9cf..30353c439d3c 100644
--- a/fs/f2fs/f2fs.h
+++ b/fs/f2fs/f2fs.h
@@ -1969,7 +1969,7 @@ struct f2fs_sb_info {
/* Precomputed FS UUID checksum for seeding other checksums */
__u32 s_chksum_seed;
- struct workqueue_struct *post_read_wq; /* post read workqueue */
+ struct workqueue_struct *wq; /* bio completion workqueue */
/*
* If we are in irq context, let's update error information into
@@ -4193,8 +4193,8 @@ bool f2fs_overwrite_io(struct inode *inode, loff_t pos, size_t len);
void f2fs_clear_page_cache_dirty_tag(struct folio *folio);
int f2fs_init_post_read_processing(void);
void f2fs_destroy_post_read_processing(void);
-int f2fs_init_post_read_wq(struct f2fs_sb_info *sbi);
-void f2fs_destroy_post_read_wq(struct f2fs_sb_info *sbi);
+int f2fs_init_wq(struct f2fs_sb_info *sbi);
+void f2fs_destroy_wq(struct f2fs_sb_info *sbi);
extern const struct iomap_ops f2fs_iomap_ops;
/*
diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c
index ccf806b676f5..5a100f740b3f 100644
--- a/fs/f2fs/super.c
+++ b/fs/f2fs/super.c
@@ -2074,7 +2074,7 @@ static void f2fs_put_super(struct super_block *sb)
/* flush s_error_work before sbi destroy */
flush_work(&sbi->s_error_work);
- f2fs_destroy_post_read_wq(sbi);
+ f2fs_destroy_wq(sbi);
kvfree(sbi->ckpt);
@@ -5130,9 +5130,9 @@ static int f2fs_fill_super(struct super_block *sb, struct fs_context *fc)
goto free_devices;
}
- err = f2fs_init_post_read_wq(sbi);
+ err = f2fs_init_wq(sbi);
if (err) {
- f2fs_err(sbi, "Failed to initialize post read workqueue");
+ f2fs_err(sbi, "Failed to create workqueue");
goto free_devices;
}
@@ -5419,7 +5419,7 @@ static int f2fs_fill_super(struct super_block *sb, struct fs_context *fc)
f2fs_stop_ckpt_thread(sbi);
/* flush s_error_work before sbi destroy */
flush_work(&sbi->s_error_work);
- f2fs_destroy_post_read_wq(sbi);
+ f2fs_destroy_wq(sbi);
free_devices:
destroy_device_list(sbi);
kvfree(sbi->ckpt);
_______________________________________________
Linux-f2fs-devel mailing list
Linux-f2fs-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/linux-f2fs-devel
^ permalink raw reply related [flat|nested] 5+ messages in thread* [f2fs-dev] [PATCH 3/4] f2fs: Split f2fs_write_end_io()
2026-05-28 21:20 [f2fs-dev] [PATCH 0/4] Reduce the time spent in interrupt context Bart Van Assche via Linux-f2fs-devel
2026-05-28 21:20 ` [f2fs-dev] [PATCH 1/4] f2fs: Prepare for supporting delayed bio completion Bart Van Assche via Linux-f2fs-devel
2026-05-28 21:20 ` [f2fs-dev] [PATCH 2/4] f2fs: Rename f2fs_post_read_wq into f2fs_wq Bart Van Assche via Linux-f2fs-devel
@ 2026-05-28 21:20 ` Bart Van Assche via Linux-f2fs-devel
2026-05-28 21:20 ` [f2fs-dev] [PATCH 4/4] f2fs: Run f2fs_write_end_io() asynchronously Bart Van Assche via Linux-f2fs-devel
3 siblings, 0 replies; 5+ messages in thread
From: Bart Van Assche via Linux-f2fs-devel @ 2026-05-28 21:20 UTC (permalink / raw)
To: Jaegeuk Kim; +Cc: Bart Van Assche, linux-f2fs-devel
Prepare for running most of the write completion work asynchronously.
Signed-off-by: Bart Van Assche <bvanassche@acm.org>
---
fs/f2fs/data.c | 14 +++++++++-----
1 file changed, 9 insertions(+), 5 deletions(-)
diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c
index 080d69fa8cd1..48c004976c4e 100644
--- a/fs/f2fs/data.c
+++ b/fs/f2fs/data.c
@@ -349,14 +349,11 @@ static void f2fs_read_end_io(struct bio *bio)
f2fs_verify_and_finish_bio(bio, intask);
}
-static void f2fs_write_end_io(struct bio *bio)
+static void f2fs_write_end_bio(struct bio *bio)
{
- struct f2fs_sb_info *sbi;
+ struct f2fs_sb_info *sbi = bio->bi_private;
struct folio_iter fi;
- iostat_update_and_unbind_ctx(bio);
- sbi = bio->bi_private;
-
if (time_to_inject(sbi, FAULT_WRITE_IO))
bio->bi_status = BLK_STS_IOERR;
@@ -412,6 +409,13 @@ static void f2fs_write_end_io(struct bio *bio)
bio_put(bio);
}
+static void f2fs_write_end_io(struct bio *bio)
+{
+ iostat_update_and_unbind_ctx(bio);
+
+ f2fs_write_end_bio(bio);
+}
+
#ifdef CONFIG_BLK_DEV_ZONED
static void f2fs_zone_write_end_io(struct bio *bio)
{
_______________________________________________
Linux-f2fs-devel mailing list
Linux-f2fs-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/linux-f2fs-devel
^ permalink raw reply related [flat|nested] 5+ messages in thread* [f2fs-dev] [PATCH 4/4] f2fs: Run f2fs_write_end_io() asynchronously
2026-05-28 21:20 [f2fs-dev] [PATCH 0/4] Reduce the time spent in interrupt context Bart Van Assche via Linux-f2fs-devel
` (2 preceding siblings ...)
2026-05-28 21:20 ` [f2fs-dev] [PATCH 3/4] f2fs: Split f2fs_write_end_io() Bart Van Assche via Linux-f2fs-devel
@ 2026-05-28 21:20 ` Bart Van Assche via Linux-f2fs-devel
3 siblings, 0 replies; 5+ messages in thread
From: Bart Van Assche via Linux-f2fs-devel @ 2026-05-28 21:20 UTC (permalink / raw)
To: Jaegeuk Kim; +Cc: Bart Van Assche, linux-f2fs-devel
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.
Signed-off-by: Bart Van Assche <bvanassche@acm.org>
---
fs/f2fs/data.c | 21 ++++++++++++++++++++-
fs/f2fs/f2fs.h | 2 ++
fs/f2fs/super.c | 5 +++++
fs/f2fs/sysfs.c | 2 ++
4 files changed, 29 insertions(+), 1 deletion(-)
diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c
index 48c004976c4e..6e169490c4cf 100644
--- a/fs/f2fs/data.c
+++ b/fs/f2fs/data.c
@@ -409,11 +409,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 30353c439d3c..a6a3e01122e1 100644
--- a/fs/f2fs/f2fs.h
+++ b/fs/f2fs/f2fs.h
@@ -1763,6 +1763,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 5a100f740b3f..1e822380edb3 100644
--- a/fs/f2fs/super.c
+++ b/fs/f2fs/super.c
@@ -5007,6 +5007,11 @@ static int f2fs_fill_super(struct super_block *sb, struct fs_context *fc)
sb->s_fs_info = sbi;
sbi->raw_super = raw_super;
+ /*
+ * SZ_16K restricts the time spent on completing writes to about 150
+ * microseconds on an Arm Cortex-A520 core.
+ */
+ sbi->max_atc_write_bio_size = SZ_16K;
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 352e96ad5c3a..70b2e9be8f8b 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);
@@ -1508,6 +1509,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),
_______________________________________________
Linux-f2fs-devel mailing list
Linux-f2fs-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/linux-f2fs-devel
^ permalink raw reply related [flat|nested] 5+ messages in thread