Re: [PATCH v2 4/5] mm: hugetlb: avoid recursive CMA allocation during migration

From: Qi Zheng

Date: Fri Oct 23 2026 - 03:26:52 EST




On 10/23/26 3:02 PM, Muchun Song wrote:


On 2026/10/22 15:31, Qi Zheng wrote:
From: Qi Zheng <zhengqi.arch@xxxxxxxxxxxxx>

Contiguous allocation can migrate allocated HugeTLB folios or replace free
HugeTLB folios while holding a CMA allocation mutex. Allocating a
replacement through CMA would recurse into that allocation path once
non-gigantic HugeTLB pages can use CMA.

Pass allow_cma through the migration and fresh allocation helpers, and
disable it for MR_CONTIG_RANGE and free folio replacement. Move the CMA
attempt into the common allocation path, retaining the gigantic size
restriction. Honor hugetlb_cma_only even when allow_cma is false.

The existing gigantic migration restriction is unchanged.

Assisted-by: LLM
Signed-off-by: Qi Zheng <zhengqi.arch@xxxxxxxxxxxxx>
---
  include/linux/hugetlb.h |  4 +--
  mm/hugetlb.c            | 69 +++++++++++++++++++++--------------------
  mm/mempolicy.c          |  2 +-
  mm/migrate.c            |  3 +-
  4 files changed, 40 insertions(+), 38 deletions(-)

diff --git a/include/linux/hugetlb.h b/include/linux/hugetlb.h
index 5029c72418631..38612f7b3478f 100644
--- a/include/linux/hugetlb.h
+++ b/include/linux/hugetlb.h
@@ -736,7 +736,7 @@ struct folio *alloc_hugetlb_folio(struct vm_area_struct *vma,
                  unsigned long addr, bool cow_from_owner);
  struct folio *alloc_hugetlb_folio_nodemask(struct hstate *h, int preferred_nid,
                  nodemask_t *nmask, gfp_t gfp_mask,
-                bool allow_alloc_fallback);
+                bool allow_alloc_fallback, bool allow_cma);
  struct folio *alloc_hugetlb_folio_reserve(struct hstate *h, int preferred_nid,
                        nodemask_t *nmask, gfp_t gfp_mask);
@@ -1172,7 +1172,7 @@ alloc_hugetlb_folio_reserve(struct hstate *h, int preferred_nid,
  static inline struct folio *
  alloc_hugetlb_folio_nodemask(struct hstate *h, int preferred_nid,
              nodemask_t *nmask, gfp_t gfp_mask,
-            bool allow_alloc_fallback)
+            bool allow_alloc_fallback, bool allow_cma)
  {
      return NULL;
  }
