diff mbox series

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

Message ID 2cba881c51e42cfe5ba213e09273642136e8ef93.1602899443.git.sudaraja@codeaurora.org (mailing list archive)
State New, archived
Headers show
Series mm/memory_hotplug, arm64: allow certain bootmem sections to be offlinable | expand

Commit Message

Sudarshan Rajagopalan Oct. 17, 2020, 2:02 a.m. UTC
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@codeaurora.org>
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: Will Deacon <will@kernel.org>
Cc: Mike Rapoport <rppt@kernel.org>
Cc: Anshuman Khandual <anshuman.khandual@arm.com>
Cc: David Hildenbrand <david@redhat.com>
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Steven Price <steven.price@arm.com>
Cc: Logan Gunthorpe <logang@deltatee.com>
Cc: Suren Baghdasaryan <surenb@google.com>
---
 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(-)

Comments

Mike Rapoport Oct. 17, 2020, 8:26 a.m. UTC | #1
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@codeaurora.org>
> Cc: Catalin Marinas <catalin.marinas@arm.com>
> Cc: Will Deacon <will@kernel.org>
> Cc: Mike Rapoport <rppt@kernel.org>
> Cc: Anshuman Khandual <anshuman.khandual@arm.com>
> Cc: David Hildenbrand <david@redhat.com>
> Cc: Mark Rutland <mark.rutland@arm.com>
> Cc: Steven Price <steven.price@arm.com>
> Cc: Logan Gunthorpe <logang@deltatee.com>
> Cc: Suren Baghdasaryan <surenb@google.com>
> ---
>  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
>
David Hildenbrand Oct. 17, 2020, 9:36 a.m. UTC | #2
On 17.10.20 10:26, Mike Rapoport wrote:
> 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 seems to be related to

https://lkml.kernel.org/r/de8388df2fbc5a6a33aab95831ba7db4@codeaurora.org

After reading how the driver is trying to abuse memory hot(un)plug
infrastructure, my tentative

Nacked-by: David Hildenbrand <david@redhat.com>
Mike Rapoport Oct. 21, 2020, 9:54 a.m. UTC | #3
On Sat, Oct 17, 2020 at 11:36:39AM +0200, David Hildenbrand wrote:
> On 17.10.20 10:26, Mike Rapoport wrote:
> > 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 seems to be related to
> 
> https://lkml.kernel.org/r/de8388df2fbc5a6a33aab95831ba7db4@codeaurora.org

Thanks for the pointer.

> After reading how the driver is trying to abuse memory hot(un)plug
> infrastructure, my tentative
> 
> Nacked-by: David Hildenbrand <david@redhat.com>

I also don't think we would want to let drivers play with the memory
map.

> -- 
> Thanks,
> 
> David / dhildenb
>
diff mbox series

Patch

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