Skip to contentSkip to conversation

Open HugeTLB allocation routine for more generic use

RFC PATCH Superseded Ackerley Tng 3 participants 15 messages Feb 12 – Mar 9

Subsystems (4)

v1 of 5 Superseded Latest is v5 →

lore
Share & tools
lore
#0 lore raw

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

#1 lore raw

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
index a1832da0f6236..fd067bd394ee0 100644
@@ -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
Reviewed-by on v1 1/7
#2 lore raw

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_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.

Quoted 4 lines

No functional change intended.

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
index a1832da0f6236..fd067bd394ee0 100644
@@ -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
#3 lore raw

RFC PATCH 2/7 mm: hugetlb: Move mpol interpretation out of alloc_buddy_hugetlb_folio_with_mpol()

In reply to Ackerley Tng

Move 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
index fd067bd394ee0..aaa23d995b65c 100644
@@ -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
Reviewed-by on v1 2/7
#4 lore raw

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 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.

Quoted 3 lines

No functional change intended.

#5 lore raw

RFC PATCH 3/7 mm: hugetlb: Move mpol interpretation out of dequeue_hugetlb_folio_vma()

In reply to Ackerley Tng

Move 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
index aaa23d995b65c..74b5136fdeb54 100644
@@ -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
Reviewed-by on v1 3/7
#6 lore raw

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 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.

Quoted 3 lines

No functional change intended.

#7 lore raw

RFC PATCH 4/7 Revert "memcg/hugetlb: remove memcg hugetlb try-commit-cancel protocol"

In reply to Ackerley Tng

This 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
index f29d4969c0c36..59eab4caa01fa 100644
@@ -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
#8 lore raw

RFC PATCH 5/7 mm: hugetlb: Adopt memcg try-commit-cancel protocol

In reply to Ackerley Tng

Refactor 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
index 74b5136fdeb54..70e91edc47dc1 100644
@@ -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
#9 lore raw

RFC PATCH 6/7 mm: memcontrol: Remove now-unused function mem_cgroup_charge_hugetlb

In reply to Ackerley Tng

With 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
index 59eab4caa01fa..572ad695afa40 100644
@@ -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
index 70d762ba465b1..87d22db5a4bd3 100644
@@ -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
#10 lore raw

RFC PATCH 7/7 mm: hugetlb: Refactor out hugetlb_alloc_folio()

In reply to Ackerley Tng

Refactor 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
index e51b8ef0cebd9..e385945c04af0 100644
@@ -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
#11 lore raw
In reply to Ackerley Tng

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 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

#12 lore raw
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 for guest_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 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

#13 lore raw
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 for guest_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, 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

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 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 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_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 :)

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

#14 lore raw
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 for guest_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, 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

Quoted 3 lines

All of these make sense. Just wanted to know if guest_memfd had 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_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 :)

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_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.

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