All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/4] mm: hugetlb: fix some CMA-related issues
@ 2026-11-03  8:01 Qi Zheng
  2026-11-03  8:01 ` [PATCH 1/4] x86: hugetlb: disable default CMA reservations on 32-bit kernel Qi Zheng
                   ` (3 more replies)
  0 siblings, 4 replies; 18+ messages in thread
From: Qi Zheng @ 2026-11-03  8:01 UTC (permalink / raw)
  To: muchun.song, osalvador, david, akpm, azpijr
  Cc: linux-kernel, linux-mm, Qi Zheng

From: Qi Zheng <zhengqi.arch@bytedance.com>

Hi all,

This series fixes four CMA-related issues in HugeTLB reservation and
allocation:

 - Avoid ineffective CMA reservations on 32-bit x86 kernels running on
   CPUs that advertise 1 GiB page support.
 - Honor PF_MEMALLOC_PIN to avoid allocating CMA-backed folios during
   long-term pinning.
 - Skip CMA allocation when the caller disallows direct reclaim.
 - Enforce cpuset restrictions on surplus allocations, preserving memory
   policy semantics and handling concurrent cpuset changes without
   discarding folios that remain eligible.

Thanks,
Qi

Qi Zheng (4):
  x86: hugetlb: disable default CMA reservations on 32-bit kernel
  mm: hugetlb: skip CMA allocations under PF_MEMALLOC_PIN
  mm: hugetlb: skip CMA when direct reclaim is disallowed
  mm: hugetlb: respect cpuset restrictions for surplus allocations

 arch/x86/mm/hugetlbpage.c |  2 +-
 mm/hugetlb.c              | 38 +++++++++++++++++++++++++++++++-------
 mm/hugetlb_cma.c          |  5 ++++-
 3 files changed, 36 insertions(+), 9 deletions(-)


base-commit: 2637bdec0535a2044cd830eba9a79363651d9043
-- 
2.54.0



^ permalink raw reply	[flat|nested] 18+ messages in thread

* [PATCH 1/4] x86: hugetlb: disable default CMA reservations on 32-bit kernel
  2026-11-03  8:01 [PATCH 0/4] mm: hugetlb: fix some CMA-related issues Qi Zheng
@ 2026-11-03  8:01 ` Qi Zheng
  2026-11-03  8:41   ` Muchun Song
  2026-11-03 11:40   ` Jose A. Perez de Azpillaga
  2026-11-03  8:01 ` [PATCH 2/4] mm: hugetlb: skip CMA allocations under PF_MEMALLOC_PIN Qi Zheng
                   ` (2 subsequent siblings)
  3 siblings, 2 replies; 18+ messages in thread
From: Qi Zheng @ 2026-11-03  8:01 UTC (permalink / raw)
  To: muchun.song, osalvador, david, akpm, azpijr
  Cc: linux-kernel, linux-mm, Qi Zheng, stable, x86

From: Qi Zheng <zhengqi.arch@bytedance.com>

A 32-bit x86 kernel can run on a 64-bit-capable CPU advertising GBPAGES.
arch_hugetlb_cma_order() checks only this feature and returns a nonzero
order, although gigantic HugeTLB support on x86 requires CONFIG_X86_64.
Specifying hugetlb_cma= can therefore reserve a CMA area that cannot
serve the intended gigantic HugeTLB allocations.

On non-PAE kernels, the folded PUD_SHIFT yields a 4 MiB order, which is
equal to MAX_PAGE_ORDER. With CONFIG_DEBUG_VM enabled, this triggers the
VM_WARN_ON(order <= MAX_PAGE_ORDER) check in hugetlb_cma_reserve(),
warning that hugetlb_cma is attempting to reserve a non-gigantic order.

Require CONFIG_X86_64 as well as GBPAGES when selecting the default CMA
order to avoid these ineffective reservations.

Fixes: cf11e85fc08c ("mm: hugetlb: optionally allocate gigantic hugepages using cma")
Cc: stable@vger.kernel.org
Cc: x86@kernel.org
Signed-off-by: Qi Zheng <zhengqi.arch@bytedance.com>
---
 arch/x86/mm/hugetlbpage.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/arch/x86/mm/hugetlbpage.c b/arch/x86/mm/hugetlbpage.c
index 3b26621c91286..70726a99ede7b 100644
--- a/arch/x86/mm/hugetlbpage.c
+++ b/arch/x86/mm/hugetlbpage.c
@@ -45,7 +45,7 @@ arch_initcall(gigantic_pages_init);
 
 unsigned int __init arch_hugetlb_cma_order(void)
 {
-	if (boot_cpu_has(X86_FEATURE_GBPAGES))
+	if (IS_ENABLED(CONFIG_X86_64) && boot_cpu_has(X86_FEATURE_GBPAGES))
 		return PUD_SHIFT - PAGE_SHIFT;
 
 	return 0;
-- 
2.54.0



^ permalink raw reply related	[flat|nested] 18+ messages in thread

* [PATCH 2/4] mm: hugetlb: skip CMA allocations under PF_MEMALLOC_PIN
  2026-11-03  8:01 [PATCH 0/4] mm: hugetlb: fix some CMA-related issues Qi Zheng
  2026-11-03  8:01 ` [PATCH 1/4] x86: hugetlb: disable default CMA reservations on 32-bit kernel Qi Zheng
@ 2026-11-03  8:01 ` Qi Zheng
  2026-11-03  8:44   ` Muchun Song
  2026-11-03 10:56   ` Muchun Song
  2026-11-03  8:01 ` [PATCH 3/4] mm: hugetlb: skip CMA when direct reclaim is disallowed Qi Zheng
  2026-11-03  8:01 ` [PATCH 4/4] mm: hugetlb: respect cpuset restrictions for surplus allocations Qi Zheng
  3 siblings, 2 replies; 18+ messages in thread
From: Qi Zheng @ 2026-11-03  8:01 UTC (permalink / raw)
  To: muchun.song, osalvador, david, akpm, azpijr
  Cc: linux-kernel, linux-mm, Qi Zheng, stable

From: Qi Zheng <zhengqi.arch@bytedance.com>

With gigantic overcommit enabled, a fault during long-term pinning
can allocate a fresh HugeTLB folio from CMA despite PF_MEMALLOC_PIN.

The folio must then be migrated before it can be pinned. However,
fresh gigantic migration targets are not supported, so pinning fails
when the HugeTLB pool has no suitable target, even if the original
fault could have allocated pinnable memory outside CMA.

Honor PF_MEMALLOC_PIN in the HugeTLB CMA allocator so the fault can
try the non-CMA fallback, or fail when allocation is restricted to CMA.

Fixes: eb02f14c4a2b ("mm/hugetlb: allow overcommitting gigantic hugepages")
Cc: stable@vger.kernel.org
Signed-off-by: Qi Zheng <zhengqi.arch@bytedance.com>
---
 mm/hugetlb_cma.c | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/mm/hugetlb_cma.c b/mm/hugetlb_cma.c
index 95fd2d190f0d2..dde1c866406fb 100644
--- a/mm/hugetlb_cma.c
+++ b/mm/hugetlb_cma.c
@@ -62,6 +62,9 @@ struct folio *hugetlb_cma_alloc_frozen_folio(int order, gfp_t gfp_mask,
 	if (!hugetlb_cma_size)
 		return NULL;
 
+	if (current->flags & PF_MEMALLOC_PIN)
+		return NULL;
+
 retry_cpuset:
 	if (!nodemask) {
 		cpuset_mems_cookie = read_mems_allowed_begin();
-- 
2.54.0



^ permalink raw reply related	[flat|nested] 18+ messages in thread

* [PATCH 3/4] mm: hugetlb: skip CMA when direct reclaim is disallowed
  2026-11-03  8:01 [PATCH 0/4] mm: hugetlb: fix some CMA-related issues Qi Zheng
  2026-11-03  8:01 ` [PATCH 1/4] x86: hugetlb: disable default CMA reservations on 32-bit kernel Qi Zheng
  2026-11-03  8:01 ` [PATCH 2/4] mm: hugetlb: skip CMA allocations under PF_MEMALLOC_PIN Qi Zheng
@ 2026-11-03  8:01 ` Qi Zheng
  2026-11-03  8:47   ` sashiko-bot
  2026-11-03  8:01 ` [PATCH 4/4] mm: hugetlb: respect cpuset restrictions for surplus allocations Qi Zheng
  3 siblings, 1 reply; 18+ messages in thread
From: Qi Zheng @ 2026-11-03  8:01 UTC (permalink / raw)
  To: muchun.song, osalvador, david, akpm, azpijr
  Cc: linux-kernel, linux-mm, Qi Zheng, stable

From: Qi Zheng <zhengqi.arch@bytedance.com>

Switching HugeTLB CMA allocation from cma_alloc_folio() to
cma_alloc_frozen_compound() stopped forwarding the caller's GFP mask.
The new helper uses GFP_KERNEL, re-enabling direct reclaim even when
the caller explicitly disabled it.

The first MPOL_PREFERRED_MANY allocation attempt clears
__GFP_DIRECT_RECLAIM to try the preferred nodes before falling back to
other nodes. For gigantic surplus pages, the CMA path can therefore
enter direct reclaim while allocating migration targets during this
attempt.

Skip HugeTLB CMA allocation when direct reclaim is disallowed.

Fixes: 9bda131c6093 ("mm: cma: add cma_alloc_frozen{_compound}()")
Cc: stable@vger.kernel.org
Signed-off-by: Qi Zheng <zhengqi.arch@bytedance.com>
---
 mm/hugetlb_cma.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/mm/hugetlb_cma.c b/mm/hugetlb_cma.c
