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

From: Sudarshan Rajagopalan
Date: Sat Oct 17 2020 - 01:58:11 EST


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 <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