[PATCH 1/2] mm/hugetlb: Prefer mempolicy nodes for calcuating surplus hugepage reservation
From: Charles Haithcock
Date: Tue Jul 07 2026 - 14:29:36 EST
Currently, reserving surplus hugetlb pages in a mempolicy still use
global counters for calculating how many hugetlb pages to reserve.
Modify this to consider the nodes in the mempolicy first but fall back
to all possible nodes.
Suggested-by: Joshua Hahn <joshua.hahnjy@xxxxxxxxx>
Signed-off-by: Charles Haithcock <chaithco@xxxxxxxxxx>
---
- v1: Modified `needed` calculation to use `allowed_mems_nr(h)` in order
to consider free hugetlb pages in our mempolicy.
- v2: Folded in Joshua Hahn's recommendation [1] to further modify
`needed` calculation to take the max of either the available hugetlb
pages in the mempolicy or the globally available hugetlb pages. Allows
allocations to prioritize nodes in the mempolicy but can still fall
back to offnode allocations. Also added selftests to check only for
the edgecase which caused this to initially be reported and sanity
checks.
- v3: Split the change and tests into separate patches and clean up code
to better align with kernel style guidelines. Likewise, fold in Usama
Arif's correction [2].
[1] https://lore.kernel.org/all/20260602152022.2673803-1-joshua.hahnjy@xxxxxxxxx/
[2] https://lore.kernel.org/linux-mm/20260624144600.8159-1-usama.arif@xxxxxxxxx/
mm/hugetlb.c | 42 ++++++++++++++++++++++--------------------
1 file changed, 22 insertions(+), 20 deletions(-)
diff --git a/mm/hugetlb.c b/mm/hugetlb.c
index f24bf49be0..f5e678e997 100644
--- a/mm/hugetlb.c
+++ b/mm/hugetlb.c
@@ -2255,6 +2255,23 @@ static nodemask_t *policy_mbind_nodemask(gfp_t gfp)
return NULL;
}
+static unsigned int allowed_mems_nr(struct hstate *h)
+{
+ int node;
+ unsigned int nr = 0;
+ nodemask_t *mbind_nodemask;
+ unsigned int *array = h->free_huge_pages_node;
+ gfp_t gfp_mask = htlb_alloc_mask(h);
+
+ mbind_nodemask = policy_mbind_nodemask(gfp_mask);
+ for_each_node_mask(node, cpuset_current_mems_allowed) {
+ if (!mbind_nodemask || node_isset(node, *mbind_nodemask))
+ nr += array[node];
+ }
+
+ return nr;
+}
+
/*
* Increase the hugetlb pool such that it can accommodate a reservation
* of size 'delta'.
@@ -2277,7 +2294,8 @@ static int gather_surplus_pages(struct hstate *h, long delta)
alloc_nodemask = cpuset_current_mems_allowed;
lockdep_assert_held(&hugetlb_lock);
- needed = (h->resv_huge_pages + delta) - h->free_huge_pages;
+ needed = max_t(long, (delta - allowed_mems_nr(h)),
+ ((h->resv_huge_pages + delta) - h->free_huge_pages));
if (needed <= 0) {
h->resv_huge_pages += delta;
return 0;
@@ -2311,8 +2329,9 @@ static int gather_surplus_pages(struct hstate *h, long delta)
* because either resv_huge_pages or free_huge_pages may have changed.
*/
spin_lock_irq(&hugetlb_lock);
- needed = (h->resv_huge_pages + delta) -
- (h->free_huge_pages + allocated);
+ needed = max_t(long, ((delta - allowed_mems_nr(h)) - allocated),
+ ((h->resv_huge_pages + delta) -
+ (h->free_huge_pages + allocated)));
if (needed > 0) {
if (alloc_ok)
goto retry;
@@ -4513,23 +4532,6 @@ static int __init hugepage_alloc_threads_setup(char *s)
}
__setup("hugepage_alloc_threads=", hugepage_alloc_threads_setup);
-static unsigned int allowed_mems_nr(struct hstate *h)
-{
- int node;
- unsigned int nr = 0;
- nodemask_t *mbind_nodemask;
- unsigned int *array = h->free_huge_pages_node;
- gfp_t gfp_mask = htlb_alloc_mask(h);
-
- mbind_nodemask = policy_mbind_nodemask(gfp_mask);
- for_each_node_mask(node, cpuset_current_mems_allowed) {
- if (!mbind_nodemask || node_isset(node, *mbind_nodemask))
- nr += array[node];
- }
-
- return nr;
-}
-
void hugetlb_report_meminfo(struct seq_file *m)
{
struct hstate *h;
--
2.55.0