summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristian Brauner <brauner@kernel.org>2026-01-12 11:07:30 +0100
committerChristian Brauner <brauner@kernel.org>2026-01-12 11:07:30 +0100
commite16688084df76e719b329a1a56b338ec491002e0 (patch)
tree963f1ef602c5429bd97e53a2a17fabc5c83798c7
parentc644bce62b9c6b441143a03c910f986109c47001 (diff)
parent30ef9a20f1fdf6ab483d64fe3d54ba7d07b9b46f (diff)
Merge patch series "Fix vm.dirtytime_expire_seconds=0 causing 100% CPU"
Laveesh Bansal <laveeshb@laveeshbansal.com> says: Setting vm.dirtytime_expire_seconds to 0 causes wakeup_dirtytime_writeback() to reschedule itself with a delay of 0, creating an infinite busy loop that spins kworker at 100% CPU. This series: - Patch 1: Fixes the bug by handling interval=0 as "disable writeback" (consistent with dirty_writeback_centisecs behavior) - Patch 2: Documents that setting the value to 0 disables writeback Tested by booting kernels in QEMU with virtme-ng: - Buggy kernel: kworker CPU spikes to ~73% when interval set to 0 - Fixed kernel: CPU remains normal, writeback correctly disabled - Re-enabling (0 -> non-zero): writeback resumes correctly * patches from https://patch.msgid.link/20260106145059.543282-1-laveeshb@laveeshbansal.com: docs: clarify that dirtytime_expire_seconds=0 disables writeback writeback: fix 100% CPU usage when dirtytime_expire_interval is 0 Link: https://patch.msgid.link/20260106145059.543282-1-laveeshb@laveeshbansal.com Signed-off-by: Christian Brauner <brauner@kernel.org>
-rw-r--r--Documentation/admin-guide/sysctl/vm.rst2
-rw-r--r--fs/fs-writeback.c14
2 files changed, 12 insertions, 4 deletions
diff --git a/Documentation/admin-guide/sysctl/vm.rst b/Documentation/admin-guide/sysctl/vm.rst
index 4d71211fdad8..e2fdbc521033 100644
--- a/Documentation/admin-guide/sysctl/vm.rst
+++ b/Documentation/admin-guide/sysctl/vm.rst
@@ -231,6 +231,8 @@ eventually gets pushed out to disk. This tunable is used to define when dirty
inode is old enough to be eligible for writeback by the kernel flusher threads.
And, it is also used as the interval to wakeup dirtytime_writeback thread.
+Setting this to zero disables periodic dirtytime writeback.
+
dirty_writeback_centisecs
=========================
diff --git a/fs/fs-writeback.c b/fs/fs-writeback.c
index 6800886c4d10..cd21c74cd0e5 100644
--- a/fs/fs-writeback.c
+++ b/fs/fs-writeback.c
@@ -2492,7 +2492,8 @@ static void wakeup_dirtytime_writeback(struct work_struct *w)
wb_wakeup(wb);
}
rcu_read_unlock();
- schedule_delayed_work(&dirtytime_work, dirtytime_expire_interval * HZ);
+ if (dirtytime_expire_interval)
+ schedule_delayed_work(&dirtytime_work, dirtytime_expire_interval * HZ);
}
static int dirtytime_interval_handler(const struct ctl_table *table, int write,
@@ -2501,8 +2502,12 @@ static int dirtytime_interval_handler(const struct ctl_table *table, int write,
int ret;
ret = proc_dointvec_minmax(table, write, buffer, lenp, ppos);
- if (ret == 0 && write)
- mod_delayed_work(system_percpu_wq, &dirtytime_work, 0);
+ if (ret == 0 && write) {
+ if (dirtytime_expire_interval)
+ mod_delayed_work(system_percpu_wq, &dirtytime_work, 0);
+ else
+ cancel_delayed_work_sync(&dirtytime_work);
+ }
return ret;
}
@@ -2519,7 +2524,8 @@ static const struct ctl_table vm_fs_writeback_table[] = {
static int __init start_dirtytime_writeback(void)
{
- schedule_delayed_work(&dirtytime_work, dirtytime_expire_interval * HZ);
+ if (dirtytime_expire_interval)
+ schedule_delayed_work(&dirtytime_work, dirtytime_expire_interval * HZ);
register_sysctl_init("vm", vm_fs_writeback_table);
return 0;
}