Merge branch 'timers-nohz-for-linus' of git://git.kernel.org/pub/scm/linux/kernel...
[linux-3.10.git] / kernel / time / tick-broadcast.c
index d76d816..206bbfb 100644 (file)
@@ -66,15 +66,30 @@ static void tick_broadcast_start_periodic(struct clock_event_device *bc)
  */
 int tick_check_broadcast_device(struct clock_event_device *dev)
 {
-       if ((tick_broadcast_device.evtdev &&
+       struct clock_event_device *cur = tick_broadcast_device.evtdev;
+
+       if ((dev->features & CLOCK_EVT_FEAT_DUMMY) ||
+           (tick_broadcast_device.evtdev &&
             tick_broadcast_device.evtdev->rating >= dev->rating) ||
             (dev->features & CLOCK_EVT_FEAT_C3STOP))
                return 0;
 
        clockevents_exchange_device(tick_broadcast_device.evtdev, dev);
+       if (cur)
+               cur->event_handler = clockevents_handle_noop;
        tick_broadcast_device.evtdev = dev;
        if (!cpumask_empty(tick_broadcast_mask))
                tick_broadcast_start_periodic(dev);
+       /*
+        * Inform all cpus about this. We might be in a situation
+        * where we did not switch to oneshot mode because the per cpu
+        * devices are affected by CLOCK_EVT_FEAT_C3STOP and the lack
+        * of a oneshot capable broadcast device. Without that
+        * notification the systems stays stuck in periodic mode
+        * forever.
+        */
+       if (dev->features & CLOCK_EVT_FEAT_ONESHOT)
+               tick_clock_notify();
        return 1;
 }
 
@@ -678,7 +693,8 @@ void tick_broadcast_setup_oneshot(struct clock_event_device *bc)
                bc->event_handler = tick_handle_oneshot_broadcast;
 
                /* Take the do_timer update */
-               tick_do_timer_cpu = cpu;
+               if (!tick_nohz_full_cpu(cpu))
+                       tick_do_timer_cpu = cpu;
 
                /*
                 * We must be careful here. There might be other CPUs