acpi: Automatically online hot-added memory [v2]
Subsystems (6)
RFC PATCH 1/1 acpi: Automatically online hot-added memory [v2]
New sockets have on-die memory controllers. This means that in certain HW configurations the memory behind the socket comes and goes as the socket is physically enabled and disabled.
When a cpu is brought into service it requests memory from the local node. If this fails, memory will be accessed from a different node for percpu data*. This results in a performance hit on large systems under scheduling intensive loads.
A solution to this is to have the memory come online automatically so that when the cpus are onlined they will have local node memory to allocate from.
This patch automatically onlines memory added via an ACPI event.
* A patch to fix this in slab has just been presented upstream.
Documentation/kernel-parameters.txt +3 -0
@@ -1416,6 +1416,9 @@ and is between 256 and 4096 characters. It is defined in the file seconds. Use this parameter to check at some other rate. 0 disables periodic checking. + mem_hotadd_auto= [KNL] + Disable the auto-onlining of hot-added memory. + memtest= [KNL,X86] Enable memtest Format: <integer> default : 0 <disable>
drivers/acpi/Kconfig +8 -0
@@ -350,6 +350,14 @@ config ACPI_HOTPLUG_MEMORY To compile this driver as a module, choose M here: the module will be called acpi_memhotplug. +config ACPI_HOTPLUG_MEMORY_AUTO_ONLINE + bool "Automatically online hotplugged memory" + depends on ACPI_HOTPLUG_MEMORY + default n + help + This forces memory that is brought into service by ACPI + to be automatically onlined. + config ACPI_SBS tristate "Smart Battery System" depends on X86
drivers/acpi/acpi_memhotplug.c +24 -0
@@ -30,6 +30,7 @@#include <linux/init.h> #include <linux/types.h> #include <linux/memory_hotplug.h> +#include <linux/memory.h> #include <acpi/acpi_drivers.h> #define ACPI_MEMORY_DEVICE_CLASS "memory"
@@ -86,6 +87,17 @@ struct acpi_memory_device { };static int acpi_hotmem_initialized; +#ifdef CONFIG_ACPI_HOTPLUG_MEMORY_AUTO_ONLINE +static int mem_hotadd_auto =1 ; +static int __init setup_mem_hotadd_auto (char *str) +{ +if (!strncmp (str,"off" ,3 )) + mem_hotadd_auto =0 ; + +return 1 ; +} +__setup ("mem_hotadd_auto=" , setup_mem_hotadd_auto); +#endif static acpi_status acpi_memory_get_resource (struct acpi_resource *resource,void *context)
@@ -252,6 +264,18 @@ static int acpi_memory_enable_device(struct acpi_memory_device *mem_device) result =add_memory (node, info->start_addr, info->length);if (result)continue ; +#ifdef CONFIG_ACPI_HOTPLUG_MEMORY_AUTO_ONLINE +if (mem_hotadd_auto) { + result =online_pages (info->start_addr >>PAGE_SHIFT , + info->length >>PAGE_SHIFT ); +if (!result) +set_memory_state (info->start_addr >>PAGE_SHIFT , + info->length >>PAGE_SHIFT , +MEM_ONLINE ); +else +printk ("Memory online failed.\n" ); + } +#endif info->enabled =1 ; num_enabled++; }
drivers/base/memory.c +23 -0
@@ -515,6 +515,29 @@ int remove_memory_block(unsigned long node_id, struct mem_section *section, }/* +* need an interface for the VM to mark sections on and offline when +* hot-swapping memory. +*/ +void set_memory_state (unsigned long start_pfn,unsigned long nr_pages, +unsigned long state) +{ +struct mem_section *section; +struct memory_block *mem; +unsigned long start_sec, end_sec, i; + + start_sec =pfn_to_section_nr (start_pfn); + end_sec =pfn_to_section_nr (start_pfn + nr_pages -1 ); +for (i = start_sec; i <= end_sec; i++) { +if (valid_section_nr (i) &&present_section_nr (i)) { + section =__nr_to_section (i); + mem =find_memory_block (section); + mem->state = state; + } + } +} +EXPORT_SYMBOL (set_memory_state); + +/* * need an interface for the VM to add new memory regions, * but without onlining it. */
include/linux/memory.h +1 -0
@@ -114,6 +114,7 @@ extern int remove_memory_block(unsigned long, struct mem_section *, int);extern int memory_notify (unsigned long val,void *v);extern int memory_isolate_notify (unsigned long val,void *v);extern struct memory_block *find_memory_block (struct mem_section *); +extern void set_memory_state (unsigned long ,unsigned long ,unsigned long );#define CONFIG_MEM_BLOCK_SIZE (PAGES_PER_SECTION<<PAGE_SHIFT)enum mem_add_context {BOOT ,HOTPLUG };#endif /* CONFIG_MEMORY_HOTPLUG_SPARSE */
mm/memory_hotplug.c +1 -0
@@ -450,6 +450,7 @@ int online_pages(unsigned long pfn, unsigned long nr_pages) return 0; } +EXPORT_SYMBOL_GPL (online_pages);#endif /* CONFIG_MEMORY_HOTPLUG_SPARSE */ /* we are OK calling __meminit stuff here - we have CONFIG_MEMORY_HOTPLUG */
Quoted 37 lines · Prarit Bhargava
New sockets have on-die memory controllers. This means that in certain HW configurations the memory behind the socket comes and goes as the socket is physically enabled and disabled.
When a cpu is brought into service it requests memory from the local node. If this fails, memory will be accessed from a different node for percpu data*. This results in a performance hit on large systems under scheduling intensive loads.
A solution to this is to have the memory come online automatically so that when the cpus are onlined they will have local node memory to allocate from.
This patch automatically onlines memory added via an ACPI event.
* A patch to fix this in slab has just been presented upstream.
Signed-off-by Prarit Bhargava <[email protected]>Documentation/kernel-parameters.txt +3
-0@@ -1416,6 +1416,9 @@ and is between 256 and 4096 characters. It is defined in the file seconds. Use this parameter to check at some other rate. 0 disables periodic checking. + mem_hotadd_auto= [KNL] + Disable the auto-onlining of hot-added memory. + memtest= [KNL,X86] Enable memtest Format: <integer> default : 0 <disable>drivers/acpi/Kconfig +0
-0
drivers/acpi/Kconfig config ACPI_HOTPLUG_MEMORY · line 359
Quoted 10 lines
drivers/acpi/Kconfig +7
-0@@ -350,6 +350,14 @@ config ACPI_HOTPLUG_MEMORY To compile this driver as a module, choose M here: the module will be called acpi_memhotplug. +config ACPI_HOTPLUG_MEMORY_AUTO_ONLINE + bool "Automatically online hotplugged memory" + depends on ACPI_HOTPLUG_MEMORY + default n + help + This forces memory that is brought into service by ACPI + to be automatically onlined.
I don't know the use cases here, so let me just ask some naive questions.
Is it really worth having both the config option and the kernel parameter? I'd expect few if any uses of hot-add in the field now, so even if you removed the config option and made the default be to always online hot-added memory, would that break anybody?
It looks like acpi_memhotplug can be compiled as a module, so shouldn't "mem_hotadd_auto=" be a module parameter?
Is it possible that a user would want to online memory for some hot-add events but not others? If so, maybe the mem_hotadd_auto thing should also be in sysfs?
Quoted 4 lines
drivers/acpi/Kconfig +0
-0@@ -252,6 +264,18 @@ static int acpi_memory_enable_device(struct acpi_memory_device *mem_device) result = add_memory(node, info->start_addr, info->length); if (result) continue;
drivers/acpi/Kconfig static int acpi_memory_enable_device(struct acpi_memory_device *mem_device) · line 276
Quoted 10 lines
drivers/acpi/Kconfig +10
-0+#ifdef CONFIG_ACPI_HOTPLUG_MEMORY_AUTO_ONLINE + if (mem_hotadd_auto) { + result = online_pages(info->start_addr >> PAGE_SHIFT, + info->length >> PAGE_SHIFT); + if (!result) + set_memory_state(info->start_addr >> PAGE_SHIFT, + info->length >> PAGE_SHIFT, + MEM_ONLINE); + else + printk("Memory online failed.\n");
Printks with constant text are one of my pet peeves because they don't give any clues for debugging. Surely there's useful information we could include here, like the ACPI device info (maybe using dev_err()), the memory range, etc?
Bjorn
Hi Bjorn,
Thanks for the comments ...
Quoted 4 lines
It looks like
acpi_memhotplugcan be compiled as a module, so shouldn't "mem_hotadd_auto=" be a module parameter?
Good point -- I have changed this to a module parameter.
Quoted 4 lines
Is it possible that a user would want to online memory for some hot-add events but not others? If so, maybe the
mem_hotadd_autothing should also be in sysfs?
Users are going to turn it on and keep it on. To be honest, I've only included it because I found it useful while debugging cpu hot adds.
Quoted 5 lines
Printks with constant text are one of my pet peeves because they don't give any clues for debugging. Surely there's useful information we could include here, like the ACPI device info (maybe using
dev_err()), the memory range, etc?
Fixed -- I was taking a hint from the surrounding code and not including the memory range, but I agree with you that more info is better. I've changed this to display the memory range that failed.
P.
D'oh! Fixed.
Quoted 2 lines
Bjorn
drivers/acpi/Kconfig +8 -0
@@ -350,6 +350,14 @@ config ACPI_HOTPLUG_MEMORY To compile this driver as a module, choose M here: the module will be called acpi_memhotplug. +config ACPI_HOTPLUG_MEMORY_AUTO_ONLINE + bool "Automatically online hotplugged memory" + depends on ACPI_HOTPLUG_MEMORY + default n + help + This forces memory that is brought into service by ACPI + to be automatically onlined. + config ACPI_SBS tristate "Smart Battery System" depends on X86
drivers/acpi/acpi_memhotplug.c +20 -0
@@ -30,6 +30,7 @@#include <linux/init.h> #include <linux/types.h> #include <linux/memory_hotplug.h> +#include <linux/memory.h> #include <acpi/acpi_drivers.h> #define ACPI_MEMORY_DEVICE_CLASS "memory"
@@ -45,6 +46,11 @@ ACPI_MODULE_NAME("acpi_memhotplug");MODULE_AUTHOR ("Naveen B S <[email protected]>" );MODULE_DESCRIPTION ("Hotplug Mem Driver" );MODULE_LICENSE ("GPL" ); +#ifdef CONFIG_ACPI_HOTPLUG_MEMORY_AUTO_ONLINE +static int mem_hotadd_auto =1 ; +module_param (mem_hotadd_auto, bool,0444 ); +MODULE_PARM_DESC (mem_hotadd_auto,"Disable automatic onlining of memory" ); +#endif /* Memory Device States */ #define MEMORY_INVALID_STATE 0
@@ -252,6 +258,20 @@ static int acpi_memory_enable_device(struct acpi_memory_device *mem_device) result =add_memory (node, info->start_addr, info->length);if (result)continue ; +#ifdef CONFIG_ACPI_HOTPLUG_MEMORY_AUTO_ONLINE +if (mem_hotadd_auto) { + result =online_pages (info->start_addr >>PAGE_SHIFT , + info->length >>PAGE_SHIFT ); +if (!result) +set_memory_state (info->start_addr >>PAGE_SHIFT , + info->length >>PAGE_SHIFT , +MEM_ONLINE ); +else + printk(KERN_ERR PREFIX "Memory online failed " +"for 0x%llx - 0x%llx\n" , info->start_addr, + info->start_addr + info->length); + } +#endif info->enabled =1 ; num_enabled++; }
drivers/base/memory.c +23 -0
@@ -515,6 +515,29 @@ int remove_memory_block(unsigned long node_id, struct mem_section *section, }/* +* need an interface for the VM to mark sections on and offline when +* hot-swapping memory. +*/ +void set_memory_state (unsigned long start_pfn,unsigned long nr_pages, +unsigned long state) +{ +struct mem_section *section; +struct memory_block *mem; +unsigned long start_sec, end_sec, i; + + start_sec =pfn_to_section_nr (start_pfn); + end_sec =pfn_to_section_nr (start_pfn + nr_pages -1 ); +for (i = start_sec; i <= end_sec; i++) { +if (valid_section_nr (i) &&present_section_nr (i)) { + section =__nr_to_section (i); + mem =find_memory_block (section); + mem->state = state; + } + } +} +EXPORT_SYMBOL (set_memory_state); + +/* * need an interface for the VM to add new memory regions, * but without onlining it. */
include/linux/memory.h +1 -0
@@ -114,6 +114,7 @@ extern int remove_memory_block(unsigned long, struct mem_section *, int);extern int memory_notify (unsigned long val,void *v);extern int memory_isolate_notify (unsigned long val,void *v);extern struct memory_block *find_memory_block (struct mem_section *); +extern void set_memory_state (unsigned long ,unsigned long ,unsigned long );#define CONFIG_MEM_BLOCK_SIZE (PAGES_PER_SECTION<<PAGE_SHIFT)enum mem_add_context {BOOT ,HOTPLUG };#endif /* CONFIG_MEMORY_HOTPLUG_SPARSE */
mm/memory_hotplug.c +1 -0
@@ -450,6 +450,7 @@ int online_pages(unsigned long pfn, unsigned long nr_pages) return 0; } +EXPORT_SYMBOL_GPL (online_pages);#endif /* CONFIG_MEMORY_HOTPLUG_SPARSE */ /* we are OK calling __meminit stuff here - we have CONFIG_MEMORY_HOTPLUG */