Subject: Re: [PATCH v3 07/26] mm: drop CONFIG_HAVE_ARCH_NODEDATA_EXTENSION

From: Jonathan Cameron <[email-masked]>
Date: Fri, 2 Aug 2024 10:49:22 +0100

On Thu,  1 Aug 2024 09:08:07 +0300
Mike Rapoport <[email-masked]> wrote:

> From: "Mike Rapoport (Microsoft)" <[email-masked]>
> 
> There are no users of HAVE_ARCH_NODEDATA_EXTENSION left, so
> arch_alloc_nodedata() and arch_refresh_nodedata() are not needed
> anymore.
> 
> Replace the call to arch_alloc_nodedata() in free_area_init() with
> memblock_alloc(), remove arch_refresh_nodedata() and cleanup
> include/linux/memory_hotplug.h from the associated ifdefery.
> 
> Signed-off-by: Mike Rapoport (Microsoft) <[email-masked]>
> Tested-by: Zi Yan <[email-masked]> # for x86_64 and arm64

Hi Mike, 

This has an accidental (I assume) functional change and if
you have an initially offline node it all goes wrong.

> ---
>  include/linux/memory_hotplug.h | 48 ----------------------------------
>  mm/mm_init.c                   |  3 +--
>  2 files changed, 1 insertion(+), 50 deletions(-)
> 
> diff --git a/include/linux/memory_hotplug.h b/include/linux/memory_hotplug.h
> index ebe876930e78..b27ddce5d324 100644
> --- a/include/linux/memory_hotplug.h
> +++ b/include/linux/memory_hotplug.h
> @@ -16,54 +16,6 @@ struct resource;
>  struct vmem_altmap;
>  struct dev_pagemap;
>  
> -#ifdef CONFIG_HAVE_ARCH_NODEDATA_EXTENSION
> -/*
> - * For supporting node-hotadd, we have to allocate a new pgdat.
> - *
> - * If an arch has generic style NODE_DATA(),
> - * node_data[nid] = kzalloc() works well. But it depends on the architecture.
> - *
> - * In general, generic_alloc_nodedata() is used.
> - *
> - */
> -extern pg_data_t *arch_alloc_nodedata(int nid);
> -extern void arch_refresh_nodedata(int nid, pg_data_t *pgdat);
> -
> -#else /* CONFIG_HAVE_ARCH_NODEDATA_EXTENSION */
> -
> -#define arch_alloc_nodedata(nid)	generic_alloc_nodedata(nid)
> -
> -#ifdef CONFIG_NUMA
> -/*
> - * XXX: node aware allocation can't work well to get new node's memory at this time.
> - *	Because, pgdat for the new node is not allocated/initialized yet itself.
> - *	To use new node's memory, more consideration will be necessary.
> - */
> -#define generic_alloc_nodedata(nid)				\
> -({								\
> -	memblock_alloc(sizeof(*pgdat), SMP_CACHE_BYTES);	\
> -})
> -
> -extern pg_data_t *node_data[];
> -static inline void arch_refresh_nodedata(int nid, pg_data_t *pgdat)
> -{
> -	node_data[nid] = pgdat;
> -}
> -
> -#else /* !CONFIG_NUMA */
> -
> -/* never called */
> -static inline pg_data_t *generic_alloc_nodedata(int nid)
> -{
> -	BUG();
> -	return NULL;
> -}
> -static inline void arch_refresh_nodedata(int nid, pg_data_t *pgdat)
> -{
> -}
> -#endif /* CONFIG_NUMA */
> -#endif /* CONFIG_HAVE_ARCH_NODEDATA_EXTENSION */
> -
>  #ifdef CONFIG_MEMORY_HOTPLUG
>  struct page *pfn_to_online_page(unsigned long pfn);
>  
> diff --git a/mm/mm_init.c b/mm/mm_init.c
> index 75c3bd42799b..bcc2f2dd8021 100644
> --- a/mm/mm_init.c
> +++ b/mm/mm_init.c
> @@ -1838,11 +1838,10 @@ void __init free_area_init(unsigned long *max_zone_pfn)
>  
>  		if (!node_online(nid)) {
>  			/* Allocator not initialized yet */
> -			pgdat = arch_alloc_nodedata(nid);
> +			pgdat = memblock_alloc(sizeof(*pgdat), SMP_CACHE_BYTES);
>  			if (!pgdat)
>  				panic("Cannot allocate %zuB for node %d.\n",
>  				       sizeof(*pgdat), nid);
> -			arch_refresh_nodedata(nid, pgdat);

This allocates pgdat but never sets node_data[nid] to it
and promptly leaks it on the line below. 

Just to sanity check this I spun up a qemu machine with no memory
initially present on some nodes and it went boom as you'd expect.

I tested with addition of
			NODE_DATA(nid) = pgdat;
and it all seems to work as expected.

Jonathan

>  		}
>  		pgdat = NODE_DATA(nid);