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
>
next prev parent 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