index dde1c866406fb..90f8d718a1f16 100644
--- a/mm/hugetlb_cma.c
+++ b/mm/hugetlb_cma.c
@@ -59,7 +59,7 @@ struct folio *hugetlb_cma_alloc_frozen_folio(int order, gfp_t gfp_mask,
 	const nodemask_t *nmask;
 	unsigned int cpuset_mems_cookie;
 
-	if (!hugetlb_cma_size)
+	if (!hugetlb_cma_size || !gfpflags_allow_blocking(gfp_mask))
 		return NULL;
 
 	if (current->flags & PF_MEMALLOC_PIN)
-- 
2.54.0



^ permalink raw reply related	[flat|nested] 18+ messages in thread

* [PATCH 4/4] mm: hugetlb: respect cpuset restrictions for surplus allocations
  2026-11-03  8:01 [PATCH 0/4] mm: hugetlb: fix some CMA-related issues Qi Zheng
                   ` (2 preceding siblings ...)
  2026-11-03  8:01 ` [PATCH 3/4] mm: hugetlb: skip CMA when direct reclaim is disallowed Qi Zheng
@ 2026-11-03  8:01 ` Qi Zheng
  2026-11-03 12:21   ` Jose A. Perez de Azpillaga
  3 siblings, 1 reply; 18+ messages in thread
From: Qi Zheng @ 2026-11-03  8:01 UTC (permalink / raw)
  To: muchun.song, osalvador, david, akpm, azpijr
  Cc: linux-kernel, linux-mm, Qi Zheng, stable, Lance Yang

From: Qi Zheng <zhengqi.arch@bytedance.com>

Gigantic surplus allocations can escape the faulting task's cpuset.
CMA and the contiguous allocator trust the supplied nodemask without
checking the current cpuset.

For example, a shared HugeTLB VMA has MPOL_BIND={0,1}, and T is a
non-leader thread. With surplus allocation enabled and no eligible free
HugeTLB folio, the following sequence can occur:

  CPU0 (cgroup v1 migration)       CPU1 (thread T)
  -------------------------       ---------------
  cpuset_attach_task(..., T)
    T->mems_allowed = {0}
    /* Non-leader: skip
       mpol_rebind_mm(). */
                                  /* Fault; VMA policy is still {0,1}. */
                                  policy_nodemask()
                                    /* {0,1} intersects {0}. */
                                    nodemask = {0,1}
                                  hugetlb_cma_alloc_frozen_folio()
                                    /* node0 fails; node1 succeeds. */

The 1 GiB folio is allocated on node1 despite T being restricted to node0.

Intersect the interpreted policy with the current cpuset before surplus
allocation, preserving MPOL_BIND and MPOL_PREFERRED_MANY fallback rules.
Apply the restriction at the policy-aware entry point to preserve
per-node pool management semantics.

If the cpuset changes during allocation, rebuild the mask and revalidate
any allocated folio against the current constraints. Keep it if it is
still eligible: freeing an HVO-optimized folio can defer returning its
range to CMA, so discarding a usable folio can cause a needless allocation
failure. Only release the folio and allocate again if it is no longer
eligible.

Fixes: eb02f14c4a2b ("mm/hugetlb: allow overcommitting gigantic hugepages")
Cc: stable@vger.kernel.org
Reported-by: Lance Yang <lance.yang@linux.dev>
Signed-off-by: Qi Zheng <zhengqi.arch@bytedance.com>
---
 mm/hugetlb.c | 38 +++++++++++++++++++++++++++++++-------
 1 file changed, 31 insertions(+), 7 deletions(-)

diff --git a/mm/hugetlb.c b/mm/hugetlb.c
index ce9c8e57b8e5c..6fd6e58ace869 100644
--- a/mm/hugetlb.c
+++ b/mm/hugetlb.c
@@ -2181,22 +2181,46 @@ 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 (folio) {
+		if (unlikely(read_mems_allowed_retry(cpuset_mems_cookie)))
+			goto retry;
+
+		if (node_isset(folio_nid(folio), alloc_nodemask))
+			return folio;
+
+		/* Balance free_huge_folio()'s decrement before caller accounting */
+		lruvec_stat_mod_folio(folio, NR_HUGETLB, pages_per_huge_page(h));
+		free_huge_folio(folio);
+		folio = NULL;
+	}
 
 	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 (unlikely(read_mems_allowed_retry(cpuset_mems_cookie)))
+		goto retry;
 
 	return folio;
 }
-- 
2.54.0



^ permalink raw reply related	[flat|nested] 18+ messages in thread

* Re: [PATCH 1/4] x86: hugetlb: disable default CMA reservations on 32-bit kernel
  2026-11-03  8:01 ` [PATCH 1/4] x86: hugetlb: disable default CMA reservations on 32-bit kernel Qi Zheng
@ 2026-11-03  8:41   ` Muchun Song
  2026-11-03 11:40   ` Jose A. Perez de Azpillaga
  1 sibling, 0 replies; 18+ messages in thread
From: Muchun Song @ 2026-11-03  8:41 UTC (permalink / raw)
  To: Qi Zheng
  Cc: osalvador, david, akpm, azpijr, linux-kernel, linux-mm, Qi Zheng,
	stable, x86



> On Nov 3, 2026, at 16:01, Qi Zheng <qi.zheng@linux.dev> wrote:
> 
> From: Qi Zheng <zhengqi.arch@bytedance.com>
> 
> A 32-bit x86 kernel can run on a 64-bit-capable CPU advertising GBPAGES.

I don't think anyone would do that. That said, it is genuinely an issue.

> arch_hugetlb_cma_order() checks only this feature and returns a nonzero
> order, although gigantic HugeTLB support on x86 requires CONFIG_X86_64.
> Specifying hugetlb_cma= can therefore reserve a CMA area that cannot
> serve the intended gigantic HugeTLB allocations.
> 
> On non-PAE kernels, the folded PUD_SHIFT yields a 4 MiB order, which is
> equal to MAX_PAGE_ORDER. With CONFIG_DEBUG_VM enabled, this triggers the
> VM_WARN_ON(order <= MAX_PAGE_ORDER) check in hugetlb_cma_reserve(),
> warning that hugetlb_cma is attempting to reserve a non-gigantic order.
> 
> Require CONFIG_X86_64 as well as GBPAGES when selecting the default CMA
> order to avoid these ineffective reservations.
> 
> Fixes: cf11e85fc08c ("mm: hugetlb: optionally allocate gigantic hugepages using cma")
> Cc: stable@vger.kernel.org
> Cc: x86@kernel.org
> Signed-off-by: Qi Zheng <zhengqi.arch@bytedance.com>

Acked-by: Muchun Song <muchun.song@linux.dev>

Thanks.



^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH 2/4] mm: hugetlb: skip CMA allocations under PF_MEMALLOC_PIN
  2026-11-03  8:01 ` [PATCH 2/4] mm: hugetlb: skip CMA allocations under PF_MEMALLOC_PIN Qi Zheng
@ 2026-11-03  8:44   ` Muchun Song
  2026-11-03 10:56   ` Muchun Song
  1 sibling, 0 replies; 18+ messages in thread
From: Muchun Song @ 2026-11-03  8:44 UTC (permalink / raw)
  To: Qi Zheng
  Cc: osalvador, david, akpm, azpijr, linux-kernel, linux-mm, Qi Zheng,
	stable



> On Nov 3, 2026, at 16:01, Qi Zheng <qi.zheng@linux.dev> wrote:
> 
> From: Qi Zheng <zhengqi.arch@bytedance.com>
> 
> With gigantic overcommit enabled, a fault during long-term pinning
> can allocate a fresh HugeTLB folio from CMA despite PF_MEMALLOC_PIN.
> 
> The folio must then be migrated before it can be pinned. However,
> fresh gigantic migration targets are not supported, so pinning fails
> when the HugeTLB pool has no suitable target, even if the original
> fault could have allocated pinnable memory outside CMA.
> 
> Honor PF_MEMALLOC_PIN in the HugeTLB CMA allocator so the fault can
> try the non-CMA fallback, or fail when allocation is restricted to CMA.
> 
> Fixes: eb02f14c4a2b ("mm/hugetlb: allow overcommitting gigantic hugepages")
> Cc: stable@vger.kernel.org
> Signed-off-by: Qi Zheng <zhengqi.arch@bytedance.com>

LGTM.

Acked-by: Muchun Song <muchun.song@linux.dev>

Thanks.



^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH 3/4] mm: hugetlb: skip CMA when direct reclaim is disallowed
  2026-11-03  8:01 ` [PATCH 3/4] mm: hugetlb: skip CMA when direct reclaim is disallowed Qi Zheng
@ 2026-11-03  8:47   ` sashiko-bot
  2026-11-03  9:11     ` Qi Zheng
  0 siblings, 1 reply; 18+ messages in thread
From: sashiko-bot @ 2026-11-03  8:47 UTC (permalink / raw)
  To: Qi Zheng, muchun.song, osalvador, david, akpm, azpijr; +Cc: linux-mm, Qi Zheng

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] mm: hugetlb: hugetlb_cma_alloc_frozen_folio() breaks MPOL_PREFERRED_MANY
--

commit c96d52d66d643a77abaa96a17e392b7ab0f36ccd
Author: Qi Zheng <qi.zheng@linux.dev>

mm: hugetlb: skip CMA when direct reclaim is disallowed

This commit updates hugetlb_cma_alloc_frozen_folio() to return NULL when
gfpflags_allow_blocking(gfp_mask) is false, avoiding direct reclaim inside
cma_alloc_frozen_compound() when __GFP_DIRECT_RECLAIM was cleared by the
caller.

> diff --git a/mm/hugetlb_cma.c b/mm/hugetlb_cma.c
> index dde1c866406fb..90f8d718a1f16 100644
> --- a/mm/hugetlb_cma.c
> +++ b/mm/hugetlb_cma.c
> @@ -59,7 +59,7 @@ struct folio *hugetlb_cma_alloc_frozen_folio(int order, gfp_t gfp_mask,
>  	const nodemask_t *nmask;
>  	unsigned int cpuset_mems_cookie;
>  
> -	if (!hugetlb_cma_size)
> +	if (!hugetlb_cma_size || !gfpflags_allow_blocking(gfp_mask))
>  		return NULL;

[Severity: Medium]
Does returning NULL from hugetlb_cma_alloc_frozen_folio() whenever
!gfpflags_allow_blocking(gfp_mask) break MPOL_PREFERRED_MANY for gigantic
surplus HugeTLB allocations?

In mm/hugetlb.c, alloc_buddy_hugetlb_folio() implements MPOL_PREFERRED_MANY
as a two-pass allocation:

mm/hugetlb.c:alloc_buddy_hugetlb_folio() {
	if (mpoli->mode == MPOL_PREFERRED_MANY) {
		gfp_t gfp = gfp_mask & ~(__GFP_DIRECT_RECLAIM | __GFP_NOFAIL);

		folio = alloc_surplus_hugetlb_folio(h, gfp, mpoli->nid,
						    nodemask);

		/* Fallback to all nodes if page==NULL */
		nodemask = NULL;
	}

	if (!folio) {
		folio = alloc_surplus_hugetlb_folio(h, gfp_mask, mpoli->nid,
						    nodemask);
	}
}

This reaches hugetlb_cma_alloc_frozen_folio() via:

  alloc_buddy_hugetlb_folio()
    -> alloc_surplus_hugetlb_folio()
      -> alloc_fresh_hugetlb_folio()
        -> only_alloc_fresh_hugetlb_folio()
          -> alloc_gigantic_frozen_folio()
            -> hugetlb_cma_alloc_frozen_folio()

On the first pass, __GFP_DIRECT_RECLAIM is cleared while nodemask points to
the preferred nodes (pol->nodes), and on the second pass nodemask is cleared
to NULL. Because hugetlb_cma_alloc_frozen_folio() now returns NULL on the
first pass, it never sees the MPOL_PREFERRED_MANY nodemask.

In mm/hugetlb.c:alloc_gigantic_frozen_folio():

	folio = hugetlb_cma_alloc_frozen_folio(order, gfp_mask, nid, nodemask);
	if (folio)
		return folio;

	if (hugetlb_cma_exclusive_alloc())
		return NULL;

	folio = (struct folio *)alloc_contig_frozen_pages(1 << order, gfp_mask,
							  nid, nodemask);

When hugetlb_cma_exclusive_alloc() is false, returning NULL from
hugetlb_cma_alloc_frozen_folio() on the first pass falls through to
alloc_contig_frozen_pages() on the preferred nodes, bypassing the dedicated
hugetlb_cma pool on the preferred nodes in favor of non-CMA memory.

When hugetlb_cma_exclusive_alloc() is true (or when non-CMA allocation fails
on the preferred nodes), the first pass returns NULL and the second pass
invokes hugetlb_cma_alloc_frozen_folio() with nodemask set to NULL. Since
mm/mempolicy.c:policy_nodemask() leaves *nid as numa_node_id() when
pol->home_node is NUMA_NO_NODE:

mm/mempolicy.c:policy_nodemask() {
	case MPOL_PREFERRED_MANY:
		nodemask = &pol->nodes;
		if (pol->home_node != NUMA_NO_NODE)
			*nid = pol->home_node;
		break;
}

hugetlb_cma_alloc_frozen_folio() on the second pass tries
hugetlb_cma[numa_node_id()] and then iterates cpuset_current_mems_allowed
from node 0, allocating from non-preferred CMA nodes even when preferred
nodes in pol->nodes have free CMA pages.

Prior to commit 9bda131c6093 ("mm: cma: add cma_alloc_frozen{_compound}()"),
cma_alloc_folio() forwarded gfp_mask down to alloc_contig_frozen_range(),
where __alloc_contig_verify_gfp_mask() preserved the cleared
__GFP_DIRECT_RECLAIM bit so CMA on the preferred nodemask was tried on the
first pass without entering direct reclaim. Would forwarding gfp_mask to
cma_alloc_frozen_compound() (in mm/cma.c) preserve both the non-reclaiming
first pass and the MPOL_PREFERRED_MANY nodemask?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/cover.1793692578.git.zhengqi.arch@bytedance.com?part=3


^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH 3/4] mm: hugetlb: skip CMA when direct reclaim is disallowed
  2026-11-03  8:47   ` sashiko-bot
