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>
This commit is contained in:
Christian Brauner 2026-01-12 11:07:30 +01:00
commit e16688084d
No known key found for this signature in database
GPG Key ID: 91C61BC06578DCA2
2 changed files with 12 additions and 4 deletions

View File

@ -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
=========================

View File

@ -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;
}