* [PATCH v2 3/5] f2fs: cache: wake up f2fs_writeback when exceeding threshold
@ 2026-10-09 9:18 Chao Yu
2026-10-09 10:13 ` Wenjie Qi
0 siblings, 1 reply; 2+ messages in thread
From: Chao Yu @ 2026-10-09 9:18 UTC (permalink / raw)
To: jaegeuk; +Cc: linux-f2fs-devel, linux-kernel, Chao Yu
From: Chao Yu <chao@kernel.org>
This patch introduces cache_wb_dirty_threshold and cache_wb_total_threshold
sysfs nodes under /sys/fs/f2fs/<disk>/, and allows waking up the
f2fs_writeback kthread when both thresholds are exceeded.
By default, cache_wb_total_threshold is zero, so if dirty cache number exceeds
cache_wb_dirty_threshold, it will trigger writebacking.
Signed-off-by: Chao Yu <chao@kernel.org>
---
v2:
- don't wake up f2fs_writeback if both thresholds are zero.
Documentation/ABI/testing/sysfs-fs-f2fs | 14 +++++++
fs/f2fs/cache.c | 55 ++++++++++++++++++++++++-
fs/f2fs/cache.h | 5 +++
fs/f2fs/sysfs.c | 8 +++-
4 files changed, 80 insertions(+), 2 deletions(-)
diff --git a/Documentation/ABI/testing/sysfs-fs-f2fs b/Documentation/ABI/testing/sysfs-fs-f2fs
index 5b196806b3bc..f50739f90ae9 100644
--- a/Documentation/ABI/testing/sysfs-fs-f2fs
+++ b/Documentation/ABI/testing/sysfs-fs-f2fs
@@ -1027,3 +1027,17 @@ Contact: "Chao Yu" <chao@kernel.org>
Description: This is a writable entry to control writeback interval of
f2fs_writeback-x:y, the range is [100, 30000], by default the value
is 5000, unit is ms.
+
+What: /sys/fs/f2fs/<disk>/cache_wb_dirty_threshold
+Date: October 2026
+Contact: "Chao Yu" <chao@kernel.org>
+Description: This is a writable entry for metadata cache, it is used to control
+ dirty cache threshold of to wake up f2fs_writeback-x:y for writeback,
+ by default the value is 8192, unit is blocks.
+
+What: /sys/fs/f2fs/<disk>/cache_wb_total_threshold
+Date: October 2026
+Contact: "Chao Yu" <chao@kernel.org>
+Description: This is a writable entry for metadata cache, it is used to control
+ total cache threshold to wake up f2fs_writeback-x:y for writeback,
+ by default the value is 0, unit is blocks.
diff --git a/fs/f2fs/cache.c b/fs/f2fs/cache.c
index f385fe928b5b..986e679f2d16 100644
--- a/fs/f2fs/cache.c
+++ b/fs/f2fs/cache.c
@@ -81,6 +81,7 @@ bool f2fs_mark_cache_dirty(struct f2fs_cached_block *entry)
f2fs_cache_update_tag(entry, F2FS_CACHE_TAG_NONE,
F2FS_CACHE_TAG_DIRTY);
inc_cache_count(cache->sbi, type);
+ f2fs_wake_up_cache_wb(cache->sbi);
return true;
}
@@ -681,6 +682,44 @@ unsigned long f2fs_shrink_node_cache(struct f2fs_sb_info *sbi,
return f2fs_do_shrink_cache(NODE_CACHE(sbi), nr_to_scan);
}
+static inline unsigned long f2fs_total_cached_entries(struct f2fs_sb_info *sbi)
+{
+ unsigned long total = META_CACHE(sbi)->num_entries +
+ NODE_CACHE(sbi)->num_entries;
+#ifdef CONFIG_F2FS_FS_COMPRESSION
+ if (test_opt(sbi, COMPRESS_CACHE))
+ total += COMPRESS_CACHE(sbi)->num_entries;
+#endif
+ return total;
+}
+
+static inline bool f2fs_should_wake_up_cache_wb(struct f2fs_sb_info *sbi)
+{
+ struct f2fs_cache_kthread *cache_thread = &sbi->cache_thread;
+ unsigned int total_threshold;
+ unsigned int dirty_threshold;
+ s64 nr_dirty;
+
+ if (!cache_thread->cache_wb_task)
+ return false;
+
+ total_threshold = cache_thread->cache_wb_total_threshold;
+ dirty_threshold = cache_thread->cache_wb_dirty_threshold;
+
+ if (total_threshold &&
+ f2fs_total_cached_entries(sbi) < total_threshold)
+ return false;
+
+ nr_dirty = get_nr_caches(sbi, F2FS_DIRTY_META) +
+ get_nr_caches(sbi, F2FS_DIRTY_NODES);
+
+ if (dirty_threshold && nr_dirty < dirty_threshold)
+ return false;
+
+ /* don't wake up f2fs_writeback if both thresholds are zero */
+ return total_threshold || dirty_threshold;
+}
+
static int f2fs_cache_writeback_kthread(void *data)
{
struct f2fs_sb_info *sbi = data;
@@ -693,7 +732,8 @@ static int f2fs_cache_writeback_kthread(void *data)
unsigned int interval = cache_thread->cache_wb_interval;
wait_event_freezable_timeout(*wq,
- kthread_should_stop(),
+ kthread_should_stop() ||
+ f2fs_should_wake_up_cache_wb(sbi),
msecs_to_jiffies(interval));
if (kthread_should_stop())
@@ -719,6 +759,17 @@ static int f2fs_cache_writeback_kthread(void *data)
return 0;
}
+void f2fs_wake_up_cache_wb(struct f2fs_sb_info *sbi)
+{
+ struct f2fs_cache_kthread *cache_thread = &sbi->cache_thread;
+
+ if (!f2fs_should_wake_up_cache_wb(sbi))
+ return;
+
+ if (wq_has_sleeper(&cache_thread->cache_wb_wq))
+ wake_up(&cache_thread->cache_wb_wq);
+}
+
int f2fs_start_cache_wb_thread(struct f2fs_sb_info *sbi)
{
struct f2fs_cache_kthread *cache_thread = &sbi->cache_thread;
@@ -731,6 +782,8 @@ int f2fs_start_cache_wb_thread(struct f2fs_sb_info *sbi)
init_waitqueue_head(&cache_thread->cache_wb_wq);
cache_thread->cache_wb_interval = DEF_DIRTY_CACHE_TIMEOUT;
+ cache_thread->cache_wb_dirty_threshold = DEF_CACHE_WB_DIRTY_THRESH;
+ cache_thread->cache_wb_total_threshold = 0;
snprintf(name, sizeof(name), "f2fs_writeback-%u:%u",
MAJOR(dev), MINOR(dev));
diff --git a/fs/f2fs/cache.h b/fs/f2fs/cache.h
index 3df3a562c46f..7a94fb8139d3 100644
--- a/fs/f2fs/cache.h
+++ b/fs/f2fs/cache.h
@@ -234,13 +234,18 @@ unsigned long f2fs_shrink_node_cache(struct f2fs_sb_info *sbi,
#define MIN_DIRTY_CACHE_TIMEOUT 100
#define MAX_DIRTY_CACHE_TIMEOUT 30000
+#define DEF_CACHE_WB_DIRTY_THRESH (8192)
+
struct f2fs_cache_kthread {
struct task_struct *cache_wb_task;
wait_queue_head_t cache_wb_wq;
unsigned int cache_wb_interval;
+ unsigned int cache_wb_dirty_threshold;
+ unsigned int cache_wb_total_threshold;
};
int f2fs_start_cache_wb_thread(struct f2fs_sb_info *sbi);
void f2fs_stop_cache_wb_thread(struct f2fs_sb_info *sbi);
+void f2fs_wake_up_cache_wb(struct f2fs_sb_info *sbi);
#endif /* _LINUX_F2FS_CACHE_H */
diff --git a/fs/f2fs/sysfs.c b/fs/f2fs/sysfs.c
index afce74744d4d..95f8dc0c4218 100644
--- a/fs/f2fs/sysfs.c
+++ b/fs/f2fs/sysfs.c
@@ -1019,7 +1019,9 @@ static ssize_t f2fs_sbi_store(struct f2fs_attr *a,
a->struct_type == GC_THREAD);
bool thread_entry = !strcmp(a->attr.name, "ckpt_thread_ioprio") ||
!strcmp(a->attr.name, "critical_task_priority") ||
- !strcmp(a->attr.name, "cache_wb_interval");
+ !strcmp(a->attr.name, "cache_wb_interval") ||
+ !strcmp(a->attr.name, "cache_wb_dirty_threshold") ||
+ !strcmp(a->attr.name, "cache_wb_total_threshold");
if (gc_entry || thread_entry) {
if (!down_read_trylock(&sbi->sb->s_umount))
@@ -1363,6 +1365,8 @@ ATGC_INFO_RW_ATTR(atgc_age_threshold, age_threshold);
/* WB_THREAD ATTR */
WB_THREAD_RW_ATTR(cache_wb_interval, cache_wb_interval);
+WB_THREAD_RW_ATTR(cache_wb_dirty_threshold, cache_wb_dirty_threshold);
+WB_THREAD_RW_ATTR(cache_wb_total_threshold, cache_wb_total_threshold);
F2FS_GENERAL_RO_ATTR(dirty_segments);
F2FS_GENERAL_RO_ATTR(free_segments);
@@ -1552,6 +1556,8 @@ static struct attribute *f2fs_attrs[] = {
ATTR_LIST(adjust_lock_priority),
ATTR_LIST(critical_task_priority),
ATTR_LIST(cache_wb_interval),
+ ATTR_LIST(cache_wb_dirty_threshold),
+ ATTR_LIST(cache_wb_total_threshold),
NULL,
};
ATTRIBUTE_GROUPS(f2fs);
--
2.49.0
^ permalink raw reply [flat|nested] 2+ messages in thread
* Re: [PATCH v2 3/5] f2fs: cache: wake up f2fs_writeback when exceeding threshold
2026-10-09 9:18 [PATCH v2 3/5] f2fs: cache: wake up f2fs_writeback when exceeding threshold Chao Yu
@ 2026-10-09 10:13 ` Wenjie Qi
0 siblings, 0 replies; 2+ messages in thread
From: Wenjie Qi @ 2026-10-09 10:13 UTC (permalink / raw)
To: chao; +Cc: jaegeuk, linux-f2fs-devel, linux-kernel, qiwenjie, qwjhust
Hi Chao,
> + if (dirty_threshold && nr_dirty < dirty_threshold)
> + return false;
> +
> + /* don't wake up f2fs_writeback if both thresholds are zero */
> + return total_threshold || dirty_threshold;
There is a related case with only the total threshold enabled. With
cache_wb_dirty_threshold=0 and cache_wb_total_threshold=1, clean cached
entries keep the predicate true even when there are no dirty meta/node
caches. Writeback does not necessarily evict those entries, so the
thread can keep looping without sleeping.
I checked this in QEMU using dev-test plus my both-zero fix, which has
the same predicate for these fixed threshold values. In a three-second
idle interval, the writeback thread used about three CPU seconds;
the total cache count was 22 and the dirty count was zero at both
sample points.
Should the thread wait when no dirty caches remain, even if the total
threshold is still met, or is different behavior intended for this mode?
Best regards,
Wenjie
^ permalink raw reply [flat|nested] 2+ messages in thread
end of thread, other threads:[~2026-10-09 10:13 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-10-09 9:18 [PATCH v2 3/5] f2fs: cache: wake up f2fs_writeback when exceeding threshold Chao Yu
2026-10-09 10:13 ` Wenjie Qi
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®