[RFC PATCH 4/6] mm/mempolicy: plumb alloc_flags through folio allocation

From: Gregory Price

Date: Wed Sep 23 2026 - 17:11:21 EST


From: Brendan Jackman <brendan.jackman@xxxxxxxxxx>

The NUMA policy allocation paths hard-code ALLOC_DEFAULT before reaching
the page allocator.

Add an alloc_flags argument to the MM-internal policy allocator and carry
it through preferred-many, huge-page, and general policy allocation paths.
Keep the public mempolicy folio interface as an ALLOC_DEFAULT wrapper.

Task policy selection is not safe in every interrupt context, so non-task
ALLOC_NOLOCK requests bypass it and use the supplied preferred node
directly.

No existing caller behavior is changed.

Link: https://lore.kernel.org/r/20260726-page_alloc-unmapped-v3-25-6f5729aa9832@xxxxxxxxxx
Signed-off-by: Brendan Jackman <brendan.jackman@xxxxxxxxxx>
Signed-off-by: Gregory Price <gourry@xxxxxxxxxx>
---
mm/mempolicy.c | 44 ++++++++++++++++++++++++++++++++------------
mm/mempolicy.h | 28 ++++++++++++++++++++++++++++
2 files changed, 60 insertions(+), 12 deletions(-)
create mode 100644 mm/mempolicy.h

diff --git a/mm/mempolicy.c b/mm/mempolicy.c
index 4c0b8ff1a7e6..2c01699081b7 100644
--- a/mm/mempolicy.c
+++ b/mm/mempolicy.c
@@ -120,6 +120,7 @@
#include <linux/memory.h>

#include "internal.h"
+#include "mempolicy.h"
#include "page_alloc.h"

/* Internal flags */
@@ -2437,7 +2438,8 @@ bool mempolicy_in_oom_domain(struct task_struct *tsk,
}

static struct page *alloc_pages_preferred_many(gfp_t gfp, unsigned int order,
- int nid, nodemask_t *nodemask)
+ int nid, nodemask_t *nodemask,
+ unsigned int alloc_flags)
{
struct page *page;
gfp_t preferred_gfp;
@@ -2451,10 +2453,10 @@ static struct page *alloc_pages_preferred_many(gfp_t gfp, unsigned int order,
preferred_gfp = gfp | __GFP_NOWARN;
preferred_gfp &= ~(__GFP_DIRECT_RECLAIM | __GFP_NOFAIL);
page = __alloc_frozen_pages_noprof(preferred_gfp, order, nid, nodemask,
- ALLOC_DEFAULT);
+ alloc_flags);
if (!page)
page = __alloc_frozen_pages_noprof(gfp, order, nid, NULL,
- ALLOC_DEFAULT);
+ alloc_flags);

