diff options
| author | Christian Brauner <brauner@kernel.org> | 2026-01-12 11:07:30 +0100 |
|---|---|---|
| committer | Christian Brauner <brauner@kernel.org> | 2026-01-12 11:07:30 +0100 |
| commit | e16688084df76e719b329a1a56b338ec491002e0 (patch) | |
| tree | 963f1ef602c5429bd97e53a2a17fabc5c83798c7 | |
| parent | c644bce62b9c6b441143a03c910f986109c47001 (diff) | |
| parent | 30ef9a20f1fdf6ab483d64fe3d54ba7d07b9b46f (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.rst | 2 | ||||
| -rw-r--r-- | fs/fs-writeback.c | 14 |
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; } |
