[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [PATCH v3 1/3] common: introduce "brk" allocator



On Wed, Oct 14, 2026 at 2:02 PM Jan Beulich <jbeulich@xxxxxxxx> wrote:
>
> ... to replace (on x86) ebmalloc(), and then to find further use(s) to
> allow recovering memory which is needed very early (and hence needs
> setting up statically), but may not fully be used (or not be used at all).
>
> Note that unlike free_ebmalloc_unused_mem(), brk_free_unused() (once
> other code is converted) will be able to free part of the BRK space even
> in the xen.efi case on x86. That would happen if BRK space extends across
> a 2Mb boundary, and actual use stops before that boundary.
>
> Signed-off-by: Jan Beulich <jbeulich@xxxxxxxx>
> ---
> Changing setup.c's reserve_e820_ram() uses would be cumbersome when done
> right here. That'll be done when ebmalloc() is replaced, and hence
> what's there can also simply be replaced.
>
> When linking xen.efi, GNU ld prior to 2.46 produces a base relocation for
> the reference to __subsystem__, which is wrong (that's an absolute symbol,
> after all). That does no harm for our purposes, though.
> ---
> v3: Move to common code and introduce BSS_WITH_BRK. Rename
>     brk_get_unused_start() to brk_cease(). Add header guard to brk.h. In
>     brk_alloc() don't calculate result too early. Add printk_once_init(),
>     for use in brk_alloc(). Move introduction of DEFINE_BRK() here; add
>     comment.
>
> --- a/xen/arch/x86/Kconfig
> +++ b/xen/arch/x86/Kconfig
> @@ -15,6 +15,7 @@ config X86
>         select FUNCTION_ALIGNMENT_16B
>         select GENERIC_BUG_FRAME
>         select HAS_ALTERNATIVE
> +       select HAS_BRK_ALLOCATOR
>         select HAS_COMPAT
>         select HAS_CPUFREQ
>         select HAS_DIT
> --- /dev/null
> +++ b/xen/arch/x86/include/asm/brk.h
> @@ -0,0 +1,23 @@
> +/* SPDX-License-Identifier: GPL-2.0-or-later */
> +#ifndef X86_BRK_H
> +#define X86_BRK_H
> +
> +static inline unsigned long brk_adjust_start(unsigned long start)
> +{
> +    unsigned int subsys;
> +
> +    /*
> +     * Only xen.efi will have the symbol __subsystem__ available, and it'll
> +     * be non-zero (10) there.  In ELF the symbol will be undefined, and
> +     * hence zero will be loaded into the register.
> +     */
> +    asm ( ".weak __subsystem__; mov $__subsystem__, %0" : "=r" (subsys) );
> +
> +    /* using_2M_mapping() isn't available here. */
> +    if ( IS_ENABLED(CONFIG_XEN_ALIGN_2M) || subsys )
> +        start = PAGE_ALIGN_2M(start);

This seems a bit fragile: As it happens this corresponds to the clip
to brk_end in setup.c in patch 2, but if it didn't, then the boundary
in the resulting e820 reservation and what gets unmapped in
brk_free_unsed() wouldn't match.

Claude seems to think you could make using_2M_mapping() available
here; and then brk_adjust_start() becomes return using_2M_mapping() ?
PAGE_ALIGN_2M(start) : start.  That drops the ASM, and the comment
about the linker producing a bogus relocation.  2/3 could then do
brk_end = brk_adjust_start(brk_cease()), and guaranteeing that the two
are the same.

> --- /dev/null
> +++ b/xen/common/brk.c
> @@ -0,0 +1,74 @@
> +/* SPDX-License-Identifier: GPL-2.0-or-later */
> +
> +#include <xen/brk.h>
> +#include <xen/efi.h>
> +#include <xen/lib.h>
> +#include <xen/mm.h>
> +#include <xen/page-defs.h>
> +
> +#if __has_include(<asm/brk.h>)
> +# include <asm/brk.h>
> +#endif
> +
> +extern char __brk_start[];
> +extern const char __bss_end[];
> +
> +static unsigned long __initdata allocated;
> +static bool __initdata finished;
> +
> +void *__init brk_alloc(size_t size)
> +{
> +    void *ptr;
> +
> +    if ( finished )
> +        return NULL;
> +
> +    /* Allocations PAGE_SIZE and up will be page-aligned. */
> +    if ( size >= PAGE_SIZE )
> +        allocated = ROUNDUP(allocated, PAGE_SIZE);
> +
> +    ptr = __brk_start + allocated;
> +
> +    allocated += ROUNDUP(size, sizeof(void *));
> +
> +    if ( allocated > __bss_end - __brk_start )
> +    {
> +        printk_once_init(
> +            "%pS: over-sized BRK request: %zu bytes\n",
> +            __builtin_extract_return_addr(__builtin_return_address(0)),
> +            size);
> +        return NULL;
> +    }

You bump the global `allocated` variable here before the check, and
don't reset it afterwards.   All future allocation attempts will
continue to bump this value, and
continue to fail.  So now allocated + brk_start is past _bbs_end,
brk_cease() will return something past bss_end, and (in patch 2) pass
that as reserve_e820_ram().  So now, so far from reducing the size
reserved to Xen by , we may be *increasing* the size reserved to Xen
with unused pages.  Worse, if it ends up overshooting the region Xen
is in, then reserve_e820_ram() will fail, and the host will panic.

This should probably use a local variable, then set the global
variable after we've determined that the check has succeeded.

The other thing Claude points out is that is_xen_fixed_mfn() isn't
updated, so if ever any of these pages want to be offlined for any
reason, it will be blocked, claiming that they're part of Xen, even if
they've already been assigned to a guest.  This is a pre-existing
situation that is extended by this patch.

 -George



 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.