The Linux Kernel Mailing List
 help / color / mirror / Atom feed
* [PATCH] sched/isolation: Defer freeing of the bootmem housekeeping cpumasks
       [not found] <20260728134016.674388f101f141362598240f@linux-foundation.org>
@ 2026-08-02 11:56 ` Ionut Nechita
  2026-08-03  6:54   ` Mike Rapoport
  0 siblings, 1 reply; 2+ messages in thread
From: Ionut Nechita @ 2026-08-02 11:56 UTC (permalink / raw)
  To: Ingo Molnar, Peter Zijlstra, Juri Lelli, Vincent Guittot,
	Ionut Nechita, Frederic Weisbecker
  Cc: Dietmar Eggemann, Steven Rostedt, Ben Segall, Mel Gorman,
	Valentin Schneider, K Prateek Nayak, Mike Rapoport (Microsoft),
	Andrew Morton, bugzilla-daemon, linux-mm, linux-kernel

housekeeping_setup() allocates the housekeeping cpumasks from memblock
while parsing the command line, long before the page allocator exists.
housekeeping_init() then reallocates them with kmalloc(), so that a later
runtime update can free the old mask with kfree(), and releases the
memblock allocations with memblock_free().

That release is not safe where it currently sits. housekeeping_init() is
called from start_kernel() after mm_core_init(), so slab_is_available()
is already true and memblock_phys_free() takes the __free_reserved_area()
path. But it is still called long before page_alloc_init_late(), so with
CONFIG_DEFERRED_STRUCT_PAGE_INIT=y the deferred part of the memory map is
not initialized yet, and __free_reserved_area() refuses to touch it:

  Cannot free reserved memory because of deferred initialization of the memory map
  WARNING: mm/memblock.c:904 at __free_reserved_area+0xde/0xf0, CPU#0: swapper/0/0
  Call Trace:
   memblock_phys_free+0xe4/0x120
   housekeeping_init+0x149/0x170
   start_kernel+0x5b6/0x800
   x86_64_start_reservations+0x24/0x30
   x86_64_start_kernel+0xd7/0xe0
   common_startup_64+0x13e/0x151

The warning fires once per housekeeping type in use -- four splats on a
112-CPU two-socket machine booted with both isolcpus= and nohz_full= --
and taints the kernel with G W. No memory is actually lost either way:
a cpumask is much smaller than a page, so __free_reserved_area() has no
whole page to hand back to the buddy allocator in the first place.

Record the bootmem masks instead and release them from a core_initcall,
which runs after page_alloc_init_late() has initialized the deferred
memory map. early_initcall() would still be too early: kernel_init_freeable()
runs do_pre_smp_initcalls() before page_alloc_init_late().

Masks that housekeeping_init() did not manage to replace, because
kmalloc() failed, are never recorded and therefore stay live, preserving
the existing error behaviour.

Fixes: 27c3a5967f05 ("sched/isolation: Convert housekeeping cpumasks to rcu pointers")
Closes: https://bugzilla.kernel.org/show_bug.cgi?id=221804
Link: https://lore.kernel.org/linux-mm/20260728134016.674388f101f141362598240f@linux-foundation.org/
Suggested-by: Mike Rapoport (Microsoft) <rppt@kernel.org>
Signed-off-by: Ionut Nechita <ionut.nechita@windriver.com>
---
Reproduced on a Dell PowerEdge R750 (2x Xeon Gold 6330, 112 CPUs, 2 NUMA
nodes, 128 GiB) running v7.2-rc5 with PREEMPT_RT and
CONFIG_DEFERRED_STRUCT_PAGE_INIT=y, booted with:

  rcu_nocbs=8-55,64-111 nohz_full=managed_irq,nohz,domain,8-55,64-111
  isolcpus=managed_irq,nohz,domain,8-55,64-111 kthread_cpus=0-3,56-59
  irqaffinity=4-7,60-63 rcutree.kthread_prio=21

Four splats at boot without this patch, none with it. Also build-tested
on x86_64 defconfig plus CONFIG_NO_HZ_FULL and
CONFIG_DEFERRED_STRUCT_PAGE_INIT.

