[PATCH-next v6 7/6] cgroup/cpuset: Set old_mems_allowed from guarantee_online_mems() consistently
From: Waiman Long
Date: Thu Jun 04 2026 - 20:03:08 EST
An earlier patch has added an optimization in guarantee_online_mems()
to just return effective_mems for v2. However there is a short window
during memory hotunplug operation that it can return a nodemask with
no online node leading to possible memory OOM. To avoid this scenario,
though highly unlikely, the optimization is dropped.
Also set old_mems_allowed of the cpuset structure consistently with
the output of guarantee_online_mems() whenever an attach or a related
operation is in progress.
Signed-off-by: Waiman Long <longman@xxxxxxxxxx>
---
kernel/cgroup/cpuset.c | 28 ++++++++++++++--------------
1 file changed, 14 insertions(+), 14 deletions(-)
diff --git a/kernel/cgroup/cpuset.c b/kernel/cgroup/cpuset.c
index d624cd0a1e04..5dabe9d040e9 100644
--- a/kernel/cgroup/cpuset.c
+++ b/kernel/cgroup/cpuset.c
@@ -502,10 +502,6 @@ static void guarantee_active_cpus(struct task_struct *tsk,
*/
static void guarantee_online_mems(struct cpuset *cs, nodemask_t *pmask)
{
- if (cpuset_v2()) {
- *pmask = cs->effective_mems;
- return;
- }
while (!nodes_and(*pmask, cs->effective_mems, node_states[N_MEMORY]))
cs = parent_cs(cs);
}
@@ -3350,22 +3346,24 @@ static void cpuset_attach(struct cgroup_taskset *tset)
}
cs = css_cs(css);
+ guarantee_online_mems(cs, &cpuset_attach_nodemask_to);
+
/*
* In the default hierarchy, enabling cpuset in the child cgroups
* will trigger a cpuset_attach() call with no change in effective cpus
* and mems. In that case, we can optimize out by skipping the task
* iteration and update, but the destination cpuset list is iterated to
- * set old_mems_sllowed.
+ * set old_mems_allowed.
*/
- if (cpuset_v2()) {
- cpuset_attach_nodemask_to = cs->effective_mems;
- if (!attach_cpus_updated && !attach_mems_updated) {
- llist_for_each_entry(cs, dst_cs_head.first, attach_node)
- cs->old_mems_allowed = cs->effective_mems;
- goto out;
+ if (cpuset_v2() && !attach_cpus_updated && !attach_mems_updated) {
+ struct cpuset *tcs;
+
+ llist_for_each_entry(tcs, dst_cs_head.first, attach_node) {
+ if (tcs == cs)
+ continue;
+ guarantee_online_mems(tcs, &tcs->old_mems_allowed);
}
- } else {
- guarantee_online_mems(cs, &cpuset_attach_nodemask_to);
+ goto out;
}
cgroup_taskset_for_each(task, css, tset) {
@@ -3381,8 +3379,8 @@ static void cpuset_attach(struct cgroup_taskset *tset)
if (queue_task_work)
schedule_flush_migrate_mm();
- cs->old_mems_allowed = cpuset_attach_nodemask_to;
out:
+ cs->old_mems_allowed = cpuset_attach_nodemask_to;
reset_attach_in_progress();
clear_attach_data(false);
mutex_unlock(&cpuset_mutex);
@@ -3824,6 +3822,8 @@ static void cpuset_fork(struct task_struct *task)
/* CLONE_INTO_CGROUP */
mutex_lock(&cpuset_mutex);
guarantee_online_mems(cs, &cpuset_attach_nodemask_to);
+ cs->old_mems_allowed = cpuset_attach_nodemask_to;
+
/*
* Assume CPUs and memory nodes are updated
* A CLONE_INTO_CGROUP operation should have taken the cgroup mutex
--
2.54.0