Merge branch 'v3.10/topic/configs' of git://git.linaro.org/kernel/linux-linaro-stable...
[firefly-linux-kernel-4.4.55.git] / kernel / time / tick-broadcast.c
index 753e910b647bc12385e197879a7f8b3bd0a1fb8d..19ee339a1d0dd7e104bf25d6c6783eaf03df9e05 100644 (file)
@@ -19,6 +19,7 @@
 #include <linux/profile.h>
 #include <linux/sched.h>
 #include <linux/smp.h>
+#include <linux/module.h>
 
 #include "tick-internal.h"
 
@@ -65,17 +66,34 @@ static void tick_broadcast_start_periodic(struct clock_event_device *bc)
 /*
  * Check, if the device can be utilized as broadcast device:
  */
+static bool tick_check_broadcast_device(struct clock_event_device *curdev,
+                                       struct clock_event_device *newdev)
+{
+       if ((newdev->features & CLOCK_EVT_FEAT_DUMMY) ||
+           (newdev->features & CLOCK_EVT_FEAT_C3STOP))
+               return false;
+
+       if (tick_broadcast_device.mode == TICKDEV_MODE_ONESHOT &&
+           !(newdev->features & CLOCK_EVT_FEAT_ONESHOT))
+               return false;
+
+       return !curdev || newdev->rating > curdev->rating;
+}
+
+/*
+ * Conditionally install/replace broadcast device
+ */
 void tick_install_broadcast_device(struct clock_event_device *dev)
 {
        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))
+       if (!tick_check_broadcast_device(cur, dev))
+               return;
+
+       if (!try_module_get(dev->owner))
                return;
 
-       clockevents_exchange_device(tick_broadcast_device.evtdev, dev);
+       clockevents_exchange_device(cur, dev);
        if (cur)
                cur->event_handler = clockevents_handle_noop;
        tick_broadcast_device.evtdev = dev;
@@ -575,6 +593,13 @@ again:
        cpumask_or(tmpmask, tmpmask, tick_broadcast_force_mask);
        cpumask_clear(tick_broadcast_force_mask);
 
+       /*
+        * Sanity check. Catch the case where we try to broadcast to
+        * offline cpus.
+        */
+       if (WARN_ON_ONCE(!cpumask_subset(tmpmask, cpu_online_mask)))
+               cpumask_and(tmpmask, tmpmask, cpu_online_mask);
+
        /*
         * Wakeup the cpus which have an expired event.
         */
@@ -727,6 +752,7 @@ out:
 static void tick_broadcast_clear_oneshot(int cpu)
 {
        cpumask_clear_cpu(cpu, tick_broadcast_oneshot_mask);
+       cpumask_clear_cpu(cpu, tick_broadcast_pending_mask);
 }
 
 static void tick_broadcast_init_next_event(struct cpumask *mask,
@@ -815,10 +841,12 @@ void tick_shutdown_broadcast_oneshot(unsigned int *cpup)
        raw_spin_lock_irqsave(&tick_broadcast_lock, flags);
 
        /*
-        * Clear the broadcast mask flag for the dead cpu, but do not
-        * stop the broadcast device!
+        * Clear the broadcast masks for the dead cpu, but do not stop
+        * the broadcast device!
         */
        cpumask_clear_cpu(cpu, tick_broadcast_oneshot_mask);
+       cpumask_clear_cpu(cpu, tick_broadcast_pending_mask);
+       cpumask_clear_cpu(cpu, tick_broadcast_force_mask);
 
        raw_spin_unlock_irqrestore(&tick_broadcast_lock, flags);
 }