diff options
| -rw-r--r-- | drivers/base/memory.c | 31 | ||||
| -rw-r--r-- | include/linux/memory_hotplug.h | 2 | ||||
| -rw-r--r-- | mm/memory_hotplug.c | 33 | 
3 files changed, 61 insertions, 5 deletions
diff --git a/drivers/base/memory.c b/drivers/base/memory.c index 44e7de6ce69..86c88216a50 100644 --- a/drivers/base/memory.c +++ b/drivers/base/memory.c @@ -275,13 +275,11 @@ memory_block_action(unsigned long phys_index, unsigned long action)  	return ret;  } -static int memory_block_change_state(struct memory_block *mem, +static int __memory_block_change_state(struct memory_block *mem,  		unsigned long to_state, unsigned long from_state_req)  {  	int ret = 0; -	mutex_lock(&mem->state_mutex); -  	if (mem->state != from_state_req) {  		ret = -EINVAL;  		goto out; @@ -309,10 +307,20 @@ static int memory_block_change_state(struct memory_block *mem,  		break;  	}  out: -	mutex_unlock(&mem->state_mutex);  	return ret;  } +static int memory_block_change_state(struct memory_block *mem, +		unsigned long to_state, unsigned long from_state_req) +{ +	int ret; + +	mutex_lock(&mem->state_mutex); +	ret = __memory_block_change_state(mem, to_state, from_state_req); +	mutex_unlock(&mem->state_mutex); + +	return ret; +}  static ssize_t  store_mem_state(struct device *dev,  		struct device_attribute *attr, const char *buf, size_t count) @@ -653,6 +661,21 @@ int unregister_memory_section(struct mem_section *section)  }  /* + * offline one memory block. If the memory block has been offlined, do nothing. + */ +int offline_memory_block(struct memory_block *mem) +{ +	int ret = 0; + +	mutex_lock(&mem->state_mutex); +	if (mem->state != MEM_OFFLINE) +		ret = __memory_block_change_state(mem, MEM_OFFLINE, MEM_ONLINE); +	mutex_unlock(&mem->state_mutex); + +	return ret; +} + +/*   * Initialize the sysfs support for memory devices...   */  int __init memory_dev_init(void) diff --git a/include/linux/memory_hotplug.h b/include/linux/memory_hotplug.h index e64fe80eba9..95573ec4ee6 100644 --- a/include/linux/memory_hotplug.h +++ b/include/linux/memory_hotplug.h @@ -10,6 +10,7 @@ struct page;  struct zone;  struct pglist_data;  struct mem_section; +struct memory_block;  #ifdef CONFIG_MEMORY_HOTPLUG @@ -234,6 +235,7 @@ extern int mem_online_node(int nid);  extern int add_memory(int nid, u64 start, u64 size);  extern int arch_add_memory(int nid, u64 start, u64 size);  extern int offline_pages(unsigned long start_pfn, unsigned long nr_pages); +extern int offline_memory_block(struct memory_block *mem);  extern int remove_memory(u64 start, u64 size);  extern int sparse_add_one_section(struct zone *zone, unsigned long start_pfn,  								int nr_pages); diff --git a/mm/memory_hotplug.c b/mm/memory_hotplug.c index dfc0a6134c7..7d0797475a4 100644 --- a/mm/memory_hotplug.c +++ b/mm/memory_hotplug.c @@ -1014,11 +1014,42 @@ int offline_pages(unsigned long start_pfn, unsigned long nr_pages)  int remove_memory(u64 start, u64 size)  { +	struct memory_block *mem = NULL; +	struct mem_section *section;  	unsigned long start_pfn, end_pfn; +	unsigned long pfn, section_nr; +	int ret;  	start_pfn = PFN_DOWN(start);  	end_pfn = start_pfn + PFN_DOWN(size); -	return __offline_pages(start_pfn, end_pfn, 120 * HZ); + +	for (pfn = start_pfn; pfn < end_pfn; pfn += PAGES_PER_SECTION) { +		section_nr = pfn_to_section_nr(pfn); +		if (!present_section_nr(section_nr)) +			continue; + +		section = __nr_to_section(section_nr); +		/* same memblock? */ +		if (mem) +			if ((section_nr >= mem->start_section_nr) && +			    (section_nr <= mem->end_section_nr)) +				continue; + +		mem = find_memory_block_hinted(section, mem); +		if (!mem) +			continue; + +		ret = offline_memory_block(mem); +		if (ret) { +			kobject_put(&mem->dev.kobj); +			return ret; +		} +	} + +	if (mem) +		kobject_put(&mem->dev.kobj); + +	return 0;  }  #else  int offline_pages(unsigned long start_pfn, unsigned long nr_pages)  |