@ 2026-11-03  9:11     ` Qi Zheng
  0 siblings, 0 replies; 18+ messages in thread
From: Qi Zheng @ 2026-11-03  9:11 UTC (permalink / raw)
  To: sashiko-reviews, muchun.song, osalvador, david, akpm, azpijr
  Cc: linux-mm, Qi Zheng



On 11/3/26 4:47 PM, sashiko-bot@kernel.org wrote:
> Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
> - [Medium] mm: hugetlb: hugetlb_cma_alloc_frozen_folio() breaks MPOL_PREFERRED_MANY
> --
> 
> commit c96d52d66d643a77abaa96a17e392b7ab0f36ccd
> Author: Qi Zheng <qi.zheng@linux.dev>
> 
> mm: hugetlb: skip CMA when direct reclaim is disallowed
> 
> This commit updates hugetlb_cma_alloc_frozen_folio() to return NULL when
> gfpflags_allow_blocking(gfp_mask) is false, avoiding direct reclaim inside
> cma_alloc_frozen_compound() when __GFP_DIRECT_RECLAIM was cleared by the
> caller.
> 
>> diff --git a/mm/hugetlb_cma.c b/mm/hugetlb_cma.c
>> index dde1c866406fb..90f8d718a1f16 100644
>> --- a/mm/hugetlb_cma.c
>> +++ b/mm/hugetlb_cma.c
>> @@ -59,7 +59,7 @@ struct folio *hugetlb_cma_alloc_frozen_folio(int order, gfp_t gfp_mask,
>>   	const nodemask_t *nmask;
>>   	unsigned int cpuset_mems_cookie;
>>   
>> -	if (!hugetlb_cma_size)
>> +	if (!hugetlb_cma_size || !gfpflags_allow_blocking(gfp_mask))
>>   		return NULL;
> 
> [Severity: Medium]
> Does returning NULL from hugetlb_cma_alloc_frozen_folio() whenever
> !gfpflags_allow_blocking(gfp_mask) break MPOL_PREFERRED_MANY for gigantic
> surplus HugeTLB allocations?
> 
> In mm/hugetlb.c, alloc_buddy_hugetlb_folio() implements MPOL_PREFERRED_MANY
> as a two-pass allocation:
> 
> mm/hugetlb.c:alloc_buddy_hugetlb_folio() {
> 	if (mpoli->mode == MPOL_PREFERRED_MANY) {
> 		gfp_t gfp = gfp_mask & ~(__GFP_DIRECT_RECLAIM | __GFP_NOFAIL);
> 
> 		folio = alloc_surplus_hugetlb_folio(h, gfp, mpoli->nid,
> 						    nodemask);
> 
> 		/* Fallback to all nodes if page==NULL */
> 		nodemask = NULL;
> 	}
> 
> 	if (!folio) {
> 		folio = alloc_surplus_hugetlb_folio(h, gfp_mask, mpoli->nid,
> 						    nodemask);
> 	}
> }
> 
> This reaches hugetlb_cma_alloc_frozen_folio() via:
> 
>    alloc_buddy_hugetlb_folio()
>      -> alloc_surplus_hugetlb_folio()
>        -> alloc_fresh_hugetlb_folio()
>          -> only_alloc_fresh_hugetlb_folio()
>            -> alloc_gigantic_frozen_folio()
>              -> hugetlb_cma_alloc_frozen_folio()
> 
> On the first pass, __GFP_DIRECT_RECLAIM is cleared while nodemask points to
> the preferred nodes (pol->nodes), and on the second pass nodemask is cleared
> to NULL. Because hugetlb_cma_alloc_frozen_folio() now returns NULL on the
> first pass, it never sees the MPOL_PREFERRED_MANY nodemask.
> 
> In mm/hugetlb.c:alloc_gigantic_frozen_folio():
> 
> 	folio = hugetlb_cma_alloc_frozen_folio(order, gfp_mask, nid, nodemask);
> 	if (folio)
> 		return folio;
> 
> 	if (hugetlb_cma_exclusive_alloc())
> 		return NULL;
> 
> 	folio = (struct folio *)alloc_contig_frozen_pages(1 << order, gfp_mask,
> 							  nid, nodemask);
> 
> When hugetlb_cma_exclusive_alloc() is false, returning NULL from
> hugetlb_cma_alloc_frozen_folio() on the first pass falls through to
> alloc_contig_frozen_pages() on the preferred nodes, bypassing the dedicated
> hugetlb_cma pool on the preferred nodes in favor of non-CMA memory.
> 
> When hugetlb_cma_exclusive_alloc() is true (or when non-CMA allocation fails
> on the preferred nodes), the first pass returns NULL and the second pass
> invokes hugetlb_cma_alloc_frozen_folio() with nodemask set to NULL. Since
> mm/mempolicy.c:policy_nodemask() leaves *nid as numa_node_id() when
> pol->home_node is NUMA_NO_NODE:
> 
> mm/mempolicy.c:policy_nodemask() {
> 	case MPOL_PREFERRED_MANY:
> 		nodemask = &pol->nodes;
> 		if (pol->home_node != NUMA_NO_NODE)
> 			*nid = pol->home_node;
> 		break;
> }
> 
> hugetlb_cma_alloc_frozen_folio() on the second pass tries
> hugetlb_cma[numa_node_id()] and then iterates cpuset_current_mems_allowed
> from node 0, allocating from non-preferred CMA nodes even when preferred
> nodes in pol->nodes have free CMA pages.
> 
> Prior to commit 9bda131c6093 ("mm: cma: add cma_alloc_frozen{_compound}()"),
> cma_alloc_folio() forwarded gfp_mask down to alloc_contig_frozen_range(),
> where __alloc_contig_verify_gfp_mask() preserved the cleared
> __GFP_DIRECT_RECLAIM bit so CMA on the preferred nodemask was tried on the
> first pass without entering direct reclaim. Would forwarding gfp_mask to

Thanks for catching this. You're right: in CMA-only mode, this check
skips the preferred-node attempt entirely. The fallback can then
allocate from a non-preferred node even when the preferred nodes have
usable CMA memory.

> cma_alloc_frozen_compound() (in mm/cma.c) preserve both the non-reclaiming
> first pass and the MPOL_PREFERRED_MANY nodemask?

Make sense.

I'll replace the check with GFP-mask propagation through
cma_alloc_frozen_compound(). This restores the preferred-node CMA
attempt while preserving the caller's __GFP_DIRECT_RECLAIM setting.

Like the following:

diff --git a/include/linux/cma.h b/include/linux/cma.h
index 8555d38a97b1..caedd59bde03 100644
--- a/include/linux/cma.h
+++ b/include/linux/cma.h
@@ -53,7 +53,8 @@ extern bool cma_release(struct cma *cma, const struct 
page *pages, unsigned long

  struct page *cma_alloc_frozen(struct cma *cma, unsigned long count,
  		unsigned int align, bool no_warn);
-struct page *cma_alloc_frozen_compound(struct cma *cma, unsigned int 
order);
+struct page *cma_alloc_frozen_compound(struct cma *cma, unsigned int order,
+				       gfp_t gfp);
  bool cma_release_frozen(struct cma *cma, const struct page *pages,
  		unsigned long count);

diff --git a/mm/cma.c b/mm/cma.c
index a10ea37a261d..c2605967d77a 100644
--- a/mm/cma.c
+++ b/mm/cma.c
@@ -929,9 +929,10 @@ struct page *cma_alloc_frozen(struct cma *cma, 
unsigned long count,
  	return __cma_alloc_frozen(cma, count, align, gfp);
  }

-struct page *cma_alloc_frozen_compound(struct cma *cma, unsigned int order)
+struct page *cma_alloc_frozen_compound(struct cma *cma, unsigned int order,
+				       gfp_t gfp)
  {
-	gfp_t gfp = GFP_KERNEL | __GFP_COMP | __GFP_NOWARN;
+	gfp |= __GFP_COMP | __GFP_NOWARN;

  	return __cma_alloc_frozen(cma, 1 << order, order, gfp);
  }
diff --git a/mm/hugetlb_cma.c b/mm/hugetlb_cma.c
index 90f8d718a1f1..250b5c10d954 100644
--- a/mm/hugetlb_cma.c
+++ b/mm/hugetlb_cma.c
@@ -59,7 +59,7 @@ struct folio *hugetlb_cma_alloc_frozen_folio(int 
order, gfp_t gfp_mask,
  	const nodemask_t *nmask;
  	unsigned int cpuset_mems_cookie;

-	if (!hugetlb_cma_size || !gfpflags_allow_blocking(gfp_mask))
+	if (!hugetlb_cma_size)
  		return NULL;

  	if (current->flags & PF_MEMALLOC_PIN)
@@ -74,14 +74,16 @@ struct folio *hugetlb_cma_alloc_frozen_folio(int 
order, gfp_t gfp_mask,
  	}

  	if (hugetlb_cma[nid] && node_isset(nid, *nmask))
-		page = cma_alloc_frozen_compound(hugetlb_cma[nid], order);
+		page = cma_alloc_frozen_compound(hugetlb_cma[nid], order,
+						 gfp_mask);

  	if (!page && !(gfp_mask & __GFP_THISNODE)) {
  		for_each_node_mask(node, *nmask) {
  			if (node == nid || !hugetlb_cma[node])
  				continue;

-			page = cma_alloc_frozen_compound(hugetlb_cma[node], order);
+			page = cma_alloc_frozen_compound(hugetlb_cma[node], order,
+							 gfp_mask);
  			if (page)
  				break;
  		}

Thanks,
Qi

> 



^ permalink raw reply related	[flat|nested] 18+ messages in thread

* Re: [PATCH 2/4] mm: hugetlb: skip CMA allocations under PF_MEMALLOC_PIN
  2026-11-03  8:01 ` [PATCH 2/4] mm: hugetlb: skip CMA allocations under PF_MEMALLOC_PIN Qi Zheng
  2026-11-03  8:44   ` Muchun Song
@ 2026-11-03 10:56   ` Muchun Song
  2026-11-03 11:59     ` Jose A. Perez de Azpillaga
  2026-11-04  3:27     ` Qi Zheng
  1 sibling, 2 replies; 18+ messages in thread
From: Muchun Song @ 2026-11-03 10:56 UTC (permalink / raw)
  To: Qi Zheng
  Cc: osalvador, David Hildenbrand, akpm, azpijr, linux-kernel,
	linux-mm, Qi Zheng, stable



> On Nov 3, 2026, at 16:01, Qi Zheng <qi.zheng@linux.dev> wrote:
> 
> From: Qi Zheng <zhengqi.arch@bytedance.com>
> 
> With gigantic overcommit enabled, a fault during long-term pinning
> can allocate a fresh HugeTLB folio from CMA despite PF_MEMALLOC_PIN.
> 
> The folio must then be migrated before it can be pinned. However,
> fresh gigantic migration targets are not supported, so pinning fails
> when the HugeTLB pool has no suitable target, even if the original
> fault could have allocated pinnable memory outside CMA.
> 
> Honor PF_MEMALLOC_PIN in the HugeTLB CMA allocator so the fault can
> try the non-CMA fallback, or fail when allocation is restricted to CMA.

