mm/hugetlb: fix resv_huge_pages double decrement in memfd error path

alloc_hugetlb_folio_reserve() decrements h->resv_huge_pages when dequeuing
a folio, but unlike the use_global_reservation handling in
hugetlb_alloc_folio(), it does not set HPageRestoreReserve on the folio.

Its sole caller memfd_alloc_folio() pre-allocates a reservation via
hugetlb_reserve_pages() before allocating.  When
hugetlb_add_to_page_cache() fails, folio_put() drops the folio without
HPageRestoreReserve set, so free_huge_folio() does not restore the
reservation.  The subsequent hugetlb_unreserve_pages() on the err_unresv
path decrements the counter a second time, leaving resv_huge_pages off by
one for every failed allocation.

Set HPageRestoreReserve when consuming the reservation in
alloc_hugetlb_folio_reserve().  On the error path, free_huge_folio() then
restores the reservation before hugetlb_unreserve_pages() releases it. 
The success path is unaffected, as hugetlb_add_to_page_cache() clears the
flag once the folio is added to the page cache.

Link: https://lore.kernel.org/20260825021013.25672-1-hongfu.li@linux.dev
Fixes: 26a8ea8092 ("mm/hugetlb: fix memfd_pin_folios resv_huge_pages leak")
Signed-off-by: Hongfu Li <lihongfu@kylinos.cn>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Reviewed-by: Muchun Song <muchun.song@linux.dev>
Cc: David Hildenbrand <david@kernel.org>
Cc: Oscar Salvador <osalvador@suse.de>
Cc: Steven Sistare <steven.sistare@oracle.com>
Cc: Vivek Kasireddy <vivek.kasireddy@intel.com>
This commit is contained in:
Hongfu Li 2026-08-25 10:10:13 +08:00 committed by Andrew Morton
parent 46fa67175c
commit afd20f513c

View File

@ -2187,8 +2187,10 @@ struct folio *alloc_hugetlb_folio_reserve(struct hstate *h, int preferred_nid,
folio = dequeue_hugetlb_folio_nodemask(h, gfp_mask, preferred_nid,
nmask);
if (folio)
if (folio) {
folio_set_hugetlb_restore_reserve(folio);
h->resv_huge_pages--;
}
spin_unlock_irq(&hugetlb_lock);
return folio;