return page;
}
@@ -2466,11 +2468,13 @@ static struct page *alloc_pages_preferred_many(gfp_t gfp, unsigned int order,
* @pol: Pointer to the NUMA mempolicy.
* @ilx: Index for interleave mempolicy (also distinguishes alloc_pages()).
* @nid: Preferred node (usually numa_node_id() but @mpol may override it).
+ * @alloc_flags: ALLOC_* flags.
*
* Return: The page on success or NULL if allocation fails.
*/
static struct page *alloc_pages_mpol(gfp_t gfp, unsigned int order,
- struct mempolicy *pol, pgoff_t ilx, int nid)
+ struct mempolicy *pol, pgoff_t ilx, int nid,
+ unsigned int alloc_flags)
{
nodemask_t *nodemask;
struct page *page;
@@ -2478,7 +2482,8 @@ static struct page *alloc_pages_mpol(gfp_t gfp, unsigned int order,
nodemask = policy_nodemask(gfp, pol, ilx, &nid);

if (pol->mode == MPOL_PREFERRED_MANY)
- return alloc_pages_preferred_many(gfp, order, nid, nodemask);
+ return alloc_pages_preferred_many(gfp, order, nid, nodemask,
+ alloc_flags);

if (IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE) &&
/* filter "hugepage" allocation, unless from alloc_pages() */
@@ -2502,7 +2507,7 @@ static struct page *alloc_pages_mpol(gfp_t gfp, unsigned int order,
*/
page = __alloc_frozen_pages_noprof(
gfp | __GFP_THISNODE | __GFP_NORETRY, order,
- nid, NULL, ALLOC_DEFAULT);
+ nid, NULL, alloc_flags);
if (page || !(gfp & __GFP_DIRECT_RECLAIM))
return page;
/*
@@ -2514,7 +2519,8 @@ static struct page *alloc_pages_mpol(gfp_t gfp, unsigned int order,
}
}

- page = __alloc_frozen_pages_noprof(gfp, order, nid, nodemask, ALLOC_DEFAULT);
+ page = __alloc_frozen_pages_noprof(gfp, order, nid, nodemask,
+ alloc_flags);

if (unlikely(pol->mode == MPOL_INTERLEAVE ||
pol->mode == MPOL_WEIGHTED_INTERLEAVE) && page) {
@@ -2530,11 +2536,18 @@ static struct page *alloc_pages_mpol(gfp_t gfp, unsigned int order,
return page;
}

-struct folio *folio_alloc_mpol_noprof(gfp_t gfp, unsigned int order,
- struct mempolicy *pol, pgoff_t ilx, int nid)
+struct folio *__folio_alloc_mpol_noprof(gfp_t gfp, unsigned int order,
+ struct mempolicy *pol, pgoff_t ilx, int nid,
+ unsigned int alloc_flags)
{
- struct page *page = alloc_pages_mpol(gfp | __GFP_COMP, order, pol,
- ilx, nid);
+ struct page *page;
+
+ if (unlikely((alloc_flags & ALLOC_NOLOCK) && !in_task()))
+ return __folio_alloc_flags_noprof(gfp, order, nid, NULL,
+ alloc_flags);
+
+ page = alloc_pages_mpol(gfp | __GFP_COMP, order, pol, ilx, nid,
+ alloc_flags);
if (!page)
return NULL;

@@ -2542,6 +2555,13 @@ struct folio *folio_alloc_mpol_noprof(gfp_t gfp, unsigned int order,
return page_rmappable_folio(page);
}

+struct folio *folio_alloc_mpol_noprof(gfp_t gfp, unsigned int order,
+ struct mempolicy *pol, pgoff_t ilx, int nid)
+{
+ return __folio_alloc_mpol_noprof(gfp, order, pol, ilx, nid,
+ ALLOC_DEFAULT);
+}
+
/**
* vma_alloc_folio - Allocate a folio for a VMA.
* @gfp: GFP flags.
@@ -2586,7 +2606,7 @@ struct page *alloc_frozen_pages_noprof(gfp_t gfp, unsigned order)
pol = get_task_policy(current);

return alloc_pages_mpol(gfp, order, pol, NO_INTERLEAVE_INDEX,
- numa_node_id());
+ numa_node_id(), ALLOC_DEFAULT);
}

/**
diff --git a/mm/mempolicy.h b/mm/mempolicy.h
new file mode 100644
index 000000000000..8d2c07868f6e
--- /dev/null
+++ b/mm/mempolicy.h
@@ -0,0 +1,28 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * mm-internal API for mempolicy.c. Public API lives in
+ * include/linux/mempolicy.h.
+ */
+#ifndef __MM_MEMPOLICY_H
+#define __MM_MEMPOLICY_H
+
+#include <linux/gfp.h>
+#include <linux/mempolicy.h>
+
+#include "page_alloc.h"
+
+#ifdef CONFIG_NUMA
+struct folio *__folio_alloc_mpol_noprof(gfp_t gfp, unsigned int order,
+ struct mempolicy *pol, pgoff_t ilx, int nid,
+ unsigned int alloc_flags);
+#else
+static inline struct folio *__folio_alloc_mpol_noprof(gfp_t gfp,
+ unsigned int order, struct mempolicy *pol, pgoff_t ilx, int nid,
+ unsigned int alloc_flags)
+{
+ return __folio_alloc_flags_noprof(gfp, order, numa_node_id(), NULL,
+ alloc_flags);
+}
+#endif
+
+#endif
--
2.55.0