Wait... I think my Acked-by got ahead of me here. :)

There seems to be a rather odd distinction that GUP is missing.

The usual reason long-term GUP must migrate a page out of a
MIGRATE_CMA pageblock is that the page was only borrowed from CMA by the
normal page allocator.  The corresponding CMA bitmap range is still
available, so a future CMA allocation relies on being able to migrate
that page away.  A long-term pin would prevent that.

However, a HugeTLB folio returned by
hugetlb_cma_alloc_frozen_folio() is different.  It is the actual CMA
allocation, not merely a movable page that happens to reside in a
MIGRATE_CMA pageblock.  Its range has already been marked allocated in
the hugetlb CMA bitmap.

Therefore, CMA cannot allocate that range again until the HugeTLB folio
is freed anyway.  Long-term pinning the folio does not seem to make the
CMA area any less available than keeping the HugeTLB folio mapped for
the same amount of time.

folio_is_longterm_pinnable() currently only sees MIGRATE_CMA and does
not distinguish these two cases.  In that light, rejecting
PF_MEMALLOC_PIN in hugetlb_cma_alloc_frozen_folio() looks like working
around an overly broad GUP classification.  It also means that
hugetlb_cma_only users will fail the fault, while other users may consume
a gigantic contiguous range outside the dedicated hugetlb CMA area even
when that area has free space.

Would it make more sense to fix the long-term GUP check instead, so that
an explicitly CMA-allocated HugeTLB folio is considered pinnable, while
ordinary pages merely borrowed from MIGRATE_CMA pageblocks remain unpinnable?

Thanks,
Muchun

> 
> Fixes: eb02f14c4a2b ("mm/hugetlb: allow overcommitting gigantic hugepages")
> Cc: stable@vger.kernel.org
> Signed-off-by: Qi Zheng <zhengqi.arch@bytedance.com>
> ---
> mm/hugetlb_cma.c | 3 +++
> 1 file changed, 3 insertions(+)
> 
> diff --git a/mm/hugetlb_cma.c b/mm/hugetlb_cma.c
> index 95fd2d190f0d2..dde1c866406fb 100644
> --- a/mm/hugetlb_cma.c
> +++ b/mm/hugetlb_cma.c
> @@ -62,6 +62,9 @@ struct folio *hugetlb_cma_alloc_frozen_folio(int order, gfp_t gfp_mask,
> 	if (!hugetlb_cma_size)
> 		return NULL;
> 
> +	if (current->flags & PF_MEMALLOC_PIN)
> +		return NULL;
> +
> retry_cpuset:
> 	if (!nodemask) {
> 		cpuset_mems_cookie = read_mems_allowed_begin();
> -- 
> 2.54.0
> 



^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH 1/4] x86: hugetlb: disable default CMA reservations on 32-bit kernel
  2026-11-03  8:01 ` [PATCH 1/4] x86: hugetlb: disable default CMA reservations on 32-bit kernel Qi Zheng
  2026-11-03  8:41   ` Muchun Song
@ 2026-11-03 11:40   ` Jose A. Perez de Azpillaga
  1 sibling, 0 replies; 18+ messages in thread
From: Jose A. Perez de Azpillaga @ 2026-11-03 11:40 UTC (permalink / raw)
  To: Qi Zheng, muchun.song, osalvador, david, akpm
  Cc: linux-kernel, linux-mm, Qi Zheng, stable, x86

On 11/3/26 9:01 AM, Qi Zheng wrote:
> From: Qi Zheng <zhengqi.arch@bytedance.com>
> 
> A 32-bit x86 kernel can run on a 64-bit-capable CPU advertising GBPAGES.

weird. anyway, LGTM:

Reviewed-by: Jose A. Perez de Azpillaga <azpijr@gmail.com>

> arch_hugetlb_cma_order() checks only this feature and returns a nonzero
> order, although gigantic HugeTLB support on x86 requires CONFIG_X86_64.
> Specifying hugetlb_cma= can therefore reserve a CMA area that cannot
> serve the intended gigantic HugeTLB allocations.
> 
> On non-PAE kernels, the folded PUD_SHIFT yields a 4 MiB order, which is
> equal to MAX_PAGE_ORDER. With CONFIG_DEBUG_VM enabled, this triggers the
> VM_WARN_ON(order <= MAX_PAGE_ORDER) check in hugetlb_cma_reserve(),
> warning that hugetlb_cma is attempting to reserve a non-gigantic order.
> 
> Require CONFIG_X86_64 as well as GBPAGES when selecting the default CMA
> order to avoid these ineffective reservations.
> 
> Fixes: cf11e85fc08c ("mm: hugetlb: optionally allocate gigantic hugepages using cma")
> Cc: stable@vger.kernel.org
> Cc: x86@kernel.org
> Signed-off-by: Qi Zheng <zhengqi.arch@bytedance.com>
> ---
>  arch/x86/mm/hugetlbpage.c | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
> 
> diff --git a/arch/x86/mm/hugetlbpage.c b/arch/x86/mm/hugetlbpage.c
> index 3b26621c91286..70726a99ede7b 100644
> --- a/arch/x86/mm/hugetlbpage.c
> +++ b/arch/x86/mm/hugetlbpage.c
> @@ -45,7 +45,7 @@ arch_initcall(gigantic_pages_init);
>  
>  unsigned int __init arch_hugetlb_cma_order(void)
>  {
> -	if (boot_cpu_has(X86_FEATURE_GBPAGES))
> +	if (IS_ENABLED(CONFIG_X86_64) && boot_cpu_has(X86_FEATURE_GBPAGES))
>  		return PUD_SHIFT - PAGE_SHIFT;
>  
>  	return 0;


-- 
cheers,
jose a. p-a


^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH 2/4] mm: hugetlb: skip CMA allocations under PF_MEMALLOC_PIN
  2026-11-03 10:56   ` Muchun Song
@ 2026-11-03 11:59     ` Jose A. Perez de Azpillaga
  2026-11-04  3:27     ` Qi Zheng
  1 sibling, 0 replies; 18+ messages in thread
From: Jose A. Perez de Azpillaga @ 2026-11-03 11:59 UTC (permalink / raw)
  To: Muchun Song, Qi Zheng
  Cc: osalvador, David Hildenbrand, akpm, linux-kernel, linux-mm,
	Qi Zheng, stable

On 11/3/26 11:56 AM, Muchun Song wrote:
> 
> 
>> On Nov 3, 2026, at 16:01, Qi Zheng <qi.zheng@linux.dev> wrote:
>>
>> From: Qi Zheng <zhengqi.arch@bytedance.com>
>>
>> With gigantic overcommit enabled, a fault during long-term pinning
>> can allocate a fresh HugeTLB folio from CMA despite PF_MEMALLOC_PIN.
>>
>> The folio must then be migrated before it can be pinned. However,
>> fresh gigantic migration targets are not supported, so pinning fails
>> when the HugeTLB pool has no suitable target, even if the original
>> fault could have allocated pinnable memory outside CMA.
>>
>> Honor PF_MEMALLOC_PIN in the HugeTLB CMA allocator so the fault can
>> try the non-CMA fallback, or fail when allocation is restricted to CMA.
> 
> Wait... I think my Acked-by got ahead of me here. :)
> 
> There seems to be a rather odd distinction that GUP is missing.
> 
> The usual reason long-term GUP must migrate a page out of a
> MIGRATE_CMA pageblock is that the page was only borrowed from CMA by the
> normal page allocator.  The corresponding CMA bitmap range is still
> available, so a future CMA allocation relies on being able to migrate
> that page away.  A long-term pin would prevent that.
> 
> However, a HugeTLB folio returned by
> hugetlb_cma_alloc_frozen_folio() is different.  It is the actual CMA
> allocation, not merely a movable page that happens to reside in a
> MIGRATE_CMA pageblock.  Its range has already been marked allocated in
> the hugetlb CMA bitmap.
> 
> Therefore, CMA cannot allocate that range again until the HugeTLB folio
> is freed anyway.  Long-term pinning the folio does not seem to make the
> CMA area any less available than keeping the HugeTLB folio mapped for
> the same amount of time.
> 
> folio_is_longterm_pinnable() currently only sees MIGRATE_CMA and does
> not distinguish these two cases.  In that light, rejecting
> PF_MEMALLOC_PIN in hugetlb_cma_alloc_frozen_folio() looks like working
> around an overly broad GUP classification.  It also means that
> hugetlb_cma_only users will fail the fault, while other users may consume
> a gigantic contiguous range outside the dedicated hugetlb CMA area even
> when that area has free space.
> 
> Would it make more sense to fix the long-term GUP check instead, so that
> an explicitly CMA-allocated HugeTLB folio is considered pinnable, while
> ordinary pages merely borrowed from MIGRATE_CMA pageblocks remain unpinnable?
> 

makes sense to me. with hugetlb_cma_only=1 a 1G FOLL_LONGTERM pin can't
succeed at all today, even on preallocated pages.


-- 
cheers,
jose a. p-a


^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH 4/4] mm: hugetlb: respect cpuset restrictions for surplus allocations
  2026-11-03  8:01 ` [PATCH 4/4] mm: hugetlb: respect cpuset restrictions for surplus allocations Qi Zheng
@ 2026-11-03 12:21   ` Jose A. Perez de Azpillaga
  2026-11-04  3:39     ` Qi Zheng
  0 siblings, 1 reply; 18+ messages in thread
From: Jose A. Perez de Azpillaga @ 2026-11-03 12:21 UTC (permalink / raw)
  To: Qi Zheng, muchun.song, osalvador, david, akpm
  Cc: linux-kernel, linux-mm, Qi Zheng, stable, Lance Yang

On 11/3/26 9:01 AM, Qi Zheng wrote:
> From: Qi Zheng <zhengqi.arch@bytedance.com>
> 
> Gigantic surplus allocations can escape the faulting task's cpuset.
> CMA and the contiguous allocator trust the supplied nodemask without
> checking the current cpuset.
> 
> For example, a shared HugeTLB VMA has MPOL_BIND={0,1}, and T is a
> non-leader thread. With surplus allocation enabled and no eligible free
> HugeTLB folio, the following sequence can occur:
> 
>   CPU0 (cgroup v1 migration)       CPU1 (thread T)
>   -------------------------       ---------------
>   cpuset_attach_task(..., T)
>     T->mems_allowed = {0}
>     /* Non-leader: skip
>        mpol_rebind_mm(). */
>                                   /* Fault; VMA policy is still {0,1}. */
>                                   policy_nodemask()
>                                     /* {0,1} intersects {0}. */
>                                     nodemask = {0,1}
>                                   hugetlb_cma_alloc_frozen_folio()
>                                     /* node0 fails; node1 succeeds. */
> 
> The 1 GiB folio is allocated on node1 despite T being restricted to node0.
> 
> Intersect the interpreted policy with the current cpuset before surplus
> allocation, preserving MPOL_BIND and MPOL_PREFERRED_MANY fallback rules.
> Apply the restriction at the policy-aware entry point to preserve
> per-node pool management semantics.
> 
> If the cpuset changes during allocation, rebuild the mask and revalidate
> any allocated folio against the current constraints. Keep it if it is
> still eligible: freeing an HVO-optimized folio can defer returning its
> range to CMA, so discarding a usable folio can cause a needless allocation
> failure. Only release the folio and allocate again if it is no longer
> eligible.
> 
> Fixes: eb02f14c4a2b ("mm/hugetlb: allow overcommitting gigantic hugepages")
> Cc: stable@vger.kernel.org
> Reported-by: Lance Yang <lance.yang@linux.dev>
> Signed-off-by: Qi Zheng <zhengqi.arch@bytedance.com>
> ---
>  mm/hugetlb.c | 38 +++++++++++++++++++++++++++++++-------
>  1 file changed, 31 insertions(+), 7 deletions(-)
> 
> diff --git a/mm/hugetlb.c b/mm/hugetlb.c
> index ce9c8e57b8e5c..6fd6e58ace869 100644
> --- a/mm/hugetlb.c
> +++ b/mm/hugetlb.c
> @@ -2181,22 +2181,46 @@ 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 (folio) {
> +		if (unlikely(read_mems_allowed_retry(cpuset_mems_cookie)))
> +			goto retry;
> +
> +		if (node_isset(folio_nid(folio), alloc_nodemask))
> +			return folio;
> +
> +		/* Balance free_huge_folio()'s decrement before caller accounting */
> +		lruvec_stat_mod_folio(folio, NR_HUGETLB, pages_per_huge_page(h));
> +		free_huge_folio(folio);
> +		folio = NULL;
> +	}

the page allocator only retries when the allocation fails, why does this
need to drop a folio that raced with a cpuset change? retrying only on
!folio would make this whole block go away, with the check at the end as:

        if (!folio &&
            unlikely(read_mems_allowed_retry(cpuset_mems_cookie)))
                goto retry;

>  
>  	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 (unlikely(read_mems_allowed_retry(cpuset_mems_cookie)))
> +		goto retry;
>  
>  	return folio;
>  }


-- 
cheers,
jose a. p-a


^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH 2/4] mm: hugetlb: skip CMA allocations under PF_MEMALLOC_PIN
  2026-11-03 10:56   ` Muchun Song
  2026-11-03 11:59     ` Jose A. Perez de Azpillaga
@ 2026-11-04  3:27     ` Qi Zheng
  2026-11-04  6:51       ` Muchun Song
  2026-11-04  7:07       ` Muchun Song
  1 sibling, 2 replies; 18+ messages in thread
