Commit e90bdb7f52f94204c78fb40b0804645defdebd71

Authored by Wen Congyang
Committed by Linus Torvalds
1 parent a16cee10c7

memory-hotplug: update memory block's state and notify userspace

remove_memory() will be called when hot removing a memory device.  But
even if offlining memory, we cannot notice it.  So the patch updates the
memory block's state and sends notification to userspace.

Additionally, the memory device may contain more than one memory block.
If the memory block has been offlined, __offline_pages() will fail.  So we
should try to offline one memory block at a time.

Thus remove_memory() also check each memory block's state.  So there is no
need to check the memory block's state before calling remove_memory().

Signed-off-by: Wen Congyang <wency@cn.fujitsu.com>
Signed-off-by: Yasuaki Ishimatsu <isimatu.yasuaki@jp.fujitsu.com>
Cc: David Rientjes <rientjes@google.com>
Cc: Jiang Liu <liuj97@gmail.com>
Cc: Len Brown <len.brown@intel.com>
Cc: Christoph Lameter <cl@linux.com>
Cc: Minchan Kim <minchan.kim@gmail.com>
Cc: KOSAKI Motohiro <kosaki.motohiro@jp.fujitsu.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>

Showing 3 changed files with 61 additions and 5 deletions Side-by-side Diff

drivers/base/memory.c
... ... @@ -275,13 +275,11 @@
275 275 return ret;
276 276 }
277 277  
278   -static int memory_block_change_state(struct memory_block *mem,
  278 +static int __memory_block_change_state(struct memory_block *mem,
279 279 unsigned long to_state, unsigned long from_state_req)
280 280 {
281 281 int ret = 0;
282 282  
283   - mutex_lock(&mem->state_mutex);
284   -
285 283 if (mem->state != from_state_req) {
286 284 ret = -EINVAL;
287 285 goto out;
288 286  
... ... @@ -309,10 +307,20 @@
309 307 break;
310 308 }
311 309 out:
312   - mutex_unlock(&mem->state_mutex);
313 310 return ret;
314 311 }
315 312  
  313 +static 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 +}
316 324 static ssize_t
317 325 store_mem_state(struct device *dev,
318 326 struct device_attribute *attr, const char *buf, size_t count)
... ... @@ -650,6 +658,21 @@
650 658 return -EINVAL;
651 659  
652 660 return remove_memory_block(0, section, 0);
  661 +}
  662 +
  663 +/*
  664 + * offline one memory block. If the memory block has been offlined, do nothing.
  665 + */
  666 +int 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;
653 676 }
654 677  
655 678 /*
include/linux/memory_hotplug.h
... ... @@ -10,6 +10,7 @@
10 10 struct zone;
11 11 struct pglist_data;
12 12 struct mem_section;
  13 +struct memory_block;
13 14  
14 15 #ifdef CONFIG_MEMORY_HOTPLUG
15 16  
... ... @@ -234,6 +235,7 @@
234 235 extern int add_memory(int nid, u64 start, u64 size);
235 236 extern int arch_add_memory(int nid, u64 start, u64 size);
236 237 extern int offline_pages(unsigned long start_pfn, unsigned long nr_pages);
  238 +extern int offline_memory_block(struct memory_block *mem);
237 239 extern int remove_memory(u64 start, u64 size);
238 240 extern int sparse_add_one_section(struct zone *zone, unsigned long start_pfn,
239 241 int nr_pages);
... ... @@ -1014,11 +1014,42 @@
1014 1014  
1015 1015 int remove_memory(u64 start, u64 size)
1016 1016 {
  1017 + struct memory_block *mem = NULL;
  1018 + struct mem_section *section;
1017 1019 unsigned long start_pfn, end_pfn;
  1020 + unsigned long pfn, section_nr;
  1021 + int ret;
1018 1022  
1019 1023 start_pfn = PFN_DOWN(start);
1020 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 1054 #else
1024 1055 int offline_pages(unsigned long start_pfn, unsigned long nr_pages)