diff options
Diffstat (limited to 'mm/memory_hotplug.c')
| -rw-r--r-- | mm/memory_hotplug.c | 58 | 
1 files changed, 56 insertions, 2 deletions
diff --git a/mm/memory_hotplug.c b/mm/memory_hotplug.c index 3f792375f32..aea6374f435 100644 --- a/mm/memory_hotplug.c +++ b/mm/memory_hotplug.c @@ -29,6 +29,7 @@  #include <linux/suspend.h>  #include <linux/mm_inline.h>  #include <linux/firmware-map.h> +#include <linux/stop_machine.h>  #include <asm/tlbflush.h> @@ -1679,7 +1680,58 @@ static int is_memblock_offlined_cb(struct memory_block *mem, void *arg)  	return ret;  } -int __ref remove_memory(u64 start, u64 size) +static int check_cpu_on_node(void *data) +{ +	struct pglist_data *pgdat = data; +	int cpu; + +	for_each_present_cpu(cpu) { +		if (cpu_to_node(cpu) == pgdat->node_id) +			/* +			 * the cpu on this node isn't removed, and we can't +			 * offline this node. +			 */ +			return -EBUSY; +	} + +	return 0; +} + +/* offline the node if all memory sections of this node are removed */ +static void try_offline_node(int nid) +{ +	unsigned long start_pfn = NODE_DATA(nid)->node_start_pfn; +	unsigned long end_pfn = start_pfn + NODE_DATA(nid)->node_spanned_pages; +	unsigned long pfn; + +	for (pfn = start_pfn; pfn < end_pfn; pfn += PAGES_PER_SECTION) { +		unsigned long section_nr = pfn_to_section_nr(pfn); + +		if (!present_section_nr(section_nr)) +			continue; + +		if (pfn_to_nid(pfn) != nid) +			continue; + +		/* +		 * some memory sections of this node are not removed, and we +		 * can't offline node now. +		 */ +		return; +	} + +	if (stop_machine(check_cpu_on_node, NODE_DATA(nid), NULL)) +		return; + +	/* +	 * all memory/cpu of this node are removed, we can offline this +	 * node now. +	 */ +	node_set_offline(nid); +	unregister_one_node(nid); +} + +int __ref remove_memory(int nid, u64 start, u64 size)  {  	unsigned long start_pfn, end_pfn;  	int ret = 0; @@ -1734,6 +1786,8 @@ repeat:  	arch_remove_memory(start, size); +	try_offline_node(nid); +  	unlock_memory_hotplug();  	return 0; @@ -1743,7 +1797,7 @@ int offline_pages(unsigned long start_pfn, unsigned long nr_pages)  {  	return -EINVAL;  } -int remove_memory(u64 start, u64 size) +int remove_memory(int nid, u64 start, u64 size)  {  	return -EINVAL;  }  |