Open HugeTLB allocation routine for more generic use
Subsystems (4)
cover Open HugeTLB allocation routine for more generic use
Hi,
The motivation for this patch series is guest_memfd, which would like to use HugeTLB as a generic source of huge pages but not adopt HugeTLB's reservation at mmap() time.
By refactoring alloc_hugetlb_folio() and some dependent functions, there is now an option to allocate HugeTLB folios without providing a VMA. Specifically, HugeTLB allocation used to be dependent on the VMA to
1. Look up reservations in the resv_map
2. Get mpol, stored at vma->vm_policy
This refactoring provides hugetlb_alloc_folio(), which focuses on just the allocation itself, and associated memory and HugeTLB charging (cgroups). alloc_hugetlb_folio() still handles reservations in the resv_map and subpools.
Regarding naming, I'm definitely open to alternative names :) I chose hugetlb_alloc_folio() because I'm seeing this function as a general allocation function that is provided by the HugeTLB subsystem (hence the hugetlb_ prefix). I'm intending for alloc_hugetlb_folio() to be later refactored as a static function for use just by HugeTLB, and HugeTLBfs should probably use hugetlb_alloc_folio() directly.
I would like to get feedback on:
1. Opening up HugeTLB's allocation for more generic use
2. Reverting and re-adopting the try-commit-cancel protocol for memory charging
To see how hugetlb_alloc_folio() is used by guest_memfd, the most recent patch series that uses this more generic HugeTLB allocation routine is at [1], and a newer revision of that patch series is at [2].
Independently of guest_memfd, I believe this change is useful in simplifying alloc_hugetlb_folio(). alloc_hugetlb_folio() was so coupled to a VMA that even HugeTLBfs allocates HugeTLB folios using a pseudo-VMA.
[1] https://lore.kernel.org/all/[email protected]/T/
[2] https://github.com/googleprodkernel/linux-cc/tree/wip-gmem-conversions-hugetlb-restructuring-12-08-25
Ackerley Tng (7):
mm: hugetlb: Consolidate interpretation of gbl_chg within
alloc_hugetlb_folio()
mm: hugetlb: Move mpol interpretation out of
alloc_buddy_hugetlb_folio_with_mpol()
mm: hugetlb: Move mpol interpretation out of
dequeue_hugetlb_folio_vma()
Revert "memcg/hugetlb: remove memcg hugetlb try-commit-cancel
protocol"
mm: hugetlb: Adopt memcg try-commit-cancel protocol
mm: memcontrol: Remove now-unused function mem_cgroup_charge_hugetlb
mm: hugetlb: Refactor out hugetlb_alloc_folio()
include/linux/hugetlb.h | 11 ++ include/linux/memcontrol.h | 21 +++- mm/hugetlb.c | 228 +++++++++++++++++++++---------------- mm/memcontrol.c | 77 ++++++++----- 4 files changed, 212 insertions(+), 125 deletions(-)
base-commit: db9571a66156bfbc0273e66e5c77923869bda547 --
2.53.0.310.g728cabbaf7-goog
RFC PATCH 1/7 mm: hugetlb: Consolidate interpretation of gbl_chg within alloc_hugetlb_folio()
Previously, gbl_chg was passed from alloc_hugetlb_folio() into dequeue_hugetlb_folio_vma(), leaking the concept of gbl_chg into dequeue_hugetlb_folio_vma().
This patch consolidates the interpretation of gbl_chg into alloc_hugetlb_folio(), also renaming dequeue_hugetlb_folio_vma() to dequeue_hugetlb_folio() so dequeue_hugetlb_folio() can just focus on dequeuing a folio.
No functional change intended.
--- mm/hugetlb.c | 24 +++++++++--------------- 1 file changed, 9 insertions(+), 15 deletions(-)
mm/hugetlb.c +9 -15
@@ -1380,7 +1380,7 @@ static unsigned long available_huge_pages(struct hstate *h)static struct folio *dequeue_hugetlb_folio_vma (struct hstate *h,struct vm_area_struct *vma, -unsigned long address,long gbl_chg) +unsigned long address) {struct folio *folio =NULL ;struct mempolicy *mpol;
@@ -1388,13 +1388,6 @@ static struct folio *dequeue_hugetlb_folio_vma(struct hstate *h,nodemask_t *nodemask;int nid; -/* -* gbl_chg==1 means the allocation requires a new page that was not -* reserved before. Making sure there's at least one free page. -*/ -if (gbl_chg && !available_huge_pages (h)) - goto err; - gfp_mask =htlb_alloc_mask (h); nid =huge_node (vma, address, gfp_mask, &mpol, &nodemask);
@@ -1412,9 +1405,6 @@ static struct folio *dequeue_hugetlb_folio_vma(struct hstate *h,mpol_cond_put (mpol);return folio; - -err: -return NULL ; }#ifdef CONFIG_ARCH_HAS_GIGANTIC_PAGE
@@ -2962,12 +2952,16 @@ struct folio *alloc_hugetlb_folio(struct vm_area_struct *vma, goto out_uncharge_cgroup_reservation;spin_lock_irq (&hugetlb_lock); +/* -* glb_chg is passed to indicate whether or not a page must be taken -* from the global free pool (global change). gbl_chg == 0 indicates -* a reservation exists for the allocation. +* gbl_chg == 0 indicates a reservation exists for the allocation - so +* try dequeuing a page. If there are available_huge_pages(), try using +* them! */ - folio =dequeue_hugetlb_folio_vma (h, vma, addr, gbl_chg); + folio =NULL ; +if (!gbl_chg ||available_huge_pages (h)) + folio =dequeue_hugetlb_folio_vma (h, vma, addr); +if (!folio) {spin_unlock_irq (&hugetlb_lock); folio =alloc_buddy_hugetlb_folio_with_mpol (h, vma, addr);
Signature · 1 line
2.53.0.310.g728cabbaf7-goog
Re: [RFC PATCH v1 1/7] mm: hugetlb: Consolidate interpretation of gbl_chg within alloc_hugetlb_folio()
Quoted 9 lines · On Wed, 11 Feb 2026 16:37:12 -0800 Ackerley Tng <[email protected]> wrote:
Previously,
gbl_chgwas passed fromalloc_hugetlb_folio()intodequeue_hugetlb_folio_vma(), leaking the concept ofgbl_chgintodequeue_hugetlb_folio_vma().This patch consolidates the interpretation of
gbl_chgintoalloc_hugetlb_folio(), also renamingdequeue_hugetlb_folio_vma()todequeue_hugetlb_folio()sodequeue_hugetlb_folio()can just focus on dequeuing a folio.
Quoted 4 lines
No functional change intended.
Signed-off-by Ackerley Tng <[email protected]>Reviewed-by James Houghton <[email protected]>
Makes sense to me, this seems like a reasonable semantic change even without factoring out hugetlb_alloc_folio. Thank you!
Quoted 66 lines
--- mm/hugetlb.c | 24 +++++++++--------------- 1 file changed, 9 insertions(+), 15 deletions(-)mm/hugetlb.c +9
-15@@ -1380,7 +1380,7 @@ static unsigned long available_huge_pages(struct hstate *h)static struct folio *dequeue_hugetlb_folio_vma (struct hstate *h,struct vm_area_struct *vma, -unsigned long address,long gbl_chg) +unsigned long address) {struct folio *folio =NULL ;struct mempolicy *mpol;@@ -1388,13 +1388,6 @@ static struct folio *dequeue_hugetlb_folio_vma(struct hstate *h,nodemask_t *nodemask;int nid; -/* -* gbl_chg==1 means the allocation requires a new page that was not -* reserved before. Making sure there's at least one free page. -*/ -if (gbl_chg && !available_huge_pages (h)) - goto err; - gfp_mask =htlb_alloc_mask (h); nid =huge_node (vma, address, gfp_mask, &mpol, &nodemask);@@ -1412,9 +1405,6 @@ static struct folio *dequeue_hugetlb_folio_vma(struct hstate *h,mpol_cond_put (mpol);return folio; - -err: -return NULL ; }#ifdef CONFIG_ARCH_HAS_GIGANTIC_PAGE @@ -2962,12 +2952,16 @@ struct folio *alloc_hugetlb_folio(struct vm_area_struct *vma, goto out_uncharge_cgroup_reservation;spin_lock_irq (&hugetlb_lock); +/* -* glb_chg is passed to indicate whether or not a page must be taken -* from the global free pool (global change). gbl_chg == 0 indicates -* a reservation exists for the allocation. +* gbl_chg == 0 indicates a reservation exists for the allocation - so +* try dequeuing a page. If there are available_huge_pages(), try using +* them! */ - folio =dequeue_hugetlb_folio_vma (h, vma, addr, gbl_chg); + folio =NULL ; +if (!gbl_chg ||available_huge_pages (h)) + folio =dequeue_hugetlb_folio_vma (h, vma, addr); +if (!folio) {spin_unlock_irq (&hugetlb_lock); folio =alloc_buddy_hugetlb_folio_with_mpol (h, vma, addr);Signature · 1 line
2.53.0.310.g728cabbaf7-goog
RFC PATCH 2/7 mm: hugetlb: Move mpol interpretation out of alloc_buddy_hugetlb_folio_with_mpol()
In reply to Ackerley TngMove memory policy interpretation out of alloc_buddy_hugetlb_folio_with_mpol() and into alloc_hugetlb_folio() to separate reading and interpretation of memory policy from actual allocation.
This will later allow memory policy to be interpreted outside of the process of allocating a hugetlb folio entirely. This opens doors for other callers of the HugeTLB folio allocation function, such as guest_memfd, where memory may not always be mapped and hence may not have an associated vma.
No functional change intended.
--- mm/hugetlb.c | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-)
mm/hugetlb.c +11 -9
@@ -2223,15 +2223,11 @@ static struct folio *alloc_migrate_hugetlb_folio(struct hstate *h, gfp_t gfp_mas*/ static struct folio *alloc_buddy_hugetlb_folio_with_mpol (struct hstate *h, -struct vm_area_struct *vma,unsigned long addr) +struct mempolicy *mpol,int nid,nodemask_t *nodemask) {struct folio *folio =NULL ; -struct mempolicy *mpol;gfp_t gfp_mask =htlb_alloc_mask (h); -int nid; -nodemask_t *nodemask; - nid =huge_node (vma, addr, gfp_mask, &mpol, &nodemask);if (mpol_is_preferred_many (mpol)) {gfp_t gfp = gfp_mask & ~(__GFP_DIRECT_RECLAIM | __GFP_NOFAIL);
@@ -2243,7 +2239,7 @@ struct folio *alloc_buddy_hugetlb_folio_with_mpol(struct hstate *h,if (!folio) folio =alloc_surplus_hugetlb_folio (h, gfp_mask, nid, nodemask); -mpol_cond_put (mpol); +return folio; }
@@ -2892,7 +2888,7 @@ struct folio *alloc_hugetlb_folio(struct vm_area_struct *vma,map_chg_state map_chg;int ret, idx;struct hugetlb_cgroup *h_cg =NULL ; -gfp_t gfp =htlb_alloc_mask (h) | __GFP_RETRY_MAYFAIL; +gfp_t gfp =htlb_alloc_mask (h); idx =hstate_index (h);
@@ -2963,8 +2959,14 @@ struct folio *alloc_hugetlb_folio(struct vm_area_struct *vma, folio =dequeue_hugetlb_folio_vma (h, vma, addr);if (!folio) { +struct mempolicy *mpol; +nodemask_t *nodemask; +int nid; +spin_unlock_irq (&hugetlb_lock); - folio =alloc_buddy_hugetlb_folio_with_mpol (h, vma, addr); + nid =huge_node (vma, addr, gfp, &mpol, &nodemask); + folio =alloc_buddy_hugetlb_folio_with_mpol (h, mpol, nid, nodemask); +mpol_cond_put (mpol);if (!folio) goto out_uncharge_cgroup;spin_lock_irq (&hugetlb_lock);
@@ -3023,7 +3025,7 @@ struct folio *alloc_hugetlb_folio(struct vm_area_struct *vma, } } - ret =mem_cgroup_charge_hugetlb (folio, gfp); + ret =mem_cgroup_charge_hugetlb (folio, gfp | __GFP_RETRY_MAYFAIL);/* * Unconditionally increment NR_HUGETLB here. If it turns out that * mem_cgroup_charge_hugetlb failed, then immediately free the page and
Signature · 1 line
2.53.0.310.g728cabbaf7-goog
Re: [RFC PATCH v1 2/7] mm: hugetlb: Move mpol interpretation out of alloc_buddy_hugetlb_folio_with_mpol()
Quoted 12 lines · Ackerley Tng
Move memory policy interpretation out of
alloc_buddy_hugetlb_folio_with_mpol()and intoalloc_hugetlb_folio()to separate reading and interpretation of memory policy from actual allocation.This will later allow memory policy to be interpreted outside of the process of allocating a hugetlb folio entirely. This opens doors for other callers of the HugeTLB folio allocation function, such as
guest_memfd, where memory may not always be mapped and hence may not have an associated vma.
Quoted 3 lines
No functional change intended.
Signed-off-by Ackerley Tng <[email protected]>
RFC PATCH 3/7 mm: hugetlb: Move mpol interpretation out of dequeue_hugetlb_folio_vma()
In reply to Ackerley TngMove memory policy interpretation out of dequeue_hugetlb_folio_vma() and into alloc_hugetlb_folio() to separate reading and interpretation of memory policy from actual allocation.
Also rename dequeue_hugetlb_folio_vma() to dequeue_hugetlb_folio_with_mpol() to remove association with vma and to align with alloc_buddy_hugetlb_folio_with_mpol().
This will later allow memory policy to be interpreted outside of the process of allocating a hugetlb folio entirely. This opens doors for other callers of the HugeTLB folio allocation function, such as guest_memfd, where memory may not always be mapped and hence may not have an associated vma.
No functional change intended.
--- mm/hugetlb.c | 34 +++++++++++++++------------------- 1 file changed, 15 insertions(+), 19 deletions(-)
mm/hugetlb.c +15 -19
@@ -1378,18 +1378,11 @@ static unsigned long available_huge_pages(struct hstate *h)return h->free_huge_pages - h->resv_huge_pages; } -static struct folio *dequeue_hugetlb_folio_vma (struct hstate *h, -struct vm_area_struct *vma, -unsigned long address) +static struct folio *dequeue_hugetlb_folio_with_mpol (struct hstate *h, +struct mempolicy *mpol,int nid,nodemask_t *nodemask) {struct folio *folio =NULL ; -struct mempolicy *mpol; -gfp_t gfp_mask; -nodemask_t *nodemask; -int nid; - - gfp_mask =htlb_alloc_mask (h); - nid =huge_node (vma, address, gfp_mask, &mpol, &nodemask); +gfp_t gfp_mask =htlb_alloc_mask (h);if (mpol_is_preferred_many (mpol)) { folio = dequeue_hugetlb_folio_nodemask(h, gfp_mask,
@@ -1403,7 +1396,6 @@ static struct folio *dequeue_hugetlb_folio_vma(struct hstate *h, folio =dequeue_hugetlb_folio_nodemask (h, gfp_mask, nid, nodemask); -mpol_cond_put (mpol);return folio; }
@@ -2889,6 +2881,9 @@ struct folio *alloc_hugetlb_folio(struct vm_area_struct *vma,int ret, idx;struct hugetlb_cgroup *h_cg =NULL ;gfp_t gfp =htlb_alloc_mask (h); +struct mempolicy *mpol; +nodemask_t *nodemask; +int nid; idx =hstate_index (h);
@@ -2949,6 +2944,9 @@ struct folio *alloc_hugetlb_folio(struct vm_area_struct *vma,spin_lock_irq (&hugetlb_lock); +/* Takes reference on mpol. */ + nid =huge_node (vma, addr, gfp, &mpol, &nodemask); +/* * gbl_chg == 0 indicates a reservation exists for the allocation - so * try dequeuing a page. If there are available_huge_pages(), try using
@@ -2956,25 +2954,23 @@ struct folio *alloc_hugetlb_folio(struct vm_area_struct *vma,*/ folio =NULL ;if (!gbl_chg ||available_huge_pages (h)) - folio =dequeue_hugetlb_folio_vma (h, vma, addr); + folio =dequeue_hugetlb_folio_with_mpol (h, mpol, nid, nodemask);if (!folio) { -struct mempolicy *mpol; -nodemask_t *nodemask; -int nid; -spin_unlock_irq (&hugetlb_lock); - nid =huge_node (vma, addr, gfp, &mpol, &nodemask); folio =alloc_buddy_hugetlb_folio_with_mpol (h, mpol, nid, nodemask); -mpol_cond_put (mpol); -if (!folio) +if (!folio) { +mpol_cond_put (mpol); goto out_uncharge_cgroup; + }spin_lock_irq (&hugetlb_lock);list_add (&folio->lru, &h->hugepage_activelist);folio_ref_unfreeze (folio,1 );/* Fall through */ } +mpol_cond_put (mpol); +/* * Either dequeued or buddy-allocated folio needs to add special * mark to the folio when it consumes a global reservation.
Signature · 1 line
2.53.0.310.g728cabbaf7-goog
Re: [RFC PATCH v1 3/7] mm: hugetlb: Move mpol interpretation out of dequeue_hugetlb_folio_vma()
Quoted 15 lines · Ackerley Tng
Move memory policy interpretation out of
dequeue_hugetlb_folio_vma()and intoalloc_hugetlb_folio()to separate reading and interpretation of memory policy from actual allocation.Also rename
dequeue_hugetlb_folio_vma()todequeue_hugetlb_folio_with_mpol()to remove association with vma and to align withalloc_buddy_hugetlb_folio_with_mpol().This will later allow memory policy to be interpreted outside of the process of allocating a hugetlb folio entirely. This opens doors for other callers of the HugeTLB folio allocation function, such as
guest_memfd, where memory may not always be mapped and hence may not have an associated vma.
Quoted 3 lines
No functional change intended.
Signed-off-by Ackerley Tng <[email protected]>
RFC PATCH 4/7 Revert "memcg/hugetlb: remove memcg hugetlb try-commit-cancel protocol"
In reply to Ackerley TngThis reverts commit 1d8f136a421f26747e58c01281cba5bffae8d289.
Restore try-commit-cancel protocol for memory charging for HugeTLB, to be used in later patches.
--- include/linux/memcontrol.h | 22 +++++++++++++ mm/memcontrol.c | 65 ++++++++++++++++++++++++++++++++++++-- 2 files changed, 84 insertions(+), 3 deletions(-)
include/linux/memcontrol.h +22 -0
@@ -639,6 +639,8 @@ static inline bool mem_cgroup_below_min(struct mem_cgroup *target,page_counter_read (&memcg->memory); } +void mem_cgroup_commit_charge (struct folio *folio,struct mem_cgroup *memcg); +int __mem_cgroup_charge (struct folio *folio,struct mm_struct *mm,gfp_t gfp);/**
@@ -663,6 +665,9 @@ static inline int mem_cgroup_charge(struct folio *folio, struct mm_struct *mm,return __mem_cgroup_charge (folio, mm, gfp); } +int mem_cgroup_hugetlb_try_charge (struct mem_cgroup *memcg,gfp_t gfp, +long nr_pages); +int mem_cgroup_charge_hugetlb (struct folio * folio,gfp_t gfp); int mem_cgroup_swapin_charge_folio(struct folio *folio, struct mm_struct *mm,
@@ -691,6 +696,7 @@ static inline void mem_cgroup_uncharge_folios(struct folio_batch *folios)__mem_cgroup_uncharge_folios (folios); } +void mem_cgroup_cancel_charge (struct mem_cgroup *memcg,unsigned int nr_pages);void mem_cgroup_replace_folio (struct folio *old,struct folio *new);void mem_cgroup_migrate (struct folio *old,struct folio *new);
@@ -1135,12 +1141,23 @@ static inline bool mem_cgroup_below_min(struct mem_cgroup *target,return false; } +static inline void mem_cgroup_commit_charge (struct folio *folio, +struct mem_cgroup *memcg) +{ +} +static inline int mem_cgroup_charge (struct folio *folio,struct mm_struct *mm,gfp_t gfp) {return 0 ; } +static inline int mem_cgroup_hugetlb_try_charge (struct mem_cgroup *memcg, +gfp_t gfp,long nr_pages) +{ +return 0 ; +} +static inline int mem_cgroup_charge_hugetlb (struct folio * folio,gfp_t gfp) {return 0 ;
@@ -1160,6 +1177,11 @@ static inline void mem_cgroup_uncharge_folios(struct folio_batch *folios) { } +static inline void mem_cgroup_cancel_charge (struct mem_cgroup *memcg, +unsigned int nr_pages) +{ +} +static inline void mem_cgroup_replace_folio (struct folio *old,struct folio *new) {
mm/memcontrol.c +62 -3
89 lines not on this page · raw message
Signature · 1 line
2.53.0.310.g728cabbaf7-goog
RFC PATCH 5/7 mm: hugetlb: Adopt memcg try-commit-cancel protocol
In reply to Ackerley TngRefactor alloc_hugetlb_folio() to use the memcg try-commit-cancel protocol.
Do this to allow the core of allocating a hugetlb folio and associated memcg charging to be refactored out in a later patch.
In addition, checking cgroup memory limits before allocating avoids unnecessary allocation if the limits had already been hit.
Update error code propagation in the failure paths so that existing error cases still return -ENOSPC, but if the memory limit is reached, return -ENOMEM as before.
--- mm/hugetlb.c | 53 +++++++++++++++++++++++++++++++++------------------- 1 file changed, 34 insertions(+), 19 deletions(-)
mm/hugetlb.c +34 -19
@@ -2881,6 +2881,8 @@ struct folio *alloc_hugetlb_folio(struct vm_area_struct *vma,int ret, idx;struct hugetlb_cgroup *h_cg =NULL ;gfp_t gfp =htlb_alloc_mask (h); +bool memory_charged = false; +struct mem_cgroup *memcg;struct mempolicy *mpol;nodemask_t *nodemask;int nid;
@@ -2917,8 +2919,10 @@ struct folio *alloc_hugetlb_folio(struct vm_area_struct *vma,*/ if (map_chg) { gbl_chg =hugepage_subpool_get_pages (spool,1 ); -if (gbl_chg <0 ) +if (gbl_chg <0 ) { + ret = -ENOSPC ; goto out_end_reservation; + } }else {/* * If we have the vma reservation ready, no need for extra
@@ -2934,13 +2938,25 @@ struct folio *alloc_hugetlb_folio(struct vm_area_struct *vma,if (map_chg) { ret =hugetlb_cgroup_charge_cgroup_rsvd ( idx,pages_per_huge_page (h), &h_cg); -if (ret) +if (ret) { + ret = -ENOSPC ; goto out_subpool_put; + } } ret =hugetlb_cgroup_charge_cgroup (idx,pages_per_huge_page (h), &h_cg); -if (ret) +if (ret) { + ret = -ENOSPC ; goto out_uncharge_cgroup_reservation; + } + + memcg =get_mem_cgroup_from_current (); + ret =mem_cgroup_hugetlb_try_charge (memcg, gfp | __GFP_RETRY_MAYFAIL, +pages_per_huge_page (h)); +if (ret == -ENOMEM ) + goto out_put_memcg; + + memory_charged = !ret;spin_lock_irq (&hugetlb_lock);
@@ -2961,7 +2977,8 @@ struct folio *alloc_hugetlb_folio(struct vm_area_struct *vma, folio =alloc_buddy_hugetlb_folio_with_mpol (h, mpol, nid, nodemask);if (!folio) {mpol_cond_put (mpol); - goto out_uncharge_cgroup; + ret = -ENOSPC ; + goto out_uncharge_memory; }spin_lock_irq (&hugetlb_lock);list_add (&folio->lru, &h->hugepage_activelist);
@@ -2991,6 +3008,12 @@ struct folio *alloc_hugetlb_folio(struct vm_area_struct *vma,spin_unlock_irq (&hugetlb_lock); +lruvec_stat_mod_folio (folio,NR_HUGETLB ,pages_per_huge_page (h)); + +if (memory_charged) +mem_cgroup_commit_charge (folio, memcg); +mem_cgroup_put (memcg); +hugetlb_set_folio_subpool (folio, spool);if (map_chg !=MAP_CHG_ENFORCED ) {
@@ -3021,22 +3044,14 @@ struct folio *alloc_hugetlb_folio(struct vm_area_struct *vma, } } - ret =mem_cgroup_charge_hugetlb (folio, gfp | __GFP_RETRY_MAYFAIL); -/* -* Unconditionally increment NR_HUGETLB here. If it turns out that -* mem_cgroup_charge_hugetlb failed, then immediately free the page and -* decrement NR_HUGETLB. -*/ -lruvec_stat_mod_folio (folio,NR_HUGETLB ,pages_per_huge_page (h)); - -if (ret == -ENOMEM ) { -free_huge_folio (folio); -return ERR_PTR (-ENOMEM ); - } -return folio; -out_uncharge_cgroup: +out_uncharge_memory: +if (memory_charged) +mem_cgroup_cancel_charge (memcg,pages_per_huge_page (h)); +out_put_memcg: +mem_cgroup_put (memcg); +hugetlb_cgroup_uncharge_cgroup (idx,pages_per_huge_page (h), h_cg); out_uncharge_cgroup_reservation:if (map_chg)
@@ -3056,7 +3071,7 @@ struct folio *alloc_hugetlb_folio(struct vm_area_struct *vma, out_end_reservation:if (map_chg !=MAP_CHG_ENFORCED )vma_end_reservation (h, vma, addr); -return ERR_PTR (-ENOSPC ); +return ERR_PTR (ret); }static __init void *alloc_bootmem (struct hstate *h,int nid,bool node_exact)
Signature · 1 line
2.53.0.310.g728cabbaf7-goog
RFC PATCH 6/7 mm: memcontrol: Remove now-unused function mem_cgroup_charge_hugetlb
In reply to Ackerley TngWith the (re)introduction of the try-commit-cancel charging protocol for HugeTLB's use, mem_cgroup_charge_hugetlb() is now redundant.
Remove the function's implementation from mm/memcontrol.c and its declaration from include/linux/memcontrol.h
No functional change intended.
--- include/linux/memcontrol.h | 7 ------- mm/memcontrol.c | 34 ---------------------------------- 2 files changed, 41 deletions(-)
include/linux/memcontrol.h +0 -7
@@ -668,8 +668,6 @@ static inline int mem_cgroup_charge(struct folio *folio, struct mm_struct *mm,int mem_cgroup_hugetlb_try_charge (struct mem_cgroup *memcg,gfp_t gfp,long nr_pages); -int mem_cgroup_charge_hugetlb (struct folio * folio,gfp_t gfp); -int mem_cgroup_swapin_charge_folio (struct folio *folio,struct mm_struct *mm,gfp_t gfp,swp_entry_t entry);
@@ -1158,11 +1156,6 @@ static inline int mem_cgroup_hugetlb_try_charge(struct mem_cgroup *memcg,return 0 ; } -static inline int mem_cgroup_charge_hugetlb (struct folio * folio,gfp_t gfp) -{ -return 0 ; -} -static inline int mem_cgroup_swapin_charge_folio (struct folio *folio,struct mm_struct *mm,gfp_t gfp,swp_entry_t entry) {
mm/memcontrol.c +0 -34
@@ -4855,40 +4855,6 @@ int mem_cgroup_hugetlb_try_charge(struct mem_cgroup *memcg, gfp_t gfp,return 0 ; } -/** -* mem_cgroup_charge_hugetlb - charge the memcg for a hugetlb folio -* @folio: folio being charged -* @gfp: reclaim mode -* -* This function is called when allocating a huge page folio, after the page has -* already been obtained and charged to the appropriate hugetlb cgroup -* controller (if it is enabled). -* -* Returns ENOMEM if the memcg is already full. -* Returns 0 if either the charge was successful, or if we skip the charging. -*/ -int mem_cgroup_charge_hugetlb (struct folio *folio,gfp_t gfp) -{ -struct mem_cgroup *memcg =get_mem_cgroup_from_current (); -int ret =0 ; - -/* -* Even memcg does not account for hugetlb, we still want to update -* system-level stats via lruvec_stat_mod_folio. Return 0, and skip -* charging the memcg. -*/ -if (mem_cgroup_disabled () || !memcg_accounts_hugetlb () || - !memcg || !cgroup_subsys_on_dfl (memory_cgrp_subsys)) - goto out; - -if (charge_memcg (folio, memcg, gfp)) - ret = -ENOMEM ; - -out: -mem_cgroup_put (memcg); -return ret; -} -/** * mem_cgroup_swapin_charge_folio - Charge a newly allocated folio for swapin. * @folio: folio to charge.
Signature · 1 line
2.53.0.310.g728cabbaf7-goog
RFC PATCH 7/7 mm: hugetlb: Refactor out hugetlb_alloc_folio()
In reply to Ackerley TngRefactor out hugetlb_alloc_folio() from alloc_hugetlb_folio(), which handles allocation of a folio and memory and HugeTLB charging to cgroups.
Other than flags to control charging, hugetlb_alloc_folio() also takes parameters for memory policy and memcg to charge memory to.
This refactoring decouples the HugeTLB page allocation from VMAs, specifically:
1. Reservations (as in resv_map) are stored in the vma
2. mpol is stored at vma->vm_policy
3. A vma must be used for allocation even if the pages are not meant to be used by host process.
Without this coupling, VMAs are no longer a requirement for allocation. This opens up the allocation routine for usage without VMAs, which will allow guest_memfd to use HugeTLB as a more generic allocator of huge pages, since guest_memfd memory may not have any associated VMAs by design. In addition, direct allocations from HugeTLB could possibly be refactored to avoid the use of a pseudo-VMA.
Also, this decouples HugeTLB page allocation from HugeTLBfs, where the subpool is stored at the fs mount. This is also a requirement for guest_memfd, where the plan is to have a subpool created per-fd and stored on the inode.
No functional change intended.
--- include/linux/hugetlb.h | 11 +++ mm/hugetlb.c | 201 +++++++++++++++++++++++----------------- 2 files changed, 126 insertions(+), 86 deletions(-)
include/linux/hugetlb.h +11 -0
@@ -704,6 +704,9 @@ bool hugetlb_bootmem_page_zones_valid(int nid, struct huge_bootmem_page *m);int isolate_or_dissolve_huge_folio (struct folio *folio,struct list_head *list);int replace_free_hugepage_folios (unsigned long start_pfn,unsigned long end_pfn);void wait_for_freed_hugetlb_folios (void ); +struct folio *hugetlb_alloc_folio (struct hstate *h,struct mempolicy *mpol, +int nid,nodemask_t *nodemask,struct mem_cgroup *memcg, +bool charge_hugetlb_rsvd,bool use_existing_reservation);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,
@@ -1115,6 +1118,14 @@ static inline void wait_for_freed_hugetlb_folios(void) { } +static inline struct folio *hugetlb_alloc_folio (struct hstate *h, +struct mempolicy *mpol,int nid,nodemask_t *nodemask, +struct mem_cgroup *memcg,bool charge_hugetlb_rsvd, +bool use_existing_reservation) +{ +return NULL ; +} +static inline struct folio *alloc_hugetlb_folio (struct vm_area_struct *vma,unsigned long addr,bool cow_from_owner)
mm/hugetlb.c +115 -86
258 lines not on this page · raw message
Signature · 1 line
2.53.0.310.g728cabbaf7-goog
On Wed, 11 Feb 2026 16:37:11 -0800 Ackerley Tng <[email protected]> wrote:
Hi Ackerly, I hope you're donig well!
[...snip...]
Quoted 3 lines
I would like to get feedback on:
1. Opening up HugeTLB's allocation for more generic use
I'm not entirely familiar with guest_memfd, so pleae excuse my ignorance if I'm missing anything obvious. But I'm wondering what hugeTLB offers that other hugepage solutions cannot offer for guest_memfd, if the goal of this series is to decouple it from hugeTLBfs.
Quoted 2 lines
2. Reverting and re-adopting the try-commit-cancel protocol for memory charging
On the second point, I am wondering if reintroducing the try-commit-cancel protocol is tied to factoring out hugetlb_alloc_folio. When I removed the protocol a while back, the justification was that for the most part, grabbing a hugetlb folio was a relatively cheap & fast operation, since hugetlb mostly operates out of a preallocated pool.
So the cost of being wrong, going above the limit, and having to return the hugetlb folio was also relatively low.
It seems like this patch series introduces some new paths for hugetlb pages to be consumed (specifically, without a reservation or vma). I imagine that these new paths make the slowpath for hugetlb more frequent, which makes the cost of assuming that the memcg limit is OK higher? I think explicitly spelling this out in the justification for reintroducing the charging protocol could be helpful.
Thank you for the series, again. I hope you have a great day! Joshua
Quoted 37 lines
To see how
hugetlb_alloc_folio()is used byguest_memfd, the most recent patch series that uses this more generic HugeTLB allocation routine is at [1], and a newer revision of that patch series is at [2].Independently of
guest_memfd, I believe this change is useful in simplifyingalloc_hugetlb_folio().alloc_hugetlb_folio()was so coupled to a VMA that even HugeTLBfs allocates HugeTLB folios using a pseudo-VMA.[1] https://lore.kernel.org/all/[email protected]/T/
[2] https://github.com/googleprodkernel/linux-cc/tree/wip-gmem-conversions-hugetlb-restructuring-12-08-25Ackerley Tng (7):
mm: hugetlb: Consolidate interpretation ofgbl_chgwithin
alloc_hugetlb_folio()
mm: hugetlb: Move mpol interpretation out of
alloc_buddy_hugetlb_folio_with_mpol()
mm: hugetlb: Move mpol interpretation out of
dequeue_hugetlb_folio_vma()
Revert "memcg/hugetlb: remove memcg hugetlb try-commit-cancel
protocol"
mm: hugetlb: Adopt memcg try-commit-cancel protocol
mm: memcontrol: Remove now-unused functionmem_cgroup_charge_hugetlb
mm: hugetlb: Refactor outhugetlb_alloc_folio()include/linux/hugetlb.h | 11 ++ include/linux/memcontrol.h | 21 +++- mm/hugetlb.c | 228 +++++++++++++++++++++---------------- mm/memcontrol.c | 77 ++++++++----- 4 files changed, 212 insertions(+), 125 deletions(-)base-commit:
db9571a66156bfbc0273e66e5c77923869bda547--
2.53.0.310.g728cabbaf7-goog
Quoted 4 lines · Joshua Hahn
On Wed, 11 Feb 2026 16:37:11 -0800 Ackerley Tng <[email protected]> wrote:
Hi Ackerly, I hope you're donig well!
Quoted 8 lines
[...snip...]
Quoted 3 lines
I would like to get feedback on:
1. Opening up HugeTLB's allocation for more generic use
I'm not entirely familiar with
guest_memfd, so pleae excuse my ignorance if I'm missing anything obvious.
Happy to take questions! Thank you for your thoughts and reviews!
Quoted 4 lines
But I'm wondering what hugeTLB offers
that other hugepage solutions cannot offer forguest_memfd, if the goal of this series is to decouple it from hugeTLBfs.
The one other huge page source that we've explored is THP pages from the buddy allocator. Compared to HugeTLB, huge pages from the buddy allocator
+ Has a maximum size of 2M
+ Does not guarantee huge pages the way HugeTLB does - HugeTLB pages are allocated at boot, and guest_memfd can reserve pages at guest_memfd creation time.
+ Allocation of HugeTLB pages is also really fast, it's just dequeuing from a preallocated pool
The last reason to use HugeTLB is not because of any inherent advantage of using HugeTLB over other sources of huge pages, but for administrative/scheduling purposes:
Given that existing non-guest_memfd workloads are already using HugeTLB, for optimal scheduling, machine memory is already carved up in HugeTLB pages for these workloads. Workloads that require using guest_memfd (like Confidential VMs) must also use HugeTLB to participate in optimial workload scheduling across machines.
Quoted 12 lines
Quoted 2 lines
2. Reverting and re-adopting the try-commit-cancel protocol for memory charging
On the second point, I am wondering if reintroducing the try-commit-cancel protocol is tied to factoring out
hugetlb_alloc_folio. When I removed the protocol a while back, the justification was that for the most part, grabbing a hugetlb folio was a relatively cheap & fast operation, since hugetlb mostly operates out of a preallocated pool.So the cost of being wrong, going above the limit, and having to return the hugetlb folio was also relatively low.
Thanks for this! I saw your patch to just optimistically grab a HugeTLB page :) For that patch, the primary reason was to simplify the logic, and the simplification was justifiable because grabbing a folio is cheap, right? (And so grabbing a folio being cheap wasn't a reason in itself?)
Quoted 7 lines
It seems like this patch series introduces some new paths for hugetlb pages to be consumed (specifically, without a reservation or vma). I imagine that these new paths make the slowpath for hugetlb more frequent, which makes the cost of assuming that the memcg limit is OK higher? I think explicitly spelling this out in the justification for reintroducing the charging protocol could be helpful.
Yes, I should have done that. Will copy the following to the next revision.
The main reason is that reintroducing the charging protocol is the clearest way (for me) to cleanly refactor out hugetlb_alloc_folio() without worrying about the edge cases around HugeTLB reservations and charging.
If I didn't reintroduce the charging protocol, I would have to depend on freeing the new hugetlb folio on memcg charging failure, and the freeing in turn depends on the subpool correctly being set in the folio, and the presence of the subpool influences (in free_huge_folio()) whether the reservation was returned to the global hstate. Aaannnd... there's also a hugetlb_restore_reserve flag that controls whether to return the folio to the subpool (and the hstate). I find folio_clear_hugetlb_restore_reserve() on certain code paths kind of magical/unexplained too.
I would rather iron out those charging and reservation details separately from this series (with more testing support).
On the other hand, reintroducing the charging protocol has the benefit of avoiding allocations (not just dequeuing, if surplus HugeTLB pages are required) if the memcg limit is hit. Also, if the original reason for removing the protocol was to simplify the code, refactoring out hugetlb_alloc_folio() also simplifies the code, and I think it's actually nice that memcg charging is done the same way as the other two (h_cg and h_cg_rsvd charging). After hugetlb_alloc_folio() is refactored out, the gotos make all three charging systems consistent and symmetric, which I think is nice to have :)
I hope the consistent/symmetric charging among all 3 systems is welcome, what do you think?
Quoted 40 lines
Thank you for the series, again. I hope you have a great day! Joshua
Quoted 37 lines
To see how
hugetlb_alloc_folio()is used byguest_memfd, the most recent patch series that uses this more generic HugeTLB allocation routine is at [1], and a newer revision of that patch series is at [2].Independently of
guest_memfd, I believe this change is useful in simplifyingalloc_hugetlb_folio().alloc_hugetlb_folio()was so coupled to a VMA that even HugeTLBfs allocates HugeTLB folios using a pseudo-VMA.[1] https://lore.kernel.org/all/[email protected]/T/
[2] https://github.com/googleprodkernel/linux-cc/tree/wip-gmem-conversions-hugetlb-restructuring-12-08-25Ackerley Tng (7):
mm: hugetlb: Consolidate interpretation ofgbl_chgwithin
alloc_hugetlb_folio()
mm: hugetlb: Move mpol interpretation out of
alloc_buddy_hugetlb_folio_with_mpol()
mm: hugetlb: Move mpol interpretation out of
dequeue_hugetlb_folio_vma()
Revert "memcg/hugetlb: remove memcg hugetlb try-commit-cancel
protocol"
mm: hugetlb: Adopt memcg try-commit-cancel protocol
mm: memcontrol: Remove now-unused functionmem_cgroup_charge_hugetlb
mm: hugetlb: Refactor outhugetlb_alloc_folio()include/linux/hugetlb.h | 11 ++ include/linux/memcontrol.h | 21 +++- mm/hugetlb.c | 228 +++++++++++++++++++++---------------- mm/memcontrol.c | 77 ++++++++----- 4 files changed, 212 insertions(+), 125 deletions(-)base-commit:
db9571a66156bfbc0273e66e5c77923869bda547--
2.53.0.310.g728cabbaf7-goog
Quoted 8 lines · On Wed, 25 Feb 2026 19:37:04 -0800 Ackerley Tng <[email protected]> wrote:
Quoted 6 lines · Joshua Hahn
On Wed, 11 Feb 2026 16:37:11 -0800 Ackerley Tng <[email protected]> wrote:
Hi Ackerly, I hope you're donig well!
[...snip...]
Quoted 8 lines
Quoted 6 lines
Quoted 3 lines
I would like to get feedback on:
1. Opening up HugeTLB's allocation for more generic use
I'm not entirely familiar with
guest_memfd, so pleae excuse my ignorance if I'm missing anything obvious.Happy to take questions! Thank you for your thoughts and reviews!
Of course, thank you for your work, Ackerley!
Quoted 9 lines
Quoted 4 lines
But I'm wondering what hugeTLB offers
that other hugepage solutions cannot offer forguest_memfd, if the goal of this series is to decouple it from hugeTLBfs.The one other huge page source that we've explored is THP pages from the buddy allocator. Compared to HugeTLB, huge pages from the buddy allocator
Quoted 6 lines
+ Has a maximum size of 2M
+ Does not guarantee huge pages the way HugeTLB does - HugeTLB pages are allocated at boot, andguest_memfdcan reserve pages atguest_memfdcreation time.
+ Allocation of HugeTLB pages is also really fast, it's just dequeuing from a preallocated pool
All of these make sense. Just wanted to know if guest_memfd had any unique usecases for hugeTLB that normal hugetlbfs didn't have.
Quoted 23 lines
The last reason to use HugeTLB is not because of any inherent advantage of using HugeTLB over other sources of huge pages, but for administrative/scheduling purposes:
Given that existing non-guest_memfd workloads are already using HugeTLB, for optimal scheduling, machine memory is already carved up in HugeTLB pages for these workloads. Workloads that require using
guest_memfd(like Confidential VMs) must also use HugeTLB to participate in optimial workload scheduling across machines.Quoted 12 lines
Quoted 2 lines
2. Reverting and re-adopting the try-commit-cancel protocol for memory charging
On the second point, I am wondering if reintroducing the try-commit-cancel protocol is tied to factoring out
hugetlb_alloc_folio. When I removed the protocol a while back, the justification was that for the most part, grabbing a hugetlb folio was a relatively cheap & fast operation, since hugetlb mostly operates out of a preallocated pool.So the cost of being wrong, going above the limit, and having to return the hugetlb folio was also relatively low.
Quoted 5 lines
Thanks for this! I saw your patch to just optimistically grab a HugeTLB page :) For that patch, the primary reason was to simplify the logic, and the simplification was justifiable because grabbing a folio is cheap, right? (And so grabbing a folio being cheap wasn't a reason in itself?)
Yes, exactly!
Quoted 10 lines
Quoted 7 lines
It seems like this patch series introduces some new paths for hugetlb pages to be consumed (specifically, without a reservation or vma). I imagine that these new paths make the slowpath for hugetlb more frequent, which makes the cost of assuming that the memcg limit is OK higher? I think explicitly spelling this out in the justification for reintroducing the charging protocol could be helpful.
Yes, I should have done that. Will copy the following to the next revision.
Thank you for considering!
Quoted 5 lines
The main reason is that reintroducing the charging protocol is the clearest way (for me) to cleanly refactor out
hugetlb_alloc_folio()without worrying about the edge cases around HugeTLB reservations and charging.
Quoted 8 lines
If I didn't reintroduce the charging protocol, I would have to depend on freeing the new hugetlb folio on memcg charging failure, and the freeing in turn depends on the subpool correctly being set in the folio, and the presence of the subpool influences (in
free_huge_folio()) whether the reservation was returned to the global hstate. Aaannnd... there's also ahugetlb_restore_reserveflag that controls whether to return the folio to the subpool (and the hstate). I findfolio_clear_hugetlb_restore_reserve()on certain code paths kind of magical/unexplained too.
I see, if it makes the code simpler to introduce the protocol again, I see no reason why we shouldn't revert the patch : -)
Quoted 13 lines
I would rather iron out those charging and reservation details separately from this series (with more testing support).
On the other hand, reintroducing the charging protocol has the benefit of avoiding allocations (not just dequeuing, if surplus HugeTLB pages are required) if the memcg limit is hit. Also, if the original reason for removing the protocol was to simplify the code, refactoring out
hugetlb_alloc_folio()also simplifies the code, and I think it's actually nice that memcg charging is done the same way as the other two (h_cgandh_cg_rsvdcharging). Afterhugetlb_alloc_folio()is refactored out, the gotos make all three charging systems consistent and symmetric, which I think is nice to have :)
Quoted 2 lines
I hope the consistent/symmetric charging among all 3 systems is welcome, what do you think?
For the hugetlbfs case, the path to allocate a hugeTLB page on demand makes sense, so I definitely see the argument for avoiding allocations. Does guest_memfd also have a path to allocate a hugeTLB page outside of the boottime reservations? In that case I think it would be nice to clarify that the allocation failure case optimization is also for guest_memfd, not only for hugetlbfs.
Symmetric charging is definitely welcome : -) All of your reasons make sense to me, I just wanted to ask and make sure.
Thanks for your thoughts! I hope you have a great day!! Joshua
Quoted 37 lines · Joshua Hahn
Quoted 8 lines · On Wed, 25 Feb 2026 19:37:04 -0800 Ackerley Tng <[email protected]> wrote:
Quoted 6 lines · Joshua Hahn
On Wed, 11 Feb 2026 16:37:11 -0800 Ackerley Tng <[email protected]> wrote:
Hi Ackerly, I hope you're donig well!
[...snip...]
Quoted 8 lines
Quoted 6 lines
Quoted 3 lines
I would like to get feedback on:
1. Opening up HugeTLB's allocation for more generic use
I'm not entirely familiar with
guest_memfd, so pleae excuse my ignorance if I'm missing anything obvious.Happy to take questions! Thank you for your thoughts and reviews!
Of course, thank you for your work, Ackerley!
Quoted 9 lines
Quoted 4 lines
But I'm wondering what hugeTLB offers
that other hugepage solutions cannot offer forguest_memfd, if the goal of this series is to decouple it from hugeTLBfs.The one other huge page source that we've explored is THP pages from the buddy allocator. Compared to HugeTLB, huge pages from the buddy allocator
Quoted 6 lines
+ Has a maximum size of 2M
+ Does not guarantee huge pages the way HugeTLB does - HugeTLB pages are allocated at boot, andguest_memfdcan reserve pages atguest_memfdcreation time.
+ Allocation of HugeTLB pages is also really fast, it's just dequeuing from a preallocated pool
Quoted 3 lines
All of these make sense. Just wanted to know if
guest_memfdhad any unique usecases for hugeTLB that normal hugetlbfs didn't have.
IIUC HugeTLB was meant to make huge pages available to userspace for performance reasons, guest_memfd wants HugeTLB for the same reason, but just for virtualization use cases. So nope, I don't think there's any specifically unique usecases.
These are the differences I can think of between guest_memfd and HugeTLBfs's usage of HugeTLB:
+ guest_memfd may split HugeTLB pages to individual struct pages during guest_memfd's ownership of the HugeTLB page. (The pages will be merged before returning them to HugeTLB)
+ guest_memfd will provide an option to remove memory in guest_memfd ownership from the kernel direct map - I think HugeTLB pages are always in the direct map (?)
+ guest_memfd doesn't want to use HugeTLB surplus pages, for now
+ guest_memfd will reserve pages at fd creation time instead of at mmap time. Reservation is done by creating a subpool, so guest_memfd doesn't use resv_map.
Quoted 26 lines
Quoted 23 lines
The last reason to use HugeTLB is not because of any inherent advantage of using HugeTLB over other sources of huge pages, but for administrative/scheduling purposes:
Given that existing non-guest_memfd workloads are already using HugeTLB, for optimal scheduling, machine memory is already carved up in HugeTLB pages for these workloads. Workloads that require using
guest_memfd(like Confidential VMs) must also use HugeTLB to participate in optimial workload scheduling across machines.[...snip...]
On the other hand, reintroducing the charging protocol has the benefit of avoiding allocations (not just dequeuing, if surplus HugeTLB pages are required) if the memcg limit is hit. Also, if the original reason for removing the protocol was to simplify the code, refactoring out
hugetlb_alloc_folio()also simplifies the code, and I think it's actually nice that memcg charging is done the same way as the other two (h_cgandh_cg_rsvdcharging). Afterhugetlb_alloc_folio()is refactored out, the gotos make all three charging systems consistent and symmetric, which I think is nice to have :)Quoted 2 lines
I hope the consistent/symmetric charging among all 3 systems is welcome, what do you think?
Quoted 7 lines
For the hugetlbfs case, the path to allocate a hugeTLB page on demand makes sense, so I definitely see the argument for avoiding allocations. Does
guest_memfdalso have a path to allocate a hugeTLB page outside of the boottime reservations? In that case I think it would be nice to clarify that the allocation failure case optimization is also forguest_memfd, not only for hugetlbfs.
For now, guest_memfd actually doesn't want to use surplus pages, so guest_memfd won't be allocating pages outside of boottime reservations.
Quoted 3 lines
Symmetric charging is definitely welcome : -) All of your reasons make sense to me, I just wanted to ask and make sure.
This change is mostly for (an alternate form of) simplicity :)
Quoted 2 lines
Thanks for your thoughts! I hope you have a great day!! Joshua