cgroup/cpuset: use mems_allowed_seq when checking a policy nodemask

A cpuset update changes the task's mems_allowed and rebinds its
mempolicy in separate steps under mems_allowed_seq, but the check
reads both without it.  This can cause a spurious numa placement
miss under some cpuset/mempolicy combinations.

A reader that loads one mask before the update and the other after
it can see a spurious empty intersection and drop the nodemask.

For example, a task with cpuset.mems={0} and MPOL_BIND={0}.  If the
cpuset moves to mems={1,2}, the policy is rebound to {1}.

The allocator can race and compare the old mems_allowed {0} against
the new policy {1} - which will result in a dropped nodemask and a
misplaced allocation on node 2.

Simply retry the intersection until mems_allowed_seq is stable.

This covers the task policy, which is rebound under mems_allowed_seq.
VMA policies are rebound afterwards by mpol_rebind_mm(), outside
mems_allowed_seq, and are not covered by this change.

Fixes: cc9a6c8776 ("cpuset: mm: reduce large amounts of memory barrier related damage v3")
Reviewed-by: Vlastimil Babka (SUSE) <vbabka@kernel.org>
Reviewed-by: Waiman Long <longman@redhat.com>
Signed-off-by: Gregory Price (Meta) <gourry@gourry.net>
Signed-off-by: Tejun Heo <tj@kernel.org>
This commit is contained in:
Gregory Price (Meta) 2026-10-19 12:21:38 -04:00 committed by Tejun Heo
parent 1850c66951
commit b0cec62d28

View File

@ -4265,10 +4265,21 @@ nodemask_t cpuset_mems_allowed(struct task_struct *tsk)
* @nodemask: the nodemask to be checked
*
* Are any of the nodes in the nodemask allowed in current->mems_allowed?
*
* A cpuset update rebinds the task's mempolicy and changes mems_allowed in
* separate steps, so retry if an update raced with the check.
*/
int cpuset_nodemask_valid_mems_allowed(const nodemask_t *nodemask)
{
return nodes_intersects(*nodemask, current->mems_allowed);
unsigned int seq;
int ret;
do {
seq = read_mems_allowed_begin();
ret = nodes_intersects(*nodemask, current->mems_allowed);
} while (read_mems_allowed_retry(seq));
return ret;
}
/*