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. 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 <[email protected]> Cc: Catalin Marinas <[email protected]> Cc: Will Deacon <[email protected]> Cc: Mike Rapoport <[email protected]> Cc: Anshuman Khandual <[email protected]> Cc: David Hildenbrand <[email protected]> Cc: Mark Rutland <[email protected]> Cc: Steven Price <[email protected]> Cc: Logan Gunthorpe <[email protected]> Cc: Suren Baghdasaryan <[email protected]> --- 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

