aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/base/memory.c31
-rw-r--r--include/linux/memory_hotplug.h2
-rw-r--r--mm/memory_hotplug.c33
3 files changed, 61 insertions, 5 deletions
diff --git a/drivers/base/memory.c b/drivers/base/memory.c
index 44e7de6ce694..86c88216a503 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)
275 return ret; 275 return ret;
276} 276}
277 277
278static int memory_block_change_state(struct memory_block *mem, 278static int __memory_block_change_state(struct memory_block *mem,
279 unsigned long to_state, unsigned long from_state_req) 279 unsigned long to_state, unsigned long from_state_req)
280{ 280{
281 int ret = 0; 281 int ret = 0;
282 282
283 mutex_lock(&mem->state_mutex);
284
285 if (mem->state != from_state_req) { 283 if (mem->state != from_state_req) {
286 ret = -EINVAL; 284 ret = -EINVAL;
287 goto out; 285 goto out;
@@ -309,10 +307,20 @@ static int memory_block_change_state(struct memory_block *mem,
309 break; 307 break;
310 } 308 }
311out: 309out:
312 mutex_unlock(&mem->state_mutex);
313 return ret; 310 return ret;
314} 311}
315 312
313static int memory_block_change_state(struct memory_block *mem,
314 unsigned long to_state, unsigned long from_state_req)
315{
316 int ret;
317
318 mutex_lock(&mem->state_mutex);
319 ret = __memory_block_change_state(mem, to_state, from_state_req);
320 mutex_unlock(&mem->state_mutex);
321
322 return ret;
323}
316static ssize_t 324static ssize_t
317store_mem_state(struct device *dev, 325store_mem_state(struct device *dev,
318 struct device_attribute *attr, const char *buf, size_t count) 326 struct device_attribute *attr, const char *buf, size_t count)
@@ -653,6 +661,21 @@ int unregister_memory_section(struct mem_section *section)
653} 661}
654 662
655/* 663/*
664 * offline one memory block. If the memory block has been offlined, do nothing.
665 */
666int offline_memory_block(struct memory_block *mem)
667{
668 int ret = 0;
669
670 mutex_lock(&mem->state_mutex);
671 if (mem->state != MEM_OFFLINE)
672 ret = __memory_block_change_state(mem, MEM_OFFLINE, MEM_ONLINE);
673 mutex_unlock(&mem->state_mutex);
674
675 return ret;
676}
677
678/*
656 * Initialize the sysfs support for memory devices... 679 * Initialize the sysfs support for memory devices...
657 */ 680 */
658int __init memory_dev_init(void) 681int __init memory_dev_init(void)
diff --git a/include/linux/memory_hotplug.h b/include/linux/memory_hotplug.h
index e64fe80eba96..95573ec4ee6c 100644
--- a/include/linux/memory_hotplug.h
+++ b/include/linux/memory_hotplug.h
@@ -10,6 +10,7 @@ struct page;
10struct zone; 10struct zone;
11struct pglist_data; 11struct pglist_data;
12struct mem_section; 12struct mem_section;
13struct memory_block;
13 14
14#ifdef CONFIG_MEMORY_HOTPLUG 15#ifdef CONFIG_MEMORY_HOTPLUG
15 16
@@ -234,6 +235,7 @@ extern int mem_online_node(int nid);
234extern int add_memory(int nid, u64 start, u64 size); 235extern int add_memory(int nid, u64 start, u64 size);
235extern int arch_add_memory(int nid, u64 start, u64 size); 236extern int arch_add_memory(int nid, u64 start, u64 size);
236extern int offline_pages(unsigned long start_pfn, unsigned long nr_pages); 237extern int offline_pages(unsigned long start_pfn, unsigned long nr_pages);
238extern int offline_memory_block(struct memory_block *mem);
237extern int remove_memory(u64 start, u64 size); 239extern int remove_memory(u64 start, u64 size);
238extern int sparse_add_one_section(struct zone *zone, unsigned long start_pfn, 240extern int sparse_add_one_section(struct zone *zone, unsigned long start_pfn,
239 int nr_pages); 241 int nr_pages);
diff --git a/mm/memory_hotplug.c b/mm/memory_hotplug.c
index dfc0a6134c7c..7d0797475a47 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)
1014 1014
1015int remove_memory(u64 start, u64 size) 1015int remove_memory(u64 start, u64 size)
1016{ 1016{
1017 struct memory_block *mem = NULL;
1018 struct mem_section *section;
1017 unsigned long start_pfn, end_pfn; 1019 unsigned long start_pfn, end_pfn;
1020 unsigned long pfn, section_nr;
1021 int ret;
1018 1022
1019 start_pfn = PFN_DOWN(start); 1023 start_pfn = PFN_DOWN(start);
1020 end_pfn = start_pfn + PFN_DOWN(size); 1024 end_pfn = start_pfn + PFN_DOWN(size);
1021 return __offline_pages(start_pfn, end_pfn, 120 * HZ); 1025
1026 for (pfn = start_pfn; pfn < end_pfn; pfn += PAGES_PER_SECTION) {
1027 section_nr = pfn_to_section_nr(pfn);
1028 if (!present_section_nr(section_nr))
1029 continue;
1030
1031 section = __nr_to_section(section_nr);
1032 /* same memblock? */
1033 if (mem)
1034 if ((section_nr >= mem->start_section_nr) &&
1035 (section_nr <= mem->end_section_nr))
1036 continue;
1037
1038 mem = find_memory_block_hinted(section, mem);
1039 if (!mem)
1040 continue;
1041
1042 ret = offline_memory_block(mem);
1043 if (ret) {
1044 kobject_put(&mem->dev.kobj);
1045 return ret;
1046 }
1047 }
1048
1049 if (mem)
1050 kobject_put(&mem->dev.kobj);
1051
1052 return 0;
1022} 1053}
1023#else 1054#else
1024int offline_pages(unsigned long start_pfn, unsigned long nr_pages) 1055int offline_pages(unsigned long start_pfn, unsigned long nr_pages)