diff --git a/mm/hugetlb.c b/mm/hugetlb.c
index 6b08074d11f52..c1a8d00be11ac 100644
--- a/mm/hugetlb.c
+++ b/mm/hugetlb.c
@@ -1362,18 +1362,8 @@ static struct folio *dequeue_hugetlb_folio(struct hstate *h, gfp_t gfp_mask,
  static struct folio *alloc_gigantic_frozen_folio(int order, gfp_t gfp_mask,
          int nid, nodemask_t *nodemask)
  {
-    struct folio *folio;
-
-    folio = hugetlb_cma_alloc_frozen_folio(order, gfp_mask, nid, nodemask);
-    if (folio)
-        return folio;
-
-    if (hugetlb_cma_exclusive_alloc(order))
-        return NULL;
-
-    folio = (struct folio *)alloc_contig_frozen_pages(1 << order, gfp_mask,
-                              nid, nodemask);
-    return folio;
+    return (struct folio *)alloc_contig_frozen_pages(1 << order, gfp_mask,
+                             nid, nodemask);
  }
  #else /* !CONFIG_ARCH_HAS_GIGANTIC_PAGE || !CONFIG_CONTIG_ALLOC */
  static struct folio *alloc_gigantic_frozen_folio(int order, gfp_t gfp_mask, int nid,
@@ -1841,37 +1831,47 @@ static struct folio *alloc_buddy_frozen_folio(int order, gfp_t gfp_mask,
  static struct folio *only_alloc_fresh_hugetlb_folio(struct hstate *h,
          gfp_t gfp_mask, int nid, nodemask_t *nmask,
-        nodemask_t *node_alloc_noretry)
+        nodemask_t *node_alloc_noretry, bool allow_cma)
  {
-    struct folio *folio;
+    struct folio *folio = NULL;
      int order = huge_page_order(h);
      if (nid == NUMA_NO_NODE)
          nid = numa_mem_id();
-    if (order_is_gigantic(order))
-        folio = alloc_gigantic_frozen_folio(order, gfp_mask, nid, nmask);
-    else
-        folio = alloc_buddy_frozen_folio(order, gfp_mask, nid, nmask,
-                         node_alloc_noretry);
+    if (allow_cma && order_is_gigantic(order))
+        folio = hugetlb_cma_alloc_frozen_folio(order, gfp_mask, nid, nmask);
+    if (!folio) {
+        if (hugetlb_cma_exclusive_alloc(order))
+            return NULL;
+
+        if (order_is_gigantic(order))
+            folio = alloc_gigantic_frozen_folio(order, gfp_mask, nid, nmask);
+        else
+            folio = alloc_buddy_frozen_folio(order, gfp_mask, nid, nmask,
+                             node_alloc_noretry);
+    }
+
      if (folio)
          init_new_hugetlb_folio(folio);
+
      return folio;
  }
  /*
- * Common helper to allocate a fresh hugetlb folio. All specific allocators
- * should use this function to get new hugetlb folio
+ * Common helper to allocate a fresh hugetlb folio. All special allocators
+ * should use this function to get new hugetlb folio.
   *
   * Note that returned folio is 'frozen':  ref count of head page and all tail
   * pages is zero, and the accounting must be done in the caller.
   */
  static struct folio *alloc_fresh_hugetlb_folio(struct hstate *h,
-        gfp_t gfp_mask, int nid, nodemask_t *nmask)
+        gfp_t gfp_mask, int nid, nodemask_t *nmask, bool allow_cma)
  {
      struct folio *folio;
-    folio = only_alloc_fresh_hugetlb_folio(h, gfp_mask, nid, nmask, NULL);
+    folio = only_alloc_fresh_hugetlb_folio(h, gfp_mask, nid, nmask, NULL,
+                           allow_cma);
      if (folio)
          hugetlb_vmemmap_optimize_folio(h, folio);
      return folio;
@@ -1911,7 +1911,7 @@ static struct folio *alloc_pool_huge_folio(struct hstate *h,
          struct folio *folio;
          folio = only_alloc_fresh_hugetlb_folio(h, gfp_mask, node,
-                    nodes_allowed, node_alloc_noretry);
+                    nodes_allowed, node_alloc_noretry, true);
          if (folio)
              return folio;
      }
@@ -2103,7 +2103,7 @@ static struct folio *alloc_surplus_hugetlb_folio(struct hstate *h,
          goto out_unlock;
      spin_unlock_irq(&hugetlb_lock);
-    folio = alloc_fresh_hugetlb_folio(h, gfp_mask, nid, nmask);
+    folio = alloc_fresh_hugetlb_folio(h, gfp_mask, nid, nmask, true);
      if (!folio)
          return NULL;
@@ -2139,14 +2139,14 @@ static struct folio *alloc_surplus_hugetlb_folio(struct hstate *h,
  }
  static struct folio *alloc_migrate_hugetlb_folio(struct hstate *h, gfp_t gfp_mask,
-                     int nid, nodemask_t *nmask)
+                     int nid, nodemask_t *nmask, bool allow_cma)
  {
      struct folio *folio;
      if (hstate_is_gigantic(h))
          return NULL;
-    folio = alloc_fresh_hugetlb_folio(h, gfp_mask, nid, nmask);
+    folio = alloc_fresh_hugetlb_folio(h, gfp_mask, nid, nmask, allow_cma);
      if (!folio)
          return NULL;
@@ -2219,7 +2219,8 @@ struct folio *alloc_hugetlb_folio_reserve(struct hstate *h, int preferred_nid,
  /* folio migration callback function */
  struct folio *alloc_hugetlb_folio_nodemask(struct hstate *h, int preferred_nid,
-        nodemask_t *nmask, gfp_t gfp_mask, bool allow_alloc_fallback)
+        nodemask_t *nmask, gfp_t gfp_mask, bool allow_alloc_fallback,

alloc_hugetlb_folio_nodemask() now ends with two adjacent boolean
arguments.

Call sites such as "false, true" are difficult to understand without
looking up the prototype.

Could you please add a comment at the caller to document this?

OK, will do.


Thanks,
Muchun