]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/commitdiff
hrtimers: allow the hot-unplugging of all cpus
authorSebastien Dugue <sebastien.dugue@bull.net>
Mon, 1 Dec 2008 13:09:07 +0000 (14:09 +0100)
committerIngo Molnar <mingo@elte.hu>
Tue, 30 Dec 2008 06:37:19 +0000 (07:37 +0100)
Impact: fix CPU hotplug hang on Power6 testbox

On architectures that support offlining all cpus (at least powerpc/pseries),
hot-unpluging the tick_do_timer_cpu can result in a system hang.

This comes from the fact that if the cpu going down happens to be the
cpu doing the tick, then as the tick_do_timer_cpu handover happens after the
cpu is dead (via the CPU_DEAD notification), we're left without ticks,
jiffies are frozen and any task relying on timers (msleep, ...) is stuck.
That's particularly the case for the cpu looping in __cpu_die() waiting
for the dying cpu to be dead.

This patch addresses this by having the tick_do_timer_cpu handover happen
earlier during the CPU_DYING notification. For this, a new clockevent
notification type is introduced (CLOCK_EVT_NOTIFY_CPU_DYING) which is triggered
in hrtimer_cpu_notify().

Signed-off-by: Sebastien Dugue <sebastien.dugue@bull.net>
Cc: <stable@kernel.org>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
include/linux/clockchips.h
kernel/hrtimer.c
kernel/time/tick-common.c

index ed3a5d473e52b2282c0d09c2c25d406400dc7522..c6de413c5dd147f79f7479d4bd1fc6b28f588d01 100644 (file)
@@ -36,6 +36,7 @@ enum clock_event_nofitiers {
        CLOCK_EVT_NOTIFY_BROADCAST_EXIT,
        CLOCK_EVT_NOTIFY_SUSPEND,
        CLOCK_EVT_NOTIFY_RESUME,
+       CLOCK_EVT_NOTIFY_CPU_DYING,
        CLOCK_EVT_NOTIFY_CPU_DEAD,
 };
 
index c2a69b89ac6190c8be46e8677b42ad055c9fa748..61cb933395baabe54ae8d9c61ab100d8f855253d 100644 (file)
@@ -1609,6 +1609,10 @@ static int __cpuinit hrtimer_cpu_notify(struct notifier_block *self,
                break;
 
 #ifdef CONFIG_HOTPLUG_CPU
+       case CPU_DYING:
+       case CPU_DYING_FROZEN:
+               clockevents_notify(CLOCK_EVT_NOTIFY_CPU_DYING, &scpu);
+               break;
        case CPU_DEAD:
        case CPU_DEAD_FROZEN:
        {
index df12434b43ca09ec5ea9b0c48d679b842feddc9e..457d281258ee9d683c0d91dbf60f9811aad0290a 100644 (file)
@@ -273,6 +273,21 @@ out_bc:
        return ret;
 }
 
+/*
+ * Transfer the do_timer job away from a dying cpu.
+ *
+ * Called with interrupts disabled.
+ */
+static void tick_handover_do_timer(int *cpup)
+{
+       if (*cpup == tick_do_timer_cpu) {
+               int cpu = first_cpu(cpu_online_map);
+
+               tick_do_timer_cpu = (cpu != NR_CPUS) ? cpu :
+                       TICK_DO_TIMER_NONE;
+       }
+}
+
 /*
  * Shutdown an event device on a given cpu:
  *
@@ -297,13 +312,6 @@ static void tick_shutdown(unsigned int *cpup)
                clockevents_exchange_device(dev, NULL);
                td->evtdev = NULL;
        }
-       /* Transfer the do_timer job away from this cpu */
-       if (*cpup == tick_do_timer_cpu) {
-               int cpu = first_cpu(cpu_online_map);
-
-               tick_do_timer_cpu = (cpu != NR_CPUS) ? cpu :
-                       TICK_DO_TIMER_NONE;
-       }
        spin_unlock_irqrestore(&tick_device_lock, flags);
 }
 
@@ -357,6 +365,10 @@ static int tick_notify(struct notifier_block *nb, unsigned long reason,
                tick_broadcast_oneshot_control(reason);
                break;
 
+       case CLOCK_EVT_NOTIFY_CPU_DYING:
+               tick_handover_do_timer(dev);
+               break;
+
        case CLOCK_EVT_NOTIFY_CPU_DEAD:
                tick_shutdown_broadcast_oneshot(dev);
                tick_shutdown_broadcast(dev);