[PATCH v2 08/12] workqueue, mm: Support dynamic housekeeping mask updates

Qiliang Yuan
2026-04-13T07:44:47Z

Unbound workqueues and kcompactd threads determine their default CPU
affinity from housekeeping masks (HK_TYPE_WQ, HK_TYPE_DOMAIN, and
HK_TYPE_KTHREAD) at boot. Currently, these boundaries are static and
are not updated if housekeeping is reconfigured at runtime.

Implement housekeeping notifiers for both workqueue and mm compaction.

This ensures that unbound workqueue tasks and background compaction
threads honor dynamic isolation boundaries configured via sysfs or
cpuset at runtime.

Signed-off-by: Qiliang Yuan <realwujing@gmail.com>
---
kernel/workqueue.c | 42 ++++++++++++++++++++++++++++++++++++++++++
mm/compaction.c | 27 +++++++++++++++++++++++++++
2 files changed, 69 insertions(+)

diff --git a/kernel/workqueue.c b/kernel/workqueue.c
index eda756556341a..354e788004b48 100644
--- a/kernel/workqueue.c
+++ b/kernel/workqueue.c
@@ -8008,6 +8008,47 @@ static void __init wq_cpu_intensive_thresh_init(void)
wq_cpu_intensive_thresh_us = thresh;
}

+static int wq_housekeeping_reconfigure(struct notifier_block *nb,
+ unsigned long action, void *data)
+{
+ if (action == HK_UPDATE_MASK) {
+ struct housekeeping_update *upd = data;
+ unsigned int type = upd->type;
+
+ if (type == HK_TYPE_WQ || type == HK_TYPE_DOMAIN) {
+ cpumask_var_t cpumask;
+
+ if (!alloc_cpumask_var(&cpumask, GFP_KERNEL)) {
+ pr_warn("workqueue: failed to allocate cpumask for housekeeping update\n");
+ return NOTIFY_BAD;
+ }
+
+ cpumask_copy(cpumask, cpu_possible_mask);
+ if (!cpumask_empty(housekeeping_cpumask(HK_TYPE_WQ)))
+ cpumask_and(cpumask, cpumask, housekeeping_cpumask(HK_TYPE_WQ));
+ if (!cpumask_empty(housekeeping_cpumask(HK_TYPE_DOMAIN)))
+ cpumask_and(cpumask, cpumask, housekeeping_cpumask(HK_TYPE_DOMAIN));
+
+ workqueue_set_unbound_cpumask(cpumask);
+
+ if (type == HK_TYPE_DOMAIN) {
+ apply_wqattrs_lock();
+ cpumask_andnot(wq_isolated_cpumask, cpu_possible_mask,
+ housekeeping_cpumask(HK_TYPE_DOMAIN));
+ apply_wqattrs_unlock();
+ }
+
+ free_cpumask_var(cpumask);
+ }
+ }
+
+ return NOTIFY_OK;
+}
+
+static struct notifier_block wq_housekeeping_nb = {
+ .notifier_call = wq_housekeeping_reconfigure,
+};
+
/**
* workqueue_init - bring workqueue subsystem fully online
*
@@ -8068,6 +8109,7 @@ void __init workqueue_init(void)

wq_online = true;
wq_watchdog_init();
+ housekeeping_register_notifier(&wq_housekeeping_nb);
}

/*
diff --git a/mm/compaction.c b/mm/compaction.c
index 1e8f8eca318c6..574ee3c6dc942 100644
--- a/mm/compaction.c
+++ b/mm/compaction.c
@@ -24,6 +24,7 @@
#include <linux/page_owner.h>
#include <linux/psi.h>
#include <linux/cpuset.h>
+#include <linux/sched/isolation.h>
#include "internal.h"

#ifdef CONFIG_COMPACTION
@@ -3246,6 +3247,7 @@ void __meminit kcompactd_run(int nid)
pr_err("Failed to start kcompactd on node %d\n", nid);
pgdat->kcompactd = NULL;
} else {
+ housekeeping_affine(pgdat->kcompactd, HK_TYPE_KTHREAD);
wake_up_process(pgdat->kcompactd);
}
}
@@ -3320,6 +3322,30 @@ static const struct ctl_table vm_compaction[] = {
},
};

+static int kcompactd_housekeeping_reconfigure(struct notifier_block *nb,
+ unsigned long action, void *data)
+{
+ struct housekeeping_update *upd = data;
+ unsigned int type = upd->type;
+
+ if (action == HK_UPDATE_MASK && type == HK_TYPE_KTHREAD) {
+ int nid;
+
+ for_each_node_state(nid, N_MEMORY) {
+ pg_data_t *pgdat = NODE_DATA(nid);
+
+ if (pgdat->kcompactd)
+ housekeeping_affine(pgdat->kcompactd, HK_TYPE_KTHREAD);
+ }
+ }
+
+ return NOTIFY_OK;
+}
+
+static struct notifier_block kcompactd_housekeeping_nb = {
+ .notifier_call = kcompactd_housekeeping_reconfigure,
+};
+
static int __init kcompactd_init(void)
{
int nid;
@@ -3327,6 +3353,7 @@ static int __init kcompactd_init(void)
for_each_node_state(nid, N_MEMORY)
kcompactd_run(nid);
register_sysctl_init("vm", vm_compaction);
+ housekeeping_register_notifier(&kcompactd_housekeeping_nb);
return 0;
}
subsys_initcall(kcompactd_init)

--
2.43.0