Re: [PATCH 1/2] mm/memory_hotplug: allow marking of memory sections as hotpluggable

From: Mike Rapoport
Date: Sat Oct 17 2020 - 04:26:16 EST


On Fri, Oct 16, 2020 at 07:02:23PM -0700, Sudarshan Rajagopalan wrote:
> Certain architectures such as arm64 doesn't allow boot memory to be
> offlined and removed. Distinguish certain memory sections as
> "hotpluggable" which can be marked by module drivers stating to memory
> hotplug layer that these sections can be offlined and then removed.

I don't quite follow why marking sections as hotpluggable or not should
be done by a device driver. Can you describe in more details your
use-case and why there is a need to add a flag to the memory map?


> This is done by using a separate section memory mab bit and setting it,
> rather than clearing the existing SECTION_IS_EARLY bit.
> This patch introduces SECTION_MARK_HOTPLUGGABLE bit into section mem map.
> Only the allowed sections which are in movable zone and have unmovable
> pages are allowed to be set with this new bit.
>
> Signed-off-by: Sudarshan Rajagopalan <sudaraja@xxxxxxxxxxxxxx>
> Cc: Catalin Marinas <catalin.marinas@xxxxxxx>
> Cc: Will Deacon <will@xxxxxxxxxx>
> Cc: Mike Rapoport <rppt@xxxxxxxxxx>
> Cc: Anshuman Khandual <anshuman.khandual@xxxxxxx>
> Cc: David Hildenbrand <david@xxxxxxxxxx>
> Cc: Mark Rutland <mark.rutland@xxxxxxx>
> Cc: Steven Price <steven.price@xxxxxxx>
> Cc: Logan Gunthorpe <logang@xxxxxxxxxxxx>
> Cc: Suren Baghdasaryan <surenb@xxxxxxxxxx>
> ---
> include/linux/memory_hotplug.h | 1 +
> include/linux/mmzone.h | 9 ++++++++-
> mm/memory_hotplug.c | 20 ++++++++++++++++++++
> mm/sparse.c | 31 +++++++++++++++++++++++++++++++
> 4 files changed, 60 insertions(+), 1 deletion(-)
>
> diff --git a/include/linux/memory_hotplug.h b/include/linux/memory_hotplug.h
> index 375515803cd8..81df45b582c8 100644
> --- a/include/linux/memory_hotplug.h
> +++ b/include/linux/memory_hotplug.h
> @@ -319,6 +319,7 @@ extern int offline_pages(unsigned long start_pfn, unsigned long nr_pages);
> extern int remove_memory(int nid, u64 start, u64 size);
> extern void __remove_memory(int nid, u64 start, u64 size);
> extern int offline_and_remove_memory(int nid, u64 start, u64 size);
> +extern int mark_memory_hotpluggable(unsigned long start, unsigned long end);
>
> #else
> static inline void try_offline_node(int nid) {}
> diff --git a/include/linux/mmzone.h b/include/linux/mmzone.h
> index 8379432f4f2f..3df3a4975236 100644
> --- a/include/linux/mmzone.h
> +++ b/include/linux/mmzone.h
> @@ -1247,7 +1247,8 @@ extern size_t mem_section_usage_size(void);
> #define SECTION_HAS_MEM_MAP (1UL<<1)
> #define SECTION_IS_ONLINE (1UL<<2)
> #define SECTION_IS_EARLY (1UL<<3)
> -#define SECTION_MAP_LAST_BIT (1UL<<4)
> +#define SECTION_MARK_HOTPLUGGABLE (1UL<<4)
> +#define SECTION_MAP_LAST_BIT (1UL<<5)
> #define SECTION_MAP_MASK (~(SECTION_MAP_LAST_BIT-1))
> #define SECTION_NID_SHIFT 3
>
> @@ -1278,6 +1279,11 @@ static inline int early_section(struct mem_section *section)
> return (section && (section->section_mem_map & SECTION_IS_EARLY));
> }
>
> +static inline int removable_section(struct mem_section *section)
> +{
> + return (section && (section->section_mem_map & SECTION_MARK_HOTPLUGGABLE));
> +}
> +
> static inline int valid_section_nr(unsigned long nr)
> {
> return valid_section(__nr_to_section(nr));
> @@ -1297,6 +1303,7 @@ static inline int online_section_nr(unsigned long nr)
> void online_mem_sections(unsigned long start_pfn, unsigned long end_pfn);
> #ifdef CONFIG_MEMORY_HOTREMOVE
> void offline_mem_sections(unsigned long start_pfn, unsigned long end_pfn);
> +int section_mark_hotpluggable(struct mem_section *ms);
> #endif
> #endif
>
> diff --git a/mm/memory_hotplug.c b/mm/memory_hotplug.c
> index e9d5ab5d3ca0..503b0de489a0 100644
> --- a/mm/memory_hotplug.c
> +++ b/mm/memory_hotplug.c
> @@ -1860,4 +1860,24 @@ int offline_and_remove_memory(int nid, u64 start, u64 size)
> return rc;
> }
> EXPORT_SYMBOL_GPL(offline_and_remove_memory);
> +
> +int mark_memory_hotpluggable(unsigned long start_pfn, unsigned long end_pfn)
> +{
> + struct mem_section *ms;
> + unsigned long nr;
> + int rc = -EINVAL;
> +
> + if (end_pfn < start_pfn)
> + return rc;
> +
> + for (nr = start_pfn; nr <= end_pfn; nr++) {
> + ms = __pfn_to_section(nr);
> + rc = section_mark_hotpluggable(ms);
> + if (!rc)
> + break;
> + }
> +
> + return rc;
> +}
> +EXPORT_SYMBOL_GPL(mark_memory_hotpluggable);
> #endif /* CONFIG_MEMORY_HOTREMOVE */
> diff --git a/mm/sparse.c b/mm/sparse.c
> index fcc3d176f1ea..cc21c23e2f1d 100644
> --- a/mm/sparse.c
> +++ b/mm/sparse.c
> @@ -13,6 +13,7 @@
> #include <linux/vmalloc.h>
> #include <linux/swap.h>
> #include <linux/swapops.h>
> +#include <linux/page-isolation.h>
>
> #include "internal.h"
> #include <asm/dma.h>
> @@ -644,6 +645,36 @@ void offline_mem_sections(unsigned long start_pfn, unsigned long end_pfn)
> ms->section_mem_map &= ~SECTION_IS_ONLINE;
> }
> }
> +
> +int section_mark_hotpluggable(struct mem_section *ms)
> +{
> + unsigned long section_nr, pfn;
> + bool unmovable;
> + struct page *page;
> +
> + /* section needs to be both valid and present to be marked */
> + if (WARN_ON(!valid_section(ms)) || !present_section(ms))
> + return -EINVAL;
> +
> + /*
> + * now check if this section is removable. This can be done by checking
> + * if section has unmovable pages or not.
> + */
> + section_nr = __section_nr(ms);
> + pfn = section_nr_to_pfn(section_nr);
> + page = pfn_to_page(pfn);
> + unmovable = has_unmovable_pages(page_zone(page), page,
> + MIGRATE_MOVABLE, MEMORY_OFFLINE | REPORT_FAILURE);
> + if (unmovable) {
> + pr_info("section %lu has unmovable pages. Cannot be marked as hotpluggable\n");
> + return -EINVAL;
> + }
> +
> + /* all good! mark section as hotpluggable */
> + ms->section_mem_map |= SECTION_MARK_HOTPLUGGABLE;
> +
> + return 0;
> +}
> #endif
>
> #ifdef CONFIG_SPARSEMEM_VMEMMAP
> --
> Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
> a Linux Foundation Collaborative Project
>

--
Sincerely yours,
Mike.