Merge tag 'lsk-v3.10-android-15.01'
[firefly-linux-kernel-4.4.55.git] / kernel / time / tick-broadcast.c
index 19ee339a1d0dd7e104bf25d6c6783eaf03df9e05..16f4830a5498acaa4f5ad768f73b47f7abc09994 100644 (file)
@@ -626,24 +626,61 @@ again:
        raw_spin_unlock(&tick_broadcast_lock);
 }
 
+static int broadcast_needs_cpu(struct clock_event_device *bc, int cpu)
+{
+       if (!(bc->features & CLOCK_EVT_FEAT_HRTIMER))
+               return 0;
+       if (bc->next_event.tv64 == KTIME_MAX)
+               return 0;
+       return bc->bound_on == cpu ? -EBUSY : 0;
+}
+
+static void broadcast_shutdown_local(struct clock_event_device *bc,
+                                    struct clock_event_device *dev)
+{
+       /*
+        * For hrtimer based broadcasting we cannot shutdown the cpu
+        * local device if our own event is the first one to expire or
+        * if we own the broadcast timer.
+        */
+       if (bc->features & CLOCK_EVT_FEAT_HRTIMER) {
+               if (broadcast_needs_cpu(bc, smp_processor_id()))
+                       return;
+               if (dev->next_event.tv64 < bc->next_event.tv64)
+                       return;
+       }
+       clockevents_set_mode(dev, CLOCK_EVT_MODE_SHUTDOWN);
+}
+
+static void broadcast_move_bc(int deadcpu)
+{
+       struct clock_event_device *bc = tick_broadcast_device.evtdev;
+
+       if (!bc || !broadcast_needs_cpu(bc, deadcpu))
+               return;
+       /* This moves the broadcast assignment to this cpu */
+       clockevents_program_event(bc, bc->next_event, 1);
+}
+
 /*
  * Powerstate information: The system enters/leaves a state, where
  * affected devices might stop
+ * Returns 0 on success, -EBUSY if the cpu is used to broadcast wakeups.
  */
-void tick_broadcast_oneshot_control(unsigned long reason)
+int tick_broadcast_oneshot_control(unsigned long reason)
 {
        struct clock_event_device *bc, *dev;
        struct tick_device *td;
        unsigned long flags;
        ktime_t now;
-       int cpu;
+       int cpu, ret = 0;
 
        /*
         * Periodic mode does not care about the enter/exit of power
         * states
         */
        if (tick_broadcast_device.mode == TICKDEV_MODE_PERIODIC)
-               return;
+               return 0;
 
        /*
         * We are called with preemtion disabled from the depth of the
@@ -654,7 +691,7 @@ void tick_broadcast_oneshot_control(unsigned long reason)
        dev = td->evtdev;
 
        if (!(dev->features & CLOCK_EVT_FEAT_C3STOP))
-               return;
+               return 0;
 
        bc = tick_broadcast_device.evtdev;
 
@@ -662,7 +699,7 @@ void tick_broadcast_oneshot_control(unsigned long reason)
        if (reason == CLOCK_EVT_NOTIFY_BROADCAST_ENTER) {
                if (!cpumask_test_and_set_cpu(cpu, tick_broadcast_oneshot_mask)) {
                        WARN_ON_ONCE(cpumask_test_cpu(cpu, tick_broadcast_pending_mask));
-                       clockevents_set_mode(dev, CLOCK_EVT_MODE_SHUTDOWN);
+                       broadcast_shutdown_local(bc, dev);
                        /*
                         * We only reprogram the broadcast timer if we
                         * did not mark ourself in the force mask and
@@ -675,6 +712,16 @@ void tick_broadcast_oneshot_control(unsigned long reason)
                            dev->next_event.tv64 < bc->next_event.tv64)
                                tick_broadcast_set_event(bc, cpu, dev->next_event, 1);
                }
+               /*
+                * If the current CPU owns the hrtimer broadcast
+                * mechanism, it cannot go deep idle and we remove the
+                * CPU from the broadcast mask. We don't have to go
+                * through the EXIT path as the local timer is not
+                * shutdown.
+                */
+               ret = broadcast_needs_cpu(bc, cpu);
+               if (ret)
+                       cpumask_clear_cpu(cpu, tick_broadcast_oneshot_mask);
        } else {
                if (cpumask_test_and_clear_cpu(cpu, tick_broadcast_oneshot_mask)) {
                        clockevents_set_mode(dev, CLOCK_EVT_MODE_ONESHOT);
@@ -742,6 +789,7 @@ void tick_broadcast_oneshot_control(unsigned long reason)
        }
 out:
        raw_spin_unlock_irqrestore(&tick_broadcast_lock, flags);
+       return ret;
 }
 
 /*
@@ -848,6 +896,8 @@ void tick_shutdown_broadcast_oneshot(unsigned int *cpup)
        cpumask_clear_cpu(cpu, tick_broadcast_pending_mask);
        cpumask_clear_cpu(cpu, tick_broadcast_force_mask);
 
+       broadcast_move_bc(cpu);
+
        raw_spin_unlock_irqrestore(&tick_broadcast_lock, flags);
 }