memory-hotplug: check whether all memory blocks are offlined or not when removing...
authorYasuaki Ishimatsu <isimatu.yasuaki@jp.fujitsu.com>
Sat, 23 Feb 2013 00:32:52 +0000 (16:32 -0800)
committerLinus Torvalds <torvalds@linux-foundation.org>
Sun, 24 Feb 2013 01:50:11 +0000 (17:50 -0800)
We remove the memory like this:

 1. lock memory hotplug
 2. offline a memory block
 3. unlock memory hotplug
 4. repeat 1-3 to offline all memory blocks
 5. lock memory hotplug
 6. remove memory(TODO)
 7. unlock memory hotplug

All memory blocks must be offlined before removing memory.  But we don't
hold the lock in the whole operation.  So we should check whether all
memory blocks are offlined before step6.  Otherwise, kernel maybe
panicked.

Offlining a memory block and removing a memory device can be two
different operations.  Users can just offline some memory blocks without
removing the memory device.  For this purpose, the kernel has held
lock_memory_hotplug() in __offline_pages().  To reuse the code for
memory hot-remove, we repeat step 1-3 to offline all the memory blocks,
repeatedly lock and unlock memory hotplug, but not hold the memory
hotplug lock in the whole operation.

Signed-off-by: Wen Congyang <wency@cn.fujitsu.com>
Signed-off-by: Yasuaki Ishimatsu <isimatu.yasuaki@jp.fujitsu.com>
Signed-off-by: Tang Chen <tangchen@cn.fujitsu.com>
Acked-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: KOSAKI Motohiro <kosaki.motohiro@jp.fujitsu.com>
Cc: Jiang Liu <jiang.liu@huawei.com>
Cc: Jianguo Wu <wujianguo@huawei.com>
Cc: Kamezawa Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Lai Jiangshan <laijs@cn.fujitsu.com>
Cc: Wu Jianguo <wujianguo@huawei.com>
Cc: Ingo Molnar <mingo@elte.hu>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: "H. Peter Anvin" <hpa@zytor.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
drivers/base/memory.c
include/linux/memory_hotplug.h
mm/memory_hotplug.c

index 83d0b17ba1c20231d51a248224996cfefbdabcc6..a51007b790327dfe6c1df06dc2f2240f5e94ca3b 100644 (file)
@@ -693,6 +693,12 @@ int offline_memory_block(struct memory_block *mem)
        return ret;
 }
 
+/* return true if the memory block is offlined, otherwise, return false */
+bool is_memblock_offlined(struct memory_block *mem)
+{
+       return mem->state == MEM_OFFLINE;
+}
+
 /*
  * Initialize the sysfs support for memory devices...
  */
index 4a45c4e500253a68c4b7f1dc59f0f31e9a26eeae..8dd0950a6a7a122b2aae6bfe016de4c5b6de4f4f 100644 (file)
@@ -247,6 +247,7 @@ 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 bool is_memblock_offlined(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);
index 6a82972aeae59e2c23700b4e88951a29fb521746..5d350f5c68e50e15717e8c1f68ae7ff22b2a2df2 100644 (file)
@@ -1429,6 +1429,54 @@ repeat:
                goto repeat;
        }
 
+       lock_memory_hotplug();
+
+       /*
+        * we have offlined all memory blocks like this:
+        *   1. lock memory hotplug
+        *   2. offline a memory block
+        *   3. unlock memory hotplug
+        *
+        * repeat step1-3 to offline the memory block. All memory blocks
+        * must be offlined before removing memory. But we don't hold the
+        * lock in the whole operation. So we should check whether all
+        * memory blocks are offlined.
+        */
+
+       mem = NULL;
+       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 = is_memblock_offlined(mem);
+               if (!ret) {
+                       pr_warn("removing memory fails, because memory "
+                               "[%#010llx-%#010llx] is onlined\n",
+                               PFN_PHYS(section_nr_to_pfn(mem->start_section_nr)),
+                               PFN_PHYS(section_nr_to_pfn(mem->end_section_nr + 1)) - 1);
+
+                       kobject_put(&mem->dev.kobj);
+                       unlock_memory_hotplug();
+                       return ret;
+               }
+       }
+
+       if (mem)
+               kobject_put(&mem->dev.kobj);
+       unlock_memory_hotplug();
+
        return 0;
 }
 #else