[PATCH v3 18/21] kho: extend scratch

Mike Rapoport rppt at kernel.org
Wed Jul 15 02:35:19 PDT 2026


> Motivation
> ==========
> 
> The scratch space is allocated by the first kernel in the KHO chain, and
> is reused by all subsequent kernels. The size of the space is either set
> via the commandline by the system administrator or by calculating the
> amount of memory used by the kernel and adding a multiplier. In either
> case, the scratch space is a heuristic and is liable to fill up and fail
> allocation if a kernel uses more memory than expected.
> 
> In addition, gigantic huge pages (usually 1 GiB) are allocated via
> memblock, and in a KHO boot that memory comes from the scratch space. In
> hypervisors it is common to dedicate a major part of the system's memory
> to gigantic hugepages for VM memory.
> 
> If this memory needs to come from scratch space, then scratch needs to
> be greater than the memory needed for huge pages, which is impractical.
> In addition, hugepages can be preserved memory. Allocating them from
> scratch violates the assumption that scratch contains no preserved
> memory.
> 
> Methodology
> ===========
> 
> Discover areas that don't contain any preserved memory at boot by
> walking the preserved memory radix tree. Mark them as scratch to allow
> allocations from them. This makes KHO more resilient to memory pressure
> and allows supporting huge page preservation.
> 
> Since the preserved memory radix tree mixes both physical address and
> order into a single key, and does not track table pages, it is difficult
> to identify free areas from it directly. Walk the tree and digest it
> down into another radix tree. The latter tracks blocks of
> KHO_EXT_SHIFT (1 GiB as of now) granularity. Then walk the digested tree
> and mark the areas between the present keys as scratch.
> 
> Performance
> ===========
> 
> The discovery algorithm traverses the preserved memory radix tree
> exactly once. While it does use memory for the digested radix tree,
> since the blocks are split by 1 GiB, a single bitmap with 4k pages can
> track up to 32 TiB of memory. So there are likely to be very few radix
> tree pages used in this tracking. For systems with all physical memory
> below 32 TiB, this should result in a total of 6 pages being
> used (KHO_TREE_MAX_DEPTH == 6).
> 
> An alternate way of achieving this would be to call kho_mem_retrieve()
> earlier in boot and mark all the KHO preservations as reserved. But that
> can blow up memblock.reserved with a bunch of 4K pages scattered
> everywhere, which will reduce performance of subsequent allocations.
> Since the free blocks are tracked in chunks of 1 GiB, this won't blow up
> memblock.memory as much.
> 
> There is no inherent reason for using 1 GiB as the discovered block
> size. This can be changed later if needed. Currently, KHO is mainly
> targeted for server grade systems with hundreds of gigabytes to
> terabytes of memory. So 1 GiB is a reasonable granularity for those
> systems. For smaller systems this doesn't work as well, but we can
> arrive at a better heuristic when we have concrete use cases.
> 
> Practical evaluation
> ====================
> 
> The testing is done on a x86_64 qemu VM running under KVM with 64G
> memory and 12 CPUs. The machine pre-allocates 50 1G pages.
> 
> Since the performance scales with how busy the radix tree is, tests are
> done with 2 preservation patterns: first with two 1M memfds, second with
> two 1G memfds, both using 4k pages.
> 
> Test case 1 - 1M memfd
> ~~~~~~~~~~~~~~~~~~~~~~
> 
> This test case has two memfds with 1M memory each in 4k pages, plus
> other preservations from LUO core and other KHO users.
> 
> This is how the radix tree stats look like:
> 
>     radix_nodes:       0x13
>     nr_preservations:  0x214
>     mem_preserved:     0x227000
> 
>     per order preservations:
>       order  0:  0x20f
>       order  1:  0x4
>       order  4:  0x1
> 
> and this is how long it takes to extend the scratch after KHO boot:
> 
>     KHO: KHO extend time: 47 us
>     KHO: KHO extend total mem: 0xe6c17b000 (~57G)
> 
> Test case 2 - 1G memfd
> ~~~~~~~~~~~~~~~~~~~~~~
> 
> This test case has two memfds with 1G memory each in 4k pages, plus
> other preservations from LUO core and other KHO users.
> 
> This is how the radix tree stats look like:
> 
>     radix_nodes:       0x28
>     nr_preservations:  0x80816
>     mem_preserved:     0x80829000
> 
>     per order preservations:
>       order  0:  0x80811
>       order  1:  0x4
>       order  4:  0x1
> 
> and this is how long it takes to extend the scratch after KHO boot:
> 
>     KHO: KHO extend time: 22514 us
>     KHO: KHO extend total mem: 0xd3f200000 (~52G)
> 
> Signed-off-by: Pratyush Yadav (Google) <pratyush at kernel.org>
>
> diff --git a/kernel/liveupdate/kexec_handover.c b/kernel/liveupdate/kexec_handover.c
> index b400976851e76..2c48488746290 100644
> --- a/kernel/liveupdate/kexec_handover.c
> +++ b/kernel/liveupdate/kexec_handover.c
> @@ -84,6 +84,23 @@ static struct kho_out kho_out = {
>  	},
>  };
>  
> +struct kho_in {
> +	phys_addr_t fdt_phys;
> +	phys_addr_t scratch_phys;
> +	char previous_release[__NEW_UTS_LEN + 1];
> +	u32 kexec_count;
> +	struct kho_debugfs dbg;
> +	struct kho_radix_tree radix_tree;
> +};
> +
> +static struct kho_in kho_in = {
> +};
> +
> +static const void *kho_get_fdt(void)
> +{
> +	return kho_in.fdt_phys ? phys_to_virt(kho_in.fdt_phys) : NULL;
> +}
> +
>  /**
>   * kho_encode_radix_key - Encodes a physical address and order into a radix key.
>   * @phys: The physical address of the page.
> @@ -895,6 +912,128 @@ static void __init kho_reserve_scratch(void)
>  	kho_enable = false;
>  }
>  
> +/*
> + * Look for free blocks of 1G. This is a heuristic chosen to work efficiently
> + * with large systems with hundreds of gigabytes of memory. It will work poorly
> + * on smaller systems. The algorithm itself doesn't depend on the actual value,
> + * so it can be changed to a different heuristic later if needed.
> + */
> +#define KHO_EXT_BLKSIZE		SZ_1G
> +#define KHO_EXT_SHIFT		const_ilog2(KHO_EXT_BLKSIZE)

Nit: EXT_ what? ;-)

> +
> +/* Called for the KHO preserved memory radix tree. */
> +static int __init kho_ext_walk_leaf(unsigned long key, void *data)

I have to say this function still confuses me :)

> +{
> +	/* Radix tree tracking free blocks. */
> +	struct kho_radix_tree *tree = data;
> +	phys_addr_t start, end;
> +	unsigned int order;
> +	int err;
> +
> +	start = kho_decode_radix_key(key, &order);

I would add a comment here, that the key is from the memory tracker and
it is decoded to an address.

-- 
Sincerely yours,
Mike.




More information about the kexec mailing list