From: Qi Zheng @ 2026-11-04  3:27 UTC (permalink / raw)
  To: Muchun Song
  Cc: osalvador, David Hildenbrand, akpm, azpijr, linux-kernel,
	linux-mm, stable, Qi Zheng

Hi Muchun,

On 11/3/26 6:56 PM, Muchun Song wrote:
> 
> 
>> On Nov 3, 2026, at 16:01, Qi Zheng <qi.zheng@linux.dev> wrote:
>>
>> From: Qi Zheng <zhengqi.arch@bytedance.com>
>>
>> With gigantic overcommit enabled, a fault during long-term pinning
>> can allocate a fresh HugeTLB folio from CMA despite PF_MEMALLOC_PIN.
>>
>> The folio must then be migrated before it can be pinned. However,
>> fresh gigantic migration targets are not supported, so pinning fails
>> when the HugeTLB pool has no suitable target, even if the original
>> fault could have allocated pinnable memory outside CMA.
>>
>> Honor PF_MEMALLOC_PIN in the HugeTLB CMA allocator so the fault can
>> try the non-CMA fallback, or fail when allocation is restricted to CMA.
> 
> Wait... I think my Acked-by got ahead of me here. :)
> 
> There seems to be a rather odd distinction that GUP is missing.
> 
> The usual reason long-term GUP must migrate a page out of a
> MIGRATE_CMA pageblock is that the page was only borrowed from CMA by the
> normal page allocator.  The corresponding CMA bitmap range is still
> available, so a future CMA allocation relies on being able to migrate
> that page away.  A long-term pin would prevent that.
> 
> However, a HugeTLB folio returned by
> hugetlb_cma_alloc_frozen_folio() is different.  It is the actual CMA
> allocation, not merely a movable page that happens to reside in a
> MIGRATE_CMA pageblock.  Its range has already been marked allocated in
> the hugetlb CMA bitmap.
> 
> Therefore, CMA cannot allocate that range again until the HugeTLB folio
> is freed anyway.  Long-term pinning the folio does not seem to make the
> CMA area any less available than keeping the HugeTLB folio mapped for
> the same amount of time.
> 
> folio_is_longterm_pinnable() currently only sees MIGRATE_CMA and does
> not distinguish these two cases.  In that light, rejecting
> PF_MEMALLOC_PIN in hugetlb_cma_alloc_frozen_folio() looks like working
> around an overly broad GUP classification.  It also means that
> hugetlb_cma_only users will fail the fault, while other users may consume
> a gigantic contiguous range outside the dedicated hugetlb CMA area even
> when that area has free space.
> 
> Would it make more sense to fix the long-term GUP check instead, so that
> an explicitly CMA-allocated HugeTLB folio is considered pinnable, while
> ordinary pages merely borrowed from MIGRATE_CMA pageblocks remain unpinnable?

Make sense!

I'll replace this patch with an exception for HPG_cma folios in the
MIGRATE_CMA check. This also lets the HugeTLB allocator select preallocated
CMA folios during long-term pinning. MIGRATE_ISOLATE and ZONE_MOVABLE
remain restricted.

Fresh CMA allocations under PF_MEMALLOC_PIN also need to skip ranges
in ZONE_MOVABLE. Returning -EBUSY here lets CMA try its remaining ranges.

The incremental diff against this patch is below.

diff --git a/include/linux/cma.h b/include/linux/cma.h
index 8555d38a97b1..7fd1b6e65b58 100644
--- a/include/linux/cma.h
+++ b/include/linux/cma.h
@@ -21,11 +21,13 @@
  #define CMA_MIN_ALIGNMENT_BYTES (PAGE_SIZE * CMA_MIN_ALIGNMENT_PAGES)

  struct cma;
+struct folio;

  extern unsigned long totalcma_pages;
  extern phys_addr_t cma_get_base(const struct cma *cma);
  extern unsigned long cma_get_size(const struct cma *cma);
  extern const char *cma_get_name(const struct cma *cma);
+bool cma_folio_is_pinnable(struct folio *folio);

  extern int __init cma_declare_contiguous_nid(phys_addr_t base,
  			phys_addr_t size, phys_addr_t limit,
diff --git a/include/linux/mm.h b/include/linux/mm.h
index cb993a1d0728..d15810be7d96 100644
--- a/include/linux/mm.h
+++ b/include/linux/mm.h
@@ -3,6 +3,7 @@
  #define _LINUX_MM_H

  #include <linux/args.h>
+#include <linux/cma.h>
  #include <linux/errno.h>
  #include <linux/mmdebug.h>
  #include <linux/gfp.h>
@@ -2975,14 +2976,16 @@ static inline bool is_zero_folio(const struct 
folio *folio)
  	return is_zero_page(&folio->page);
  }

-/* MIGRATE_CMA and ZONE_MOVABLE do not allow pin folios */
+/* Borrowed CMA pages and ZONE_MOVABLE folios cannot be pinned long 
term. */
  #ifdef CONFIG_MIGRATION
  static inline bool folio_is_longterm_pinnable(struct folio *folio)
  {
  #ifdef CONFIG_CMA
  	int mt = folio_migratetype(folio);

-	if (mt == MIGRATE_CMA || mt == MIGRATE_ISOLATE)
+	if (mt == MIGRATE_ISOLATE)
+		return false;
+	if (mt == MIGRATE_CMA && !cma_folio_is_pinnable(folio))
  		return false;
  #endif
  	/* The zero page can be "pinned" but gets special handling. */
diff --git a/mm/cma.c b/mm/cma.c
index a10ea37a261d..c2814f54f86a 100644
--- a/mm/cma.c
+++ b/mm/cma.c
@@ -27,6 +27,7 @@
  #include <linux/log2.h>
  #include <linux/cma.h>
  #include <linux/highmem.h>
+#include <linux/hugetlb.h>
  #include <linux/io.h>
  #include <linux/kmemleak.h>
  #include <trace/events/cma.h>
@@ -55,6 +56,12 @@ const char *cma_get_name(const struct cma *cma)
  }
  EXPORT_SYMBOL_GPL(cma_get_name);

+bool cma_folio_is_pinnable(struct folio *folio)
+{
+	/* Explicit HugeTLB CMA allocations own their bitmap ranges. */
+	return folio_test_hugetlb(folio) && folio_test_hugetlb_cma(folio);
+}
+
  static unsigned long cma_bitmap_aligned_mask(const struct cma *cma,
  					     unsigned int align_order)
  {
@@ -792,6 +799,11 @@ static int cma_range_alloc(struct cma *cma, struct 
cma_memrange *cmr,
  	int ret = -EBUSY;
  	struct page *page = NULL;

+	/* Each range lies within one zone; try other ranges if needed. */
+	if ((current->flags & PF_MEMALLOC_PIN) &&
+	    is_zone_movable_page(pfn_to_page(cmr->base_pfn)))
+		return -EBUSY;
+
  	mask = cma_bitmap_aligned_mask(cma, align);
  	offset = cma_bitmap_aligned_offset(cma, cmr, align);
  	bitmap_maxno = cma_bitmap_maxno(cma, cmr);
diff --git a/mm/hugetlb_cma.c b/mm/hugetlb_cma.c
index dde1c866406f..95fd2d190f0d 100644
--- a/mm/hugetlb_cma.c
+++ b/mm/hugetlb_cma.c
@@ -62,9 +62,6 @@ struct folio *hugetlb_cma_alloc_frozen_folio(int 
order, gfp_t gfp_mask,
  	if (!hugetlb_cma_size)
  		return NULL;

-	if (current->flags & PF_MEMALLOC_PIN)
-		return NULL;
-
  retry_cpuset:
  	if (!nodemask) {
  		cpuset_mems_cookie = read_mems_allowed_begin();

Thanks,
Qi

> 
> Thanks,
> Muchun
> 
>>
>> Fixes: eb02f14c4a2b ("mm/hugetlb: allow overcommitting gigantic hugepages")
>> Cc: stable@vger.kernel.org
>> Signed-off-by: Qi Zheng <zhengqi.arch@bytedance.com>
>> ---
>> mm/hugetlb_cma.c | 3 +++
>> 1 file changed, 3 insertions(+)
>>
>> diff --git a/mm/hugetlb_cma.c b/mm/hugetlb_cma.c
>> index 95fd2d190f0d2..dde1c866406fb 100644
>> --- a/mm/hugetlb_cma.c
>> +++ b/mm/hugetlb_cma.c
>> @@ -62,6 +62,9 @@ struct folio *hugetlb_cma_alloc_frozen_folio(int order, gfp_t gfp_mask,
>> 	if (!hugetlb_cma_size)
>> 		return NULL;
>>
>> +	if (current->flags & PF_MEMALLOC_PIN)
>> +		return NULL;
>> +
>> retry_cpuset:
>> 	if (!nodemask) {
>> 		cpuset_mems_cookie = read_mems_allowed_begin();
>> -- 
>> 2.54.0
>>
> 



^ permalink raw reply related	[flat|nested] 18+ messages in thread

* Re: [PATCH 4/4] mm: hugetlb: respect cpuset restrictions for surplus allocations
  2026-11-03 12:21   ` Jose A. Perez de Azpillaga
@ 2026-11-04  3:39     ` Qi Zheng
  0 siblings, 0 replies; 18+ messages in thread
From: Qi Zheng @ 2026-11-04  3:39 UTC (permalink / raw)
  To: Jose A. Perez de Azpillaga, muchun.song, osalvador, david, akpm
  Cc: linux-kernel, linux-mm, stable, Lance Yang, Qi Zheng



On 11/3/26 8:21 PM, Jose A. Perez de Azpillaga wrote:
> On 11/3/26 9:01 AM, Qi Zheng wrote:
>> From: Qi Zheng <zhengqi.arch@bytedance.com>
>>
>> Gigantic surplus allocations can escape the faulting task's cpuset.
>> CMA and the contiguous allocator trust the supplied nodemask without
>> checking the current cpuset.
>>
>> For example, a shared HugeTLB VMA has MPOL_BIND={0,1}, and T is a
>> non-leader thread. With surplus allocation enabled and no eligible free
>> HugeTLB folio, the following sequence can occur:
>>
>>    CPU0 (cgroup v1 migration)       CPU1 (thread T)
>>    -------------------------       ---------------
>>    cpuset_attach_task(..., T)
>>      T->mems_allowed = {0}
>>      /* Non-leader: skip
>>         mpol_rebind_mm(). */
>>                                    /* Fault; VMA policy is still {0,1}. */
>>                                    policy_nodemask()
>>                                      /* {0,1} intersects {0}. */
>>                                      nodemask = {0,1}
>>                                    hugetlb_cma_alloc_frozen_folio()
>>                                      /* node0 fails; node1 succeeds. */
>>
>> The 1 GiB folio is allocated on node1 despite T being restricted to node0.
>>
>> Intersect the interpreted policy with the current cpuset before surplus
>> allocation, preserving MPOL_BIND and MPOL_PREFERRED_MANY fallback rules.
>> Apply the restriction at the policy-aware entry point to preserve
>> per-node pool management semantics.
>>
>> If the cpuset changes during allocation, rebuild the mask and revalidate
>> any allocated folio against the current constraints. Keep it if it is
>> still eligible: freeing an HVO-optimized folio can defer returning its
>> range to CMA, so discarding a usable folio can cause a needless allocation
>> failure. Only release the folio and allocate again if it is no longer
>> eligible.
>>
>> Fixes: eb02f14c4a2b ("mm/hugetlb: allow overcommitting gigantic hugepages")
>> Cc: stable@vger.kernel.org
>> Reported-by: Lance Yang <lance.yang@linux.dev>
>> Signed-off-by: Qi Zheng <zhengqi.arch@bytedance.com>
>> ---
>>   mm/hugetlb.c | 38 +++++++++++++++++++++++++++++++-------
>>   1 file changed, 31 insertions(+), 7 deletions(-)
>>
>> diff --git a/mm/hugetlb.c b/mm/hugetlb.c
>> index ce9c8e57b8e5c..6fd6e58ace869 100644
>> --- a/mm/hugetlb.c
>> +++ b/mm/hugetlb.c
>> @@ -2181,22 +2181,46 @@ 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 (folio) {
>> +		if (unlikely(read_mems_allowed_retry(cpuset_mems_cookie)))
>> +			goto retry;
>> +
>> +		if (node_isset(folio_nid(folio), alloc_nodemask))
>> +			return folio;
>> +
>> +		/* Balance free_huge_folio()'s decrement before caller accounting */
>> +		lruvec_stat_mod_folio(folio, NR_HUGETLB, pages_per_huge_page(h));
>> +		free_huge_folio(folio);
>> +		folio = NULL;
>> +	}
> 
> the page allocator only retries when the allocation fails, why does this
> need to drop a folio that raced with a cpuset change? retrying only on
> !folio would make this whole block go away, with the check at the end as:

You're right. I overthought the cpuset race and added unnecessary
revalidation of successful allocations.

> 
>          if (!folio &&
>              unlikely(read_mems_allowed_retry(cpuset_mems_cookie)))
>                  goto retry;

I'll follow your suggestion and retry only failed allocations, and
update the commit message accordingly.

Thanks,
Qi

> 
>>   
>>   	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 (unlikely(read_mems_allowed_retry(cpuset_mems_cookie)))
>> +		goto retry;
>>   
>>   	return folio;
>>   }
> 
> 



^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH 2/4] mm: hugetlb: skip CMA allocations under PF_MEMALLOC_PIN
  2026-11-04  3:27     ` Qi Zheng
@ 2026-11-04  6:51       ` Muchun Song
  2026-11-04  7:07       ` Muchun Song
  1 sibling, 0 replies; 18+ messages in thread
From: Muchun Song @ 2026-11-04  6:51 UTC (permalink / raw)
  To: Qi Zheng
  Cc: osalvador, David Hildenbrand, akpm, azpijr, linux-kernel,
	linux-mm, stable, Qi Zheng

[-- Attachment #1: Type: text/plain, Size: 7868 bytes --]



> On Nov 4, 2026, at 11:27, Qi Zheng <qi.zheng@linux.dev> wrote:
> 
> Hi Muchun,

Hi,

> 
> On 11/3/26 6:56 PM, Muchun Song wrote:
>>> On Nov 3, 2026, at 16:01, Qi Zheng <qi.zheng@linux.dev> wrote:
>>> 
>>> From: Qi Zheng <zhengqi.arch@bytedance.com>
>>> 
>>> With gigantic overcommit enabled, a fault during long-term pinning
>>> can allocate a fresh HugeTLB folio from CMA despite PF_MEMALLOC_PIN.
>>> 
>>> The folio must then be migrated before it can be pinned. However,
>>> fresh gigantic migration targets are not supported, so pinning fails
>>> when the HugeTLB pool has no suitable target, even if the original
>>> fault could have allocated pinnable memory outside CMA.
>>> 
>>> Honor PF_MEMALLOC_PIN in the HugeTLB CMA allocator so the fault can
>>> try the non-CMA fallback, or fail when allocation is restricted to CMA.
>> Wait... I think my Acked-by got ahead of me here. :)
>> There seems to be a rather odd distinction that GUP is missing.
>> The usual reason long-term GUP must migrate a page out of a
>> MIGRATE_CMA pageblock is that the page was only borrowed from CMA by the
>> normal page allocator.  The corresponding CMA bitmap range is still
>> available, so a future CMA allocation relies on being able to migrate
>> that page away.  A long-term pin would prevent that.
>> However, a HugeTLB folio returned by
>> hugetlb_cma_alloc_frozen_folio() is different.  It is the actual CMA
>> allocation, not merely a movable page that happens to reside in a
>> MIGRATE_CMA pageblock.  Its range has already been marked allocated in
>> the hugetlb CMA bitmap.
>> Therefore, CMA cannot allocate that range again until the HugeTLB folio
>> is freed anyway.  Long-term pinning the folio does not seem to make the
>> CMA area any less available than keeping the HugeTLB folio mapped for
>> the same amount of time.
>> folio_is_longterm_pinnable() currently only sees MIGRATE_CMA and does
>> not distinguish these two cases.  In that light, rejecting
>> PF_MEMALLOC_PIN in hugetlb_cma_alloc_frozen_folio() looks like working
>> around an overly broad GUP classification.  It also means that
>> hugetlb_cma_only users will fail the fault, while other users may consume
>> a gigantic contiguous range outside the dedicated hugetlb CMA area even
>> when that area has free space.
>> Would it make more sense to fix the long-term GUP check instead, so that
>> an explicitly CMA-allocated HugeTLB folio is considered pinnable, while
>> ordinary pages merely borrowed from MIGRATE_CMA pageblocks remain unpinnable?
> 
> Make sense!
> 
> I'll replace this patch with an exception for HPG_cma folios in the
> MIGRATE_CMA check. This also lets the HugeTLB allocator select preallocated
> CMA folios during long-term pinning. MIGRATE_ISOLATE and ZONE_MOVABLE
> remain restricted.
> 
> Fresh CMA allocations under PF_MEMALLOC_PIN also need to skip ranges
> in ZONE_MOVABLE. Returning -EBUSY here lets CMA try its remaining ranges.
> 
> The incremental diff against this patch is below.
> 
> diff --git a/include/linux/cma.h b/include/linux/cma.h
> index 8555d38a97b1..7fd1b6e65b58 100644
> --- a/include/linux/cma.h
> +++ b/include/linux/cma.h
> @@ -21,11 +21,13 @@
> #define CMA_MIN_ALIGNMENT_BYTES (PAGE_SIZE * CMA_MIN_ALIGNMENT_PAGES)
> 
> struct cma;
> +struct folio;
> 
> extern unsigned long totalcma_pages;
> extern phys_addr_t cma_get_base(const struct cma *cma);
> extern unsigned long cma_get_size(const struct cma *cma);
> extern const char *cma_get_name(const struct cma *cma);
> +bool cma_folio_is_pinnable(struct folio *folio);
> 
> extern int __init cma_declare_contiguous_nid(phys_addr_t base,
> 			phys_addr_t size, phys_addr_t limit,
> diff --git a/include/linux/mm.h b/include/linux/mm.h
> index cb993a1d0728..d15810be7d96 100644
> --- a/include/linux/mm.h
> +++ b/include/linux/mm.h
> @@ -3,6 +3,7 @@
> #define _LINUX_MM_H
> 
> #include <linux/args.h>
> +#include <linux/cma.h>
> #include <linux/errno.h>
> #include <linux/mmdebug.h>
> #include <linux/gfp.h>
> @@ -2975,14 +2976,16 @@ static inline bool is_zero_folio(const struct folio *folio)
> 	return is_zero_page(&folio->page);
> }
> 
> -/* MIGRATE_CMA and ZONE_MOVABLE do not allow pin folios */
> +/* Borrowed CMA pages and ZONE_MOVABLE folios cannot be pinned long term. */
> #ifdef CONFIG_MIGRATION
> static inline bool folio_is_longterm_pinnable(struct folio *folio)
> {
> #ifdef CONFIG_CMA
> 	int mt = folio_migratetype(folio);
> 
> -	if (mt == MIGRATE_CMA || mt == MIGRATE_ISOLATE)
> +	if (mt == MIGRATE_ISOLATE)
> +		return false;
> +	if (mt == MIGRATE_CMA && !cma_folio_is_pinnable(folio))
> 		return false;

Actually, we do not need cma_folio_is_pinnable(), does the following work for you?

--- a/include/linux/mm.h
+++ b/include/linux/mm.h
@@ -2982,6 +2982,9 @@ static inline bool folio_is_longterm_pinnable(struct folio *folio)
 #ifdef CONFIG_CMA
        int mt = folio_migratetype(folio);

+       if (folio_test_hugetlb(folio) && folio_test_hugetlb_cma(folio))
+               return true;
+
        if (mt == MIGRATE_CMA || mt == MIGRATE_ISOLATE)
                return false;
 #endif

> #endif
> 	/* The zero page can be "pinned" but gets special handling. */
> diff --git a/mm/cma.c b/mm/cma.c
> index a10ea37a261d..c2814f54f86a 100644
> --- a/mm/cma.c
> +++ b/mm/cma.c
> @@ -27,6 +27,7 @@
> #include <linux/log2.h>
> #include <linux/cma.h>
> #include <linux/highmem.h>
> +#include <linux/hugetlb.h>
> #include <linux/io.h>
> #include <linux/kmemleak.h>
> #include <trace/events/cma.h>
> @@ -55,6 +56,12 @@ const char *cma_get_name(const struct cma *cma)
> }
> EXPORT_SYMBOL_GPL(cma_get_name);
> 
> +bool cma_folio_is_pinnable(struct folio *folio)
> +{
> +	/* Explicit HugeTLB CMA allocations own their bitmap ranges. */
> +	return folio_test_hugetlb(folio) && folio_test_hugetlb_cma(folio);
> +}
> +
> static unsigned long cma_bitmap_aligned_mask(const struct cma *cma,
> 					     unsigned int align_order)
> {
> @@ -792,6 +799,11 @@ static int cma_range_alloc(struct cma *cma, struct cma_memrange *cmr,
> 	int ret = -EBUSY;
> 	struct page *page = NULL;
> 
> +	/* Each range lies within one zone; try other ranges if needed. */
> +	if ((current->flags & PF_MEMALLOC_PIN) &&
> +	    is_zone_movable_page(pfn_to_page(cmr->base_pfn)))
> +		return -EBUSY;
> +

I don't know why we need this.

Thanks.

> 	mask = cma_bitmap_aligned_mask(cma, align);
> 	offset = cma_bitmap_aligned_offset(cma, cmr, align);
> 	bitmap_maxno = cma_bitmap_maxno(cma, cmr);
> diff --git a/mm/hugetlb_cma.c b/mm/hugetlb_cma.c
> index dde1c866406f..95fd2d190f0d 100644
> --- a/mm/hugetlb_cma.c
> +++ b/mm/hugetlb_cma.c
> @@ -62,9 +62,6 @@ struct folio *hugetlb_cma_alloc_frozen_folio(int order, gfp_t gfp_mask,
> 	if (!hugetlb_cma_size)
> 		return NULL;
> 
> -	if (current->flags & PF_MEMALLOC_PIN)
> -		return NULL;
> -
> retry_cpuset:
> 	if (!nodemask) {
> 		cpuset_mems_cookie = read_mems_allowed_begin();
> 
> Thanks,
> Qi
> 
>> Thanks,
>> Muchun
>>> 
>>> Fixes: eb02f14c4a2b ("mm/hugetlb: allow overcommitting gigantic hugepages")
>>> Cc: stable@vger.kernel.org
>>> Signed-off-by: Qi Zheng <zhengqi.arch@bytedance.com>
>>> ---
>>> mm/hugetlb_cma.c | 3 +++
>>> 1 file changed, 3 insertions(+)
>>> 
>>> diff --git a/mm/hugetlb_cma.c b/mm/hugetlb_cma.c
>>> index 95fd2d190f0d2..dde1c866406fb 100644
>>> --- a/mm/hugetlb_cma.c
>>> +++ b/mm/hugetlb_cma.c
>>> @@ -62,6 +62,9 @@ struct folio *hugetlb_cma_alloc_frozen_folio(int order, gfp_t gfp_mask,
>>> 	if (!hugetlb_cma_size)
>>> 		return NULL;
>>> 
>>> +	if (current->flags & PF_MEMALLOC_PIN)
>>> +		return NULL;
>>> +
>>> retry_cpuset:
>>> 	if (!nodemask) {
>>> 		cpuset_mems_cookie = read_mems_allowed_begin();
>>> -- 
>>> 2.54.0


[-- Attachment #2: Type: text/html, Size: 115435 bytes --]

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH 2/4] mm: hugetlb: skip CMA allocations under PF_MEMALLOC_PIN
  2026-11-04  3:27     ` Qi Zheng
  2026-11-04  6:51       ` Muchun Song
@ 2026-11-04  7:07       ` Muchun Song
  2026-11-04  7:47         ` Qi Zheng
  1 sibling, 1 reply; 18+ messages in thread
From: Muchun Song @ 2026-11-04  7:07 UTC (permalink / raw)
  To: Qi Zheng
  Cc: osalvador, David Hildenbrand, akpm, azpijr, linux-kernel,
	linux-mm, stable, Qi Zheng



> On Nov 4, 2026, at 11:27, Qi Zheng <qi.zheng@linux.dev> wrote:
> 
> Hi Muchun,

Hi,

> 
> On 11/3/26 6:56 PM, Muchun Song wrote:
>>> On Nov 3, 2026, at 16:01, Qi Zheng <qi.zheng@linux.dev> wrote:
>>> 
>>> From: Qi Zheng <zhengqi.arch@bytedance.com>
>>> 
>>> With gigantic overcommit enabled, a fault during long-term pinning
>>> can allocate a fresh HugeTLB folio from CMA despite PF_MEMALLOC_PIN.
>>> 
>>> The folio must then be migrated before it can be pinned. However,
>>> fresh gigantic migration targets are not supported, so pinning fails
>>> when the HugeTLB pool has no suitable target, even if the original
>>> fault could have allocated pinnable memory outside CMA.
>>> 
>>> Honor PF_MEMALLOC_PIN in the HugeTLB CMA allocator so the fault can
>>> try the non-CMA fallback, or fail when allocation is restricted to CMA.
>> Wait... I think my Acked-by got ahead of me here. :)
>> There seems to be a rather odd distinction that GUP is missing.
>> The usual reason long-term GUP must migrate a page out of a
>> MIGRATE_CMA pageblock is that the page was only borrowed from CMA by the
>> normal page allocator.  The corresponding CMA bitmap range is still
>> available, so a future CMA allocation relies on being able to migrate
>> that page away.  A long-term pin would prevent that.
>> However, a HugeTLB folio returned by
>> hugetlb_cma_alloc_frozen_folio() is different.  It is the actual CMA
>> allocation, not merely a movable page that happens to reside in a
>> MIGRATE_CMA pageblock.  Its range has already been marked allocated in
>> the hugetlb CMA bitmap.
>> Therefore, CMA cannot allocate that range again until the HugeTLB folio
>> is freed anyway.  Long-term pinning the folio does not seem to make the
>> CMA area any less available than keeping the HugeTLB folio mapped for
>> the same amount of time.
>> folio_is_longterm_pinnable() currently only sees MIGRATE_CMA and does
>> not distinguish these two cases.  In that light, rejecting
>> PF_MEMALLOC_PIN in hugetlb_cma_alloc_frozen_folio() looks like working
>> around an overly broad GUP classification.  It also means that
>> hugetlb_cma_only users will fail the fault, while other users may consume
>> a gigantic contiguous range outside the dedicated hugetlb CMA area even
>> when that area has free space.
>> Would it make more sense to fix the long-term GUP check instead, so that
>> an explicitly CMA-allocated HugeTLB folio is considered pinnable, while
>> ordinary pages merely borrowed from MIGRATE_CMA pageblocks remain unpinnable?
> 
> Make sense!
> 
> I'll replace this patch with an exception for HPG_cma folios in the
> MIGRATE_CMA check. This also lets the HugeTLB allocator select preallocated
> CMA folios during long-term pinning. MIGRATE_ISOLATE and ZONE_MOVABLE
> remain restricted.
> 
> Fresh CMA allocations under PF_MEMALLOC_PIN also need to skip ranges
> in ZONE_MOVABLE. Returning -EBUSY here lets CMA try its remaining ranges.
> 
> The incremental diff against this patch is below.
> 
> diff --git a/include/linux/cma.h b/include/linux/cma.h
> index 8555d38a97b1..7fd1b6e65b58 100644
> --- a/include/linux/cma.h
> +++ b/include/linux/cma.h
> @@ -21,11 +21,13 @@
> #define CMA_MIN_ALIGNMENT_BYTES (PAGE_SIZE * CMA_MIN_ALIGNMENT_PAGES)
> 
> struct cma;
> +struct folio;
> 
> extern unsigned long totalcma_pages;
> extern phys_addr_t cma_get_base(const struct cma *cma);
> extern unsigned long cma_get_size(const struct cma *cma);
> extern const char *cma_get_name(const struct cma *cma);
> +bool cma_folio_is_pinnable(struct folio *folio);
> 
> extern int __init cma_declare_contiguous_nid(phys_addr_t base,
> 			phys_addr_t size, phys_addr_t limit,
> diff --git a/include/linux/mm.h b/include/linux/mm.h
> index cb993a1d0728..d15810be7d96 100644
> --- a/include/linux/mm.h
> +++ b/include/linux/mm.h
> @@ -3,6 +3,7 @@
> #define _LINUX_MM_H
> 
> #include <linux/args.h>
> +#include <linux/cma.h>
> #include <linux/errno.h>
> #include <linux/mmdebug.h>
> #include <linux/gfp.h>
> @@ -2975,14 +2976,16 @@ static inline bool is_zero_folio(const struct folio *folio)
> 	return is_zero_page(&folio->page);
> }
> 
> -/* MIGRATE_CMA and ZONE_MOVABLE do not allow pin folios */
> +/* Borrowed CMA pages and ZONE_MOVABLE folios cannot be pinned long term. */
> #ifdef CONFIG_MIGRATION
> static inline bool folio_is_longterm_pinnable(struct folio *folio)
> {
> #ifdef CONFIG_CMA
> 	int mt = folio_migratetype(folio);
> 
> -	if (mt == MIGRATE_CMA || mt == MIGRATE_ISOLATE)
> +	if (mt == MIGRATE_ISOLATE)
> +		return false;
> +	if (mt == MIGRATE_CMA && !cma_folio_is_pinnable(folio))
> 		return false;

Actually, we do not need cma_folio_is_pinnable(), does the following work for you?

--- a/include/linux/mm.h
+++ b/include/linux/mm.h
@@ -2982,6 +2982,9 @@ static inline bool folio_is_longterm_pinnable(struct folio *folio)
 #ifdef CONFIG_CMA
        int mt = folio_migratetype(folio);

+       if (folio_test_hugetlb(folio) && folio_test_hugetlb_cma(folio))
+               return true;
+
        if (mt == MIGRATE_CMA || mt == MIGRATE_ISOLATE)
                return false;
 #endif

> #endif
> /* The zero page can be "pinned" but gets special handling. */
> diff --git a/mm/cma.c b/mm/cma.c
> index a10ea37a261d..c2814f54f86a 100644
> --- a/mm/cma.c
> +++ b/mm/cma.c
> @@ -27,6 +27,7 @@
> #include <linux/log2.h>
> #include <linux/cma.h>
> #include <linux/highmem.h>
> +#include <linux/hugetlb.h>
> #include <linux/io.h>
> #include <linux/kmemleak.h>
> #include <trace/events/cma.h>
> @@ -55,6 +56,12 @@ const char *cma_get_name(const struct cma *cma)
> }
> EXPORT_SYMBOL_GPL(cma_get_name);
> 
> +bool cma_folio_is_pinnable(struct folio *folio)
> +{
> +	/* Explicit HugeTLB CMA allocations own their bitmap ranges. */
> +	return folio_test_hugetlb(folio) && folio_test_hugetlb_cma(folio);
> +}
> +
> static unsigned long cma_bitmap_aligned_mask(const struct cma *cma,
> 					     unsigned int align_order)
> {
> @@ -792,6 +799,11 @@ static int cma_range_alloc(struct cma *cma, struct cma_memrange *cmr,
> 	int ret = -EBUSY;
> 	struct page *page = NULL;
> 
> + 	/* Each range lies within one zone; try other ranges if needed. */
> +	if ((current->flags & PF_MEMALLOC_PIN) &&
> + 	    is_zone_movable_page(pfn_to_page(cmr->base_pfn)))
> +		return -EBUSY;

I don't know why we need this.

Thanks,
Muchun

> +
> 	mask = cma_bitmap_aligned_mask(cma, align);
> 	offset = cma_bitmap_aligned_offset(cma, cmr, align);
> 	bitmap_maxno = cma_bitmap_maxno(cma, cmr);
> diff --git a/mm/hugetlb_cma.c b/mm/hugetlb_cma.c
> index dde1c866406f..95fd2d190f0d 100644
> --- a/mm/hugetlb_cma.c
> +++ b/mm/hugetlb_cma.c
> @@ -62,9 +62,6 @@ struct folio *hugetlb_cma_alloc_frozen_folio(int order, gfp_t gfp_mask,
> 	if (!hugetlb_cma_size)
> 		return NULL;
> 
> -	if (current->flags & PF_MEMALLOC_PIN)
> -		return NULL;
> -
> retry_cpuset:
> 	if (!nodemask) {
> 		cpuset_mems_cookie = read_mems_allowed_begin();
> 
> Thanks,
> Qi
> 
>> Thanks,
>> Muchun
>>> 
>>> Fixes: eb02f14c4a2b ("mm/hugetlb: allow overcommitting gigantic hugepages")
>>> Cc: stable@vger.kernel.org
>>> Signed-off-by: Qi Zheng <zhengqi.arch@bytedance.com>
>>> ---
>>> mm/hugetlb_cma.c | 3 +++
>>> 1 file changed, 3 insertions(+)
>>> 
>>> diff --git a/mm/hugetlb_cma.c b/mm/hugetlb_cma.c
>>> index 95fd2d190f0d2..dde1c866406fb 100644
>>> --- a/mm/hugetlb_cma.c
>>> +++ b/mm/hugetlb_cma.c
>>> @@ -62,6 +62,9 @@ struct folio *hugetlb_cma_alloc_frozen_folio(int order, gfp_t gfp_mask,
>>> 	if (!hugetlb_cma_size)
>>> 		return NULL;
>>> 
>>> +	if (current->flags & PF_MEMALLOC_PIN)
>>> +		return NULL;
>>> +
>>> retry_cpuset:
>>> 	if (!nodemask) {
>>> 		cpuset_mems_cookie = read_mems_allowed_begin();
>>> -- 
>>> 2.54.0




^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH 2/4] mm: hugetlb: skip CMA allocations under PF_MEMALLOC_PIN
  2026-11-04  7:07       ` Muchun Song
@ 2026-11-04  7:47         ` Qi Zheng
  0 siblings, 0 replies; 18+ messages in thread
From: Qi Zheng @ 2026-11-04  7:47 UTC (permalink / raw)
  To: Muchun Song
  Cc: osalvador, David Hildenbrand, akpm, azpijr, linux-kernel,
	linux-mm, stable, Qi Zheng

Hi Muchun,

On 11/4/26 3:07 PM, Muchun Song wrote:
> 
> 
>> On Nov 4, 2026, at 11:27, Qi Zheng <qi.zheng@linux.dev> wrote:
>>
>> Hi Muchun,
> 
> Hi,
> 
>>
>> On 11/3/26 6:56 PM, Muchun Song wrote:
>>>> On Nov 3, 2026, at 16:01, Qi Zheng <qi.zheng@linux.dev> wrote:
>>>>
>>>> From: Qi Zheng <zhengqi.arch@bytedance.com>
>>>>
>>>> With gigantic overcommit enabled, a fault during long-term pinning
>>>> can allocate a fresh HugeTLB folio from CMA despite PF_MEMALLOC_PIN.
>>>>
>>>> The folio must then be migrated before it can be pinned. However,
>>>> fresh gigantic migration targets are not supported, so pinning fails
>>>> when the HugeTLB pool has no suitable target, even if the original
>>>> fault could have allocated pinnable memory outside CMA.
>>>>
>>>> Honor PF_MEMALLOC_PIN in the HugeTLB CMA allocator so the fault can
>>>> try the non-CMA fallback, or fail when allocation is restricted to CMA.
>>> Wait... I think my Acked-by got ahead of me here. :)
>>> There seems to be a rather odd distinction that GUP is missing.
>>> The usual reason long-term GUP must migrate a page out of a
>>> MIGRATE_CMA pageblock is that the page was only borrowed from CMA by the
>>> normal page allocator.  The corresponding CMA bitmap range is still
>>> available, so a future CMA allocation relies on being able to migrate
>>> that page away.  A long-term pin would prevent that.
>>> However, a HugeTLB folio returned by
>>> hugetlb_cma_alloc_frozen_folio() is different.  It is the actual CMA
>>> allocation, not merely a movable page that happens to reside in a
>>> MIGRATE_CMA pageblock.  Its range has already been marked allocated in
>>> the hugetlb CMA bitmap.
>>> Therefore, CMA cannot allocate that range again until the HugeTLB folio
>>> is freed anyway.  Long-term pinning the folio does not seem to make the
>>> CMA area any less available than keeping the HugeTLB folio mapped for
>>> the same amount of time.
>>> folio_is_longterm_pinnable() currently only sees MIGRATE_CMA and does
>>> not distinguish these two cases.  In that light, rejecting
>>> PF_MEMALLOC_PIN in hugetlb_cma_alloc_frozen_folio() looks like working
>>> around an overly broad GUP classification.  It also means that
>>> hugetlb_cma_only users will fail the fault, while other users may consume
>>> a gigantic contiguous range outside the dedicated hugetlb CMA area even
>>> when that area has free space.
>>> Would it make more sense to fix the long-term GUP check instead, so that
>>> an explicitly CMA-allocated HugeTLB folio is considered pinnable, while
>>> ordinary pages merely borrowed from MIGRATE_CMA pageblocks remain unpinnable?
>>
>> Make sense!
>>
>> I'll replace this patch with an exception for HPG_cma folios in the
>> MIGRATE_CMA check. This also lets the HugeTLB allocator select preallocated
>> CMA folios during long-term pinning. MIGRATE_ISOLATE and ZONE_MOVABLE
>> remain restricted.
>>
>> Fresh CMA allocations under PF_MEMALLOC_PIN also need to skip ranges
>> in ZONE_MOVABLE. Returning -EBUSY here lets CMA try its remaining ranges.
>>
>> The incremental diff against this patch is below.
>>
>> diff --git a/include/linux/cma.h b/include/linux/cma.h
>> index 8555d38a97b1..7fd1b6e65b58 100644
>> --- a/include/linux/cma.h
>> +++ b/include/linux/cma.h
>> @@ -21,11 +21,13 @@
>> #define CMA_MIN_ALIGNMENT_BYTES (PAGE_SIZE * CMA_MIN_ALIGNMENT_PAGES)
>>
>> struct cma;
>> +struct folio;
>>
>> extern unsigned long totalcma_pages;
>> extern phys_addr_t cma_get_base(const struct cma *cma);
>> extern unsigned long cma_get_size(const struct cma *cma);
>> extern const char *cma_get_name(const struct cma *cma);
>> +bool cma_folio_is_pinnable(struct folio *folio);
>>
>> extern int __init cma_declare_contiguous_nid(phys_addr_t base,
>> 			phys_addr_t size, phys_addr_t limit,
>> diff --git a/include/linux/mm.h b/include/linux/mm.h
>> index cb993a1d0728..d15810be7d96 100644
>> --- a/include/linux/mm.h
>> +++ b/include/linux/mm.h
>> @@ -3,6 +3,7 @@
>> #define _LINUX_MM_H
>>
>> #include <linux/args.h>
>> +#include <linux/cma.h>
>> #include <linux/errno.h>
>> #include <linux/mmdebug.h>
>> #include <linux/gfp.h>
>> @@ -2975,14 +2976,16 @@ static inline bool is_zero_folio(const struct folio *folio)
>> 	return is_zero_page(&folio->page);
>> }
>>
>> -/* MIGRATE_CMA and ZONE_MOVABLE do not allow pin folios */
>> +/* Borrowed CMA pages and ZONE_MOVABLE folios cannot be pinned long term. */
>> #ifdef CONFIG_MIGRATION
>> static inline bool folio_is_longterm_pinnable(struct folio *folio)
>> {
>> #ifdef CONFIG_CMA
>> 	int mt = folio_migratetype(folio);
>>
>> -	if (mt == MIGRATE_CMA || mt == MIGRATE_ISOLATE)
>> +	if (mt == MIGRATE_ISOLATE)
>> +		return false;
>> +	if (mt == MIGRATE_CMA && !cma_folio_is_pinnable(folio))
>> 		return false;
> 
> Actually, we do not need cma_folio_is_pinnable(), does the following work for you?

OK, will do.

> 
> --- a/include/linux/mm.h
> +++ b/include/linux/mm.h
> @@ -2982,6 +2982,9 @@ static inline bool folio_is_longterm_pinnable(struct folio *folio)
>   #ifdef CONFIG_CMA
>          int mt = folio_migratetype(folio);
> 
> +       if (folio_test_hugetlb(folio) && folio_test_hugetlb_cma(folio))
> +               return true;
> +
>          if (mt == MIGRATE_CMA || mt == MIGRATE_ISOLATE)
>                  return false;
>   #endif

We should move the implementation of folio_is_longterm_pinnable() to
mm/gup.c to avoid the header dependency and build errors.

> 
>> #endif
>> /* The zero page can be "pinned" but gets special handling. */
>> diff --git a/mm/cma.c b/mm/cma.c
>> index a10ea37a261d..c2814f54f86a 100644
>> --- a/mm/cma.c
>> +++ b/mm/cma.c
>> @@ -27,6 +27,7 @@
>> #include <linux/log2.h>
>> #include <linux/cma.h>
>> #include <linux/highmem.h>
>> +#include <linux/hugetlb.h>
>> #include <linux/io.h>
>> #include <linux/kmemleak.h>
>> #include <trace/events/cma.h>
>> @@ -55,6 +56,12 @@ const char *cma_get_name(const struct cma *cma)
>> }
>> EXPORT_SYMBOL_GPL(cma_get_name);
>>
>> +bool cma_folio_is_pinnable(struct folio *folio)
>> +{
>> +	/* Explicit HugeTLB CMA allocations own their bitmap ranges. */
>> +	return folio_test_hugetlb(folio) && folio_test_hugetlb_cma(folio);
>> +}
>> +
>> static unsigned long cma_bitmap_aligned_mask(const struct cma *cma,
>> 					     unsigned int align_order)
>> {
>> @@ -792,6 +799,11 @@ static int cma_range_alloc(struct cma *cma, struct cma_memrange *cmr,
>> 	int ret = -EBUSY;
>> 	struct page *page = NULL;
>>
>> + 	/* Each range lies within one zone; try other ranges if needed. */
>> +	if ((current->flags & PF_MEMALLOC_PIN) &&
>> + 	    is_zone_movable_page(pfn_to_page(cmr->base_pfn)))
>> +		return -EBUSY;
> 
> I don't know why we need this.

I had assumed that ZONE_MOVABLE pages must always remain unpinnable.
But Documentation/admin-guide/mm/memory-hotplug.rst explicitly states:

   - Memory blocks overlapping with CMA areas cannot be offlined, this 
applies to
     memory blocks present during boot only.

So pinning explicitly CMA-allocated HugeTLB folios does not introduce
an additional offlining restriction. The ZONE_MOVABLE check I added
in cma_range_alloc() is unnecessary, and I'll drop it.

Thanks,
Qi



^ permalink raw reply	[flat|nested] 18+ messages in thread

end of thread, other threads:[~2026-11-04  7:48 UTC | newest]

Thread overview: 18+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-11-03  8:01 [PATCH 0/4] mm: hugetlb: fix some CMA-related issues Qi Zheng
2026-11-03  8:01 ` [PATCH 1/4] x86: hugetlb: disable default CMA reservations on 32-bit kernel Qi Zheng
2026-11-03  8:41   ` Muchun Song
2026-11-03 11:40   ` Jose A. Perez de Azpillaga
2026-11-03  8:01 ` [PATCH 2/4] mm: hugetlb: skip CMA allocations under PF_MEMALLOC_PIN Qi Zheng
2026-11-03  8:44   ` Muchun Song
2026-11-03 10:56   ` Muchun Song
2026-11-03 11:59     ` Jose A. Perez de Azpillaga
2026-11-04  3:27     ` Qi Zheng
2026-11-04  6:51       ` Muchun Song
2026-11-04  7:07       ` Muchun Song
2026-11-04  7:47         ` Qi Zheng
2026-11-03  8:01 ` [PATCH 3/4] mm: hugetlb: skip CMA when direct reclaim is disallowed Qi Zheng
2026-11-03  8:47   ` sashiko-bot
2026-11-03  9:11     ` Qi Zheng
2026-11-03  8:01 ` [PATCH 4/4] mm: hugetlb: respect cpuset restrictions for surplus allocations Qi Zheng
2026-11-03 12:21   ` Jose A. Perez de Azpillaga
2026-11-04  3:39     ` Qi Zheng

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.