Re: [PATCH 0/2] mm/hugetlb: fix surplus accounting and availability checks during demotion
From: Longlong Xia
Date: Mon Aug 31 2026 - 08:14:08 EST
在 2026/8/29 7:10, Andrew Morton 写道:
On Sun, 23 Aug 2026 11:43:05 +0800 Longlong Xia<xialonglong2025@xxxxxxx> wrote:
This series fixes two problems in the hugetlb demote path.Thanks. Have you created and tested reproducers for these? Gemini was
Patch 1 fixes surplus accounting in the source hstate.
demote_pool_huge_page() removes every source folio as a persistent
folio, but a free folio may instead account for one of the source
hstate's surplus pages (for example after a vmemmap restoration
failure). Removing such a folio without adjusting surplus_huge_pages
makes the persistent count underflow, and later subtracting it from
max_huge_pages can underflow that counter as well.
Patch 2 fixes an availability overcount in the sysfs demote path. The
sysfs trigger checks whether any page is available but then passes the
entire request to demote_pool_huge_page(), which can remove free huge
pages that back existing reservations. With two free pages and one
reservation, a request for two pages removes both and leaves the
reservation without a backing page.
trivially able to do this for me.
If so, it would be helpful to includes the details in the
changelogging.
Hi Andrew,
Yes, I created and tested reproducers for both bugs.
Patch 1 requires a vmemmap restoration failure, which is difficult to
trigger deterministically. For that test only, I added a one-shot fault
injection that makes the first attempt to restore the vmemmap of an
optimized 1 GiB folio fail with -ENOMEM. The injection does not modify
the demotion or accounting code, and it is one-shot so that the later
restore performed during demotion can succeed.
Patch 2 is reproducible entirely from userspace and requires no kernel
instrumentation.
Patch 1 (surplus accounting):
1. Hack the kernel to force the first 1G vmemmap restore to fail:
/* TEST ONLY: fail the first optimized 1G folio restore. */
static atomic_t fail_next_1g_restore = ATOMIC_INIT(1);
... in __hugetlb_vmemmap_restore_folio():
if (huge_page_size(h) == SZ_1G &&
atomic_cmpxchg(&fail_next_1g_restore, 1, 0) == 1) {
pr_info("TEST ONLY: forcing one 1G vmemmap restore failure\n");
return -ENOMEM;
}
2. Boot QEMU with:
hugepagesz=1G hugepages=0 hugetlb_cma=1G hugetlb_free_vmemmap=on
3. Enable overcommit:
echo 1 > /sys/kernel/mm/hugepages/hugepages-1048576kB/nr_overcommit_hugepages
4. Allocate one 1G hugepage:
nr=1 surplus=1 free=0 resv=0
5. Unmap it. Due to the forced restore failure, the folio is left on
the freelist but still accounted as surplus:
nr=1 surplus=1 free=1 resv=0
6. Demote one page:
echo 1 > /sys/kernel/mm/hugepages/hugepages-1048576kB/demote
Before fix:
nr=0 surplus=1 free=0 resv=0
surplus > nr (wrong -- surplus not decremented)
After fix:
nr=0 surplus=0 free=0 resv=0
accounts consistent
Patch 2 (cap demotion):
1. Boot QEMU with:
hugepagesz=1G hugepages=2
nr=2 surplus=0 free=2 resv=0
2. Reserve one 1G page (mmap hugetlbfs, do not touch):
nr=2 surplus=0 free=2 resv=1
3. Request demotion of 2 pages:
echo 2 > /sys/kernel/mm/hugepages/hugepages-1048576kB/demote
Before fix:
nr=0 surplus=0 free=0 resv=1
resv > free (wrong -- reservation has no backing page)
After fix:
nr=1 surplus=0 free=1 resv=1
resv == free (correct -- request capped at available=1)
4. Touch the reserved page and let the process exit:
Before fix:
Bus error (core dumped)
nr=0 surplus=0 free=0 resv=0
After fix:
touch: OK (value=0x00)
nr=1 surplus=0 free=1 resv=0
I'll include these reproducer details in the changelog of the next
version.
Thanks,
Longlong