[PATCH v2 4/4] mm: hugetlb: fix cpuset violations in gigantic surplus allocations

From: Qi Zheng

Date: Wed Nov 04 2026 - 23:19:08 EST


From: Qi Zheng <zhengqi.arch@xxxxxxxxxxxxx>

CMA and the contiguous allocator trust a supplied nodemask without
checking the current cpuset. Gigantic surplus allocations can therefore
use nodes forbidden to the faulting thread.

For example, moving a non-leader thread to another cgroup v1 cpuset
updates its mems_allowed without rebinding the shared VMA policy:

CPU0 (cpuset attach) CPU1 (thread T)
------------------- ---------------
T->mems_allowed = {0}
/* Skip mpol_rebind_mm()
for a non-leader. */
/* VMA MPOL_BIND remains {0,1}. */
hugetlb_cma_alloc_frozen_folio()
/* node0 fails; node1 succeeds. */

Intersect the allocation mask with the current cpuset, preserving
MPOL_BIND and MPOL_PREFERRED_MANY fallback rules. Retry only failed
allocations when the cpuset changes.

Fixes: eb02f14c4a2b ("mm/hugetlb: allow overcommitting gigantic hugepages")
Cc: stable@xxxxxxxxxxxxxxx
Reported-by: Lance Yang <lance.yang@xxxxxxxxx>
Signed-off-by: Qi Zheng <zhengqi.arch@xxxxxxxxxxxxx>
---
mm/hugetlb.c | 25 ++++++++++++++++++-------
1 file changed, 18 insertions(+), 7 deletions(-)

diff --git a/mm/hugetlb.c b/mm/hugetlb.c
index 6fba92b0f6749..09b64f7c11dd2 100644
--- a/mm/hugetlb.c
+++ b/mm/hugetlb.c
@@ -2181,22 +2181,33 @@ struct folio *alloc_buddy_hugetlb_folio(struct hstate *h,
gfp_t gfp_mask, struct mempolicy_interpreted *mpoli)
{
struct folio *folio = NULL;
- nodemask_t *nodemask = mpoli->nodemask;
+ nodemask_t alloc_nodemask;
+ unsigned int cpuset_mems_cookie;
+
+retry:
+ cpuset_mems_cookie = read_mems_allowed_begin();
+ alloc_nodemask = cpuset_current_mems_allowed;
+ if (mpoli->mode == MPOL_BIND && mpoli->nodemask &&
+ nodes_intersects(*mpoli->nodemask, alloc_nodemask))
+ nodes_and(alloc_nodemask, alloc_nodemask, *mpoli->nodemask);

if (mpoli->mode == MPOL_PREFERRED_MANY) {
gfp_t gfp = gfp_mask & ~(__GFP_DIRECT_RECLAIM | __GFP_NOFAIL);

+ nodes_and(alloc_nodemask, alloc_nodemask, *mpoli->nodemask);
folio = alloc_surplus_hugetlb_folio(h, gfp, mpoli->nid,
- nodemask);
+ &alloc_nodemask);

- /* Fallback to all nodes if page==NULL */
- nodemask = NULL;
+ /* Fallback to all nodes allowed by the current cpuset */
+ alloc_nodemask = cpuset_current_mems_allowed;
}

- if (!folio) {
+ if (!folio)
folio = alloc_surplus_hugetlb_folio(h, gfp_mask, mpoli->nid,
- nodemask);
- }
+ &alloc_nodemask);
+
+ if (!folio && unlikely(read_mems_allowed_retry(cpuset_mems_cookie)))
+ goto retry;

return folio;
}
--
2.54.0