The bad memblock_free() has been there since 27c3a5967f05 in v7.0, but it
only became visible in v7.1, when 59bd1d914bb5 ("memblock: warn when
freeing reserved memory before memory map is initialized") added the
WARN. Since nothing is actually leaked and the effect is a warning plus a
G W taint, I did not Cc stable -- say the word if you would rather have
it there.

I also considered Mike's second suggestion, of having housekeeping_setup()
make a single memblock allocation covering all HK_TYPE_MAX masks and
freeing that one region later. It touches more code for no additional
benefit here, so I went with the simpler variant.

 kernel/sched/isolation.c | 24 +++++++++++++++++++++++-
 1 file changed, 23 insertions(+), 1 deletion(-)

diff --git a/kernel/sched/isolation.c b/kernel/sched/isolation.c
index ef152d401fe2..5c36e33bac50 100644
--- a/kernel/sched/isolation.c
+++ b/kernel/sched/isolation.c
@@ -28,6 +28,17 @@ struct housekeeping {
 
 static struct housekeeping housekeeping;
 
+/*
+ * Bootmem cpumasks that housekeeping_init() has replaced with kmalloc()ed
+ * copies. They can't be released right there: housekeeping_init() runs once
+ * the page allocator and slab are up, but before page_alloc_init_late() has
+ * initialized the deferred part of the memory map, and memblock_free() then
+ * reaches __free_reserved_area(), which refuses to touch a memory map that
+ * isn't fully initialized yet. Record them instead and release them from a
+ * core_initcall, which runs after page_alloc_init_late().
+ */
+static struct cpumask *housekeeping_bootmem_masks[HK_TYPE_MAX] __initdata;
+
 bool housekeeping_enabled(enum hk_type type)
 {
 	return !!(READ_ONCE(housekeeping.flags) & BIT(type));
@@ -189,10 +200,21 @@ void __init housekeeping_init(void)
 		WARN_ON_ONCE(cpumask_empty(omask));
 		cpumask_copy(nmask, omask);
 		RCU_INIT_POINTER(housekeeping.cpumasks[type], nmask);
-		memblock_free(omask, cpumask_size());
+		housekeeping_bootmem_masks[type] = omask;
 	}
 }
 
+static int __init housekeeping_free_bootmem_masks(void)
+{
+	enum hk_type type;
+
+	for (type = 0; type < HK_TYPE_MAX; type++)
+		memblock_free(housekeeping_bootmem_masks[type], cpumask_size());
+
+	return 0;
+}
+core_initcall(housekeeping_free_bootmem_masks);
+
 static void __init housekeeping_setup_type(enum hk_type type,
 					   cpumask_var_t housekeeping_staging)
 {

base-commit: 2d2338c93da79b3bfe4b6099a931d9468d539952
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 2+ messages in thread

* Re: [PATCH] sched/isolation: Defer freeing of the bootmem housekeeping cpumasks
  2026-08-02 11:56 ` [PATCH] sched/isolation: Defer freeing of the bootmem housekeeping cpumasks Ionut Nechita
@ 2026-08-03  6:54   ` Mike Rapoport
  0 siblings, 0 replies; 2+ messages in thread
From: Mike Rapoport @ 2026-08-03  6:54 UTC (permalink / raw)
  To: Ionut Nechita
  Cc: Ingo Molnar, Peter Zijlstra, Juri Lelli, Vincent Guittot,
	Frederic Weisbecker, Dietmar Eggemann, Steven Rostedt, Ben Segall,
	Mel Gorman, Valentin Schneider, K Prateek Nayak, Andrew Morton,
	bugzilla-daemon, linux-mm, linux-kernel

On Sun, Aug 02, 2026 at 02:56:23PM +0300, Ionut Nechita wrote:
> housekeeping_setup() allocates the housekeeping cpumasks from memblock
> while parsing the command line, long before the page allocator exists.
> housekeeping_init() then reallocates them with kmalloc(), so that a later
> runtime update can free the old mask with kfree(), and releases the
> memblock allocations with memblock_free().
> 
> That release is not safe where it currently sits. housekeeping_init() is
> called from start_kernel() after mm_core_init(), so slab_is_available()
> is already true and memblock_phys_free() takes the __free_reserved_area()
> path. But it is still called long before page_alloc_init_late(), so with
> CONFIG_DEFERRED_STRUCT_PAGE_INIT=y the deferred part of the memory map is
> not initialized yet, and __free_reserved_area() refuses to touch it:
> 
> Record the bootmem masks instead and release them from a core_initcall,
> which runs after page_alloc_init_late() has initialized the deferred
> memory map. early_initcall() would still be too early: kernel_init_freeable()
> runs do_pre_smp_initcalls() before page_alloc_init_late().
> 
> Masks that housekeeping_init() did not manage to replace, because
> kmalloc() failed, are never recorded and therefore stay live, preserving
> the existing error behaviour.
> 
> Fixes: 27c3a5967f05 ("sched/isolation: Convert housekeeping cpumasks to rcu pointers")
> Closes: https://bugzilla.kernel.org/show_bug.cgi?id=221804
> Link: https://lore.kernel.org/linux-mm/20260728134016.674388f101f141362598240f@linux-foundation.org/
> Suggested-by: Mike Rapoport (Microsoft) <rppt@kernel.org>
> Signed-off-by: Ionut Nechita <ionut.nechita@windriver.com>

Acked-by: Mike Rapoport (Microsoft) <rppt@kernel.org>

Some nits below.

> ---
>  kernel/sched/isolation.c | 24 +++++++++++++++++++++++-
>  1 file changed, 23 insertions(+), 1 deletion(-)
> 
> diff --git a/kernel/sched/isolation.c b/kernel/sched/isolation.c
> index ef152d401fe2..5c36e33bac50 100644
> --- a/kernel/sched/isolation.c
> +++ b/kernel/sched/isolation.c
> @@ -28,6 +28,17 @@ struct housekeeping {
>  
>  static struct housekeeping housekeeping;
>  
> +/*
> + * Bootmem cpumasks that housekeeping_init() has replaced with kmalloc()ed

Nit: s/has replaced/replaces

> + * copies. They can't be released right there: housekeeping_init() runs once
> + * the page allocator and slab are up, but before page_alloc_init_late() has
> + * initialized the deferred part of the memory map, and memblock_free() then
> + * reaches __free_reserved_area(), which refuses to touch a memory map that
> + * isn't fully initialized yet. Record them instead and release them from a
> + * core_initcall, which runs after page_alloc_init_late().

And I'd split it into 3 paragraphs :)

- the first sentence
- description why that memory can't be released in housekeeping_init()
- Record them ...

> + */
> +static struct cpumask *housekeeping_bootmem_masks[HK_TYPE_MAX] __initdata;
> +
>  bool housekeeping_enabled(enum hk_type type)
>  {
>  	return !!(READ_ONCE(housekeeping.flags) & BIT(type));
> @@ -189,10 +200,21 @@ void __init housekeeping_init(void)
>  		WARN_ON_ONCE(cpumask_empty(omask));
>  		cpumask_copy(nmask, omask);
>  		RCU_INIT_POINTER(housekeeping.cpumasks[type], nmask);
> -		memblock_free(omask, cpumask_size());
> +		housekeeping_bootmem_masks[type] = omask;
>  	}
>  }
>  
> +static int __init housekeeping_free_bootmem_masks(void)
> +{
> +	enum hk_type type;
> +
> +	for (type = 0; type < HK_TYPE_MAX; type++)
> +		memblock_free(housekeeping_bootmem_masks[type], cpumask_size());
> +
> +	return 0;
> +}
> +core_initcall(housekeeping_free_bootmem_masks);
> +
>  static void __init housekeeping_setup_type(enum hk_type type,
>  					   cpumask_var_t housekeeping_staging)
>  {
> 
> base-commit: 2d2338c93da79b3bfe4b6099a931d9468d539952
> -- 
> 2.55.0
> 

-- 
Sincerely yours,
Mike.

^ permalink raw reply	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2026-08-03  6:54 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
     [not found] <20260728134016.674388f101f141362598240f@linux-foundation.org>
2026-08-02 11:56 ` [PATCH] sched/isolation: Defer freeing of the bootmem housekeeping cpumasks Ionut Nechita
2026-08-03  6:54   ` Mike Rapoport

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox