Linux-mm Archive on lore.kernel.org
 help / color / mirror / Atom feed
From: Qi Zheng <qi.zheng@linux.dev>
To: sashiko-reviews@lists.linux.dev, muchun.song@linux.dev,
	osalvador@suse.de, david@kernel.org, akpm@linux-foundation.org,
	azpijr@gmail.com
Cc: linux-mm@kvack.org, Qi Zheng <zhengqi.arch@bytedance.com>
Subject: Re: [PATCH 3/4] mm: hugetlb: skip CMA when direct reclaim is disallowed
Date: Tue, 3 Nov 2026 17:11:39 +0800	[thread overview]
Message-ID: <d9ebae9f-7a3e-412f-bb1c-f753b83ede59@linux.dev> (raw)
In-Reply-To: <sashiko-outbox-194875@kernel.org>



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

> 



  reply	other threads:[~2026-11-03  9:11 UTC|newest]

Thread overview: 18+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
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 [this message]
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

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=d9ebae9f-7a3e-412f-bb1c-f753b83ede59@linux.dev \
    --to=qi.zheng@linux.dev \
    --cc=akpm@linux-foundation.org \
    --cc=azpijr@gmail.com \
    --cc=david@kernel.org \
    --cc=linux-mm@kvack.org \
    --cc=muchun.song@linux.dev \
    --cc=osalvador@suse.de \
    --cc=sashiko-reviews@lists.linux.dev \
    --cc=zhengqi.arch@bytedance.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox