[PATCH 12/12] mm/hugetlb: make bootmem allocation work with KHO

Mike Rapoport rppt at kernel.org
Sun May 17 03:05:27 PDT 2026


Hi Pratyush,

On Wed, Apr 29, 2026 at 03:39:14PM +0200, Pratyush Yadav wrote:
> From: "Pratyush Yadav (Google)" <pratyush at kernel.org>
> 
> Gigantic page allocation is somewhat broken currently when KHO is used.
> 
> Firstly, they break KHO scratch size accounting. RSRV_KERN is used to
> track how much memory is reserved for use by the kernel. Since
> alloc_bootmem() calls the memblock_alloc*() APIs, the hugepages
> allocated also get marked as RSRV_KERN.

The intended semantics of RSRV_KERN was to distinguish memory reserved
because it's in use by firmware from the memory reserved or allocated by
the kernel. It does not have to be directly used by the kernel, some of
PG_Reserved memory is mapped to user-space and it's not only hugetlb. There
are zero pages, VDSO and maybe something else, I don't remember.

> Allocations marked RSRV_KERN are used by KHO to calculate how much
> scratch space it should reserve to make sure the next kernel has enough
> memory to boot when it is in scratch-only phase. Counting hugepages in

The size of RSRV_KERN allocations is also used in
memblock_estimated_nr_free_pages() that's currently used by
set_max_threads() to set several rlimits and by s390 to allocate colored
zero pages. Excluding hugetlb from that will skew the calculations there.

> that blows up scratch size, and can lead to the scratch allocation
> failing, making KHO unusable. This will show up when huge pages make up
> more than 50% of the system, which is a fairly common use case.
> 
> Secondly, while not supported right now, huge pages are user memory and
> can be preserved via KHO. The scratch spaces should not have any
> preserved memory. Allocating hugepages from scratch (on a KHO boot) can
> lead to them being un-preservable.
> 
> Introduce memblock_alloc_nid_user(). This does two things: first, it
> instructs __memblock_alloc_range_nid() to not use scratch areas to
> fulfill allocation. If KHO is in scratch-only mode, allocations will
> only be made from extended scratch areas. Second, it removes RSRV_KERN
> from the allocation to make sure it doesn't mess up scratch size
> accounting.
> 
> To reduce duplication, introduce __memblock_alloc_range_nid() which does
> exactly what memblock_alloc_range_nid() used to do, but takes the flags
> from its caller. Then make memblock_alloc_range_nid() a wrapper to it.
> This lets memblock_alloc_nid_user() re-use most of the logic without
> causing churn to update all callers of memblock_alloc_range_nid() and
> adding yet another argument to it.

That's neat :)

But I'm not too fond of the memblock_alloc_nid_user() as a concept. That
early at boot everything is still kernel, even though hugetlb pages might
become user afterwards if they are actually consumed.
Another thing, is that adding such global API to memblock could be abused
and suddenly some early code will clear RSRV_KERN for a random pieces of
memory.

If we'd still need a special memblock function for gigantic pages
allocation I'd rather make it explicit that it's for hugetlb and keep it in
mm/internal.h

I was thinking about possible alternative solutions and here's what I came
up with

1. SCRATCH_EXT nicely increases the memory pool available to memblock, but
   the decision which memory can be used for which allocations becomes more
   complex. It should make sure that SCRATCH is not used for hugetlb, but
   OTOH it's preferred for other early allocations. With implicit
   interaction between choose_memblock_flags() and should_skip_region()
   this seems to me quite a headache.
   memblock_reserved_clear_kern() should be changed to something else to
   keep set_max_threads() working. And, IMHO, memblock_alloc_nid_user()
   should be turned into memblock_alloc_gigantic_hugetlb() and only exposed
   to MM code. It's even possible that it will duplicate some of
   memblock_alloc_range_nid) rather than use it because hugetlb is always a
   special case.

2. Split memblock_reserve() part from kho_mem_retrieve() to run before
   hugetlb allocations. With this we won't need new types and APIs, we can
   ensure that hugetlb allocations do not use SCRATCH by reserving scratch
   areas before hugetlb allocations and releasing them afterwards.
   Obviously this is the slowest option as it will slow down all memblock
   allocations from the point we memblock_reserve() preserved memory. Still
   realistically I wouldn't expect large impact on performance because the
   heaviest part there is reserving of the preserved memory that has to
   happen anyway. Also, I don't thing that a system that uses a lot of
   gigantic pages will have a lot of preserved chunks scattered around.

3. Move the complexity into hugetlb and make it preserve all the gigantic
   pages with KHO. This means, though, that we won't be able to increase
   the number of gigantic pages after the first boot (although decreasing
   it seems easy) and that we need to let scratch auto scaling understand
   what were the "normal" memblock allocations and what were the
   allocations of the gigantic pages.

4. Invert SCRATCH_EXT logic and instead of freeing large chunks around the
   preserved memory to SCRATCH_EXT, reserve memory surrounding the
   preserved areas and release scratch_only before hugetlb allocations.
   We'd still need to somehow prevent hugetlb allocation spilling into
   scratch and there's a nasty piece of releasing the memory around the
   preserved chunks. On the bright side, I think it's feasible to defer the
   release of those regions and free them when we are already
   multithreaded. This probably the most involved alternative but it also
   could help with the bottleneck of kho_mem_retrieve() creating too many
   memblock.reserved regions.

Thoughts?

> Signed-off-by: Pratyush Yadav (Google) <pratyush at kernel.org>
> ---
> 
> Notes:
>     Checkpatch complains here about the alignment of arguments of
>     memblock_alloc_range_nid() with open parentheses. That can be ignored
>     since the code already was mis-aligned, and for good reason.
> 
>  include/linux/memblock.h |   4 ++
>  mm/hugetlb.c             |  19 ++----
>  mm/memblock.c            | 138 ++++++++++++++++++++++++++++++---------
>  3 files changed, 116 insertions(+), 45 deletions(-)

-- 
Sincerely yours,
Mike.



More information about the kexec mailing list