From: Deepthi Dharwar <deepthi@linux.vnet.ibm.com>
Subject: powerpc/cpuidle: Fixes for pseries_idle hotplug notifier
Git-commit: 852d8cb1ff7cc26e59a4bbb68e230d133b257098
Patch-mainline: yes
References: fate#314019,bnc#795230

    Currently the call to pseries_notify_cpuidle_add_cpu(), that takes
    action on the cpuidle front when a cpu is added/removed
    is being made from smp_xics_setup_cpu().
    This caused lockdep issues as
    reported https://lkml.org/lkml/2012/5/17/2

    On addition of each cpu,
    resources were cleared and re-allocated each time, all in critical
    section as part of start_secondary() call were interrupts are disabled.
    To resolve this issue, the pseries_notify_cpuidle_add_cpu() call is
    is being replaced by a hotplug notifier which
    would prevent cpuidle resources from being
    released and allocated each time cpu is onlined in the critical code path.
    It was fixed in https://lkml.org/lkml/2012/5/18/174.

    Also it is essential to call cpuidle_enable/disable_device
    between  cpuidle_pause_and_lock()  and
    cpuidle_resume_and_unlock()  when used externally
    to avoid race conditions. Add support for CPU_ONLINE_FROZEN
    and CPU_DEAD_FROZEN as part of hotplug notify event for
    pseries_idle  and unregister hotplug notifier
    while exiting out. The above mentioned issues
    are fixed as part of this patch.

Signed-off-by: Deepthi Dharwar <deepthi@linux.vnet.ibm.com>
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Acked-by: Torsten Duwe <duwe@suse.de>
---

 arch/powerpc/platforms/pseries/processor_idle.c |   23 ++++++++++++++++++-----
 1 file changed, 18 insertions(+), 5 deletions(-)


diff --git a/arch/powerpc/platforms/pseries/processor_idle.c b/arch/powerpc/platforms/pseries/processor_idle.c
index 9b9b4e3..7c755f9 100644
--- a/arch/powerpc/platforms/pseries/processor_idle.c
+++ b/arch/powerpc/platforms/pseries/processor_idle.c
@@ -180,13 +180,25 @@ static int pseries_cpuidle_add_cpu_notifier(struct notifier_block *n,
 	struct cpuidle_device *dev =
 			per_cpu_ptr(pseries_cpuidle_devices, hotcpu);
 
-	switch (action & 0xf) {
-	case CPU_ONLINE:
-		if (dev && cpuidle_get_driver()) {
-			cpuidle_disable_device(dev);
+	if (dev && cpuidle_get_driver()) {
+		switch (action) {
+		case CPU_ONLINE:
+		case CPU_ONLINE_FROZEN:
+			cpuidle_pause_and_lock();
 			cpuidle_enable_device(dev);
+			cpuidle_resume_and_unlock();
+			break;
+
+		case CPU_DEAD:
+		case CPU_DEAD_FROZEN:
+			cpuidle_pause_and_lock();
+			cpuidle_disable_device(dev);
+			cpuidle_resume_and_unlock();
+			break;
+
+		default:
+			return NOTIFY_DONE;
 		}
-		break;
 	}
 	return NOTIFY_OK;
 }
@@ -328,6 +340,7 @@ static int __init pseries_processor_idle_init(void)
 static void __exit pseries_processor_idle_exit(void)
 {
 
+	unregister_cpu_notifier(&setup_hotplug_notifier);
 	pseries_idle_devices_uninit();
 	cpuidle_unregister_driver(&pseries_idle_driver);
 
