Sorry, I can't test this now but I post this while discussion is hot. I'll test this with CONFIG_PM_DEBUG's hibernation test mode if I can. (But may take a long time..) Any feedback is welcome. Thanks, -Kame == Now, MEMORY_HOTPLUG and HIBERNATION is mutually exclusive in Kconfig. That's because - memory hotplug changes pgdat/zone/memmap range. - hibernation countes # of memory based on pgdate/zone/memmap. This patch adds mutex for disallowing them to run simultaneously. IIUC, add_memory() is called from following places. - probe interface - acpi handler It seems both can sleep. (acpi handler uses sleepable memory allocator etc...) online/offline handlers can sleep. hibernation notifier can sleep, too. This also makes all memory hotplug event not to happen at the same time. (memory hotplug is not busy code.) Anyway, the most difficult thing is how to test this. Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@xxxxxxxxxxxxxx> mm/Kconfig | 5 ---- mm/memory_hotplug.c | 60 +++++++++++++++++++++++++++++++++++++++++++++------- 2 files changed, 53 insertions(+), 12 deletions(-) Index: linux-2.6.27.4/mm/memory_hotplug.c =================================================================== --- linux-2.6.27.4.orig/mm/memory_hotplug.c +++ linux-2.6.27.4/mm/memory_hotplug.c @@ -31,6 +31,8 @@ #include "internal.h" +DEFINE_MUTEX(memhp_mutex); + /* add this memory to iomem resource */ static struct resource *register_memory_resource(u64 start, u64 size) { @@ -381,6 +383,7 @@ int online_pages(unsigned long pfn, unsi int ret; struct memory_notify arg; + mutex_lock(&memhp_mutex); arg.start_pfn = pfn; arg.nr_pages = nr_pages; arg.status_change_nid = -1; @@ -392,6 +395,7 @@ int online_pages(unsigned long pfn, unsi ret = memory_notify(MEM_GOING_ONLINE, &arg); ret = notifier_to_errno(ret); if (ret) { + mutex_unlock(&memhp_mutex); memory_notify(MEM_CANCEL_ONLINE, &arg); return ret; } @@ -415,6 +419,7 @@ int online_pages(unsigned long pfn, unsi printk(KERN_DEBUG "online_pages %lx at %lx failed\n", nr_pages, pfn); memory_notify(MEM_CANCEL_ONLINE, &arg); + mutex_unlock(&memhp_mutex); return ret; } @@ -436,7 +441,7 @@ int online_pages(unsigned long pfn, unsi if (onlined_pages) memory_notify(MEM_ONLINE, &arg); - + mutex_unlock(&memhp_mutex); return 0; } #endif /* CONFIG_MEMORY_HOTPLUG_SPARSE */ @@ -477,14 +482,18 @@ int add_memory(int nid, u64 start, u64 s struct resource *res; int ret; + mutex_lock(&memhp_mutex); res = register_memory_resource(start, size); - if (!res) + if (!res) { + mutex_unlock(&memhp_mutex); return -EEXIST; - + } if (!node_online(nid)) { pgdat = hotadd_new_pgdat(nid, start); - if (!pgdat) + if (!pgdat) { + mutex_unlock(&memhp_mutex); return -ENOMEM; + } new_pgdat = 1; } @@ -508,7 +517,7 @@ int add_memory(int nid, u64 start, u64 s */ BUG_ON(ret); } - + mutex_unlock(&memhp_mutex); return ret; error: /* rollback pgdat allocation and others */ @@ -517,10 +526,12 @@ error: if (res) release_memory_resource(res); + mutex_unlock(&memhp_mutex); return ret; } EXPORT_SYMBOL_GPL(add_memory); + #ifdef CONFIG_MEMORY_HOTREMOVE /* * A free page on the buddy free lists (not the per-cpu lists) has PageBuddy @@ -750,20 +761,23 @@ int offline_pages(unsigned long start_pf return -EINVAL; if (!IS_ALIGNED(end_pfn, pageblock_nr_pages)) return -EINVAL; + /* This makes hotplug much easier...and readable. we assume this for now. .*/ if (!test_pages_in_a_zone(start_pfn, end_pfn)) return -EINVAL; + mutex_lock(&memhp_mutex); zone = page_zone(pfn_to_page(start_pfn)); node = zone_to_nid(zone); nr_pages = end_pfn - start_pfn; /* set above range as isolated */ ret = start_isolate_page_range(start_pfn, end_pfn); - if (ret) + if (ret) { + mutex_unlock(&memhp_mutex); return ret; - + } arg.start_pfn = start_pfn; arg.nr_pages = nr_pages; arg.status_change_nid = -1; @@ -838,6 +852,7 @@ repeat: writeback_set_ratelimit(); memory_notify(MEM_OFFLINE, &arg); + mutex_unlock(&memhp_mutex); return 0; failed_removal: @@ -846,7 +861,7 @@ failed_removal: memory_notify(MEM_CANCEL_OFFLINE, &arg); /* pushback to free area */ undo_isolate_page_range(start_pfn, end_pfn); - + mutex_unlock(&memhp_mutex); return ret; } #else @@ -856,3 +871,43 @@ int remove_memory(u64 start, u64 size) } EXPORT_SYMBOL_GPL(remove_memory); #endif /* CONFIG_MEMORY_HOTREMOVE */ + + +#ifdef CONFIG_HIBERNATION + +int memhp_hibenation_callback(struct notifier_block *self, + unsigned long action, + void *arg) +{ + switch(action) { + case PM_HIBERNATION_PREPARE: + mutex_lock(&memhp_mutex); + break; + case PM_POST_HIBERNATION: + mutex_unlock(&memhp_mutex); + break; + case PM_RESTORE_PREPARE: + mutex_lock(&memhp_mutex); + break; + case PM_POST_RESTORE: + mutex_unlock(&memhp_mutex); + break; + default: + break; + } + return NOTIFY_OK; +} +static struct notifier_block memhp_notifier { + .notifier_call = memhp_hibernation_callback, +}; + +static int __init memhp_hibernation_notifier_init(void) +{ + register_pm_notifier(&memhp_notifier); + return 0; +} +__initcall(&memhp_hibernation_notifier_init); + +#endif + + Index: linux-2.6.27.4/mm/Kconfig =================================================================== --- linux-2.6.27.4.orig/mm/Kconfig +++ linux-2.6.27.4/mm/Kconfig @@ -128,12 +128,9 @@ config SPARSEMEM_VMEMMAP config MEMORY_HOTPLUG bool "Allow for memory hot-add" depends on SPARSEMEM || X86_64_ACPI_NUMA - depends on HOTPLUG && !HIBERNATION && ARCH_ENABLE_MEMORY_HOTPLUG + depends on HOTPLUG && ARCH_ENABLE_MEMORY_HOTPLUG depends on (IA64 || X86 || PPC64 || SUPERH || S390) -comment "Memory hotplug is currently incompatible with Software Suspend" - depends on SPARSEMEM && HOTPLUG && HIBERNATION - config MEMORY_HOTPLUG_SPARSE def_bool y depends on SPARSEMEM && MEMORY_HOTPLUG _______________________________________________ linux-pm mailing list linux-pm@xxxxxxxxxxxxxxxxxxxxxxxxxx https://lists.linux-foundation.org/mailman/listinfo/linux-pm