Merge tag 'hwlock-v4.8' of git://github.com/andersson/remoteproc
[deliverable/linux.git] / kernel / time / timer.c
index 9339d71ee998ccd2fc73f9cfdacfa5b4321d5cd6..cb9ab401e2d9cad40d1f1dc41e33d52642a5d6e2 100644 (file)
@@ -471,12 +471,9 @@ static inline unsigned calc_index(unsigned expires, unsigned lvl)
        return LVL_OFFS(lvl) + (expires & LVL_MASK);
 }
 
-static void
-__internal_add_timer(struct timer_base *base, struct timer_list *timer)
+static int calc_wheel_index(unsigned long expires, unsigned long clk)
 {
-       unsigned long expires = timer->expires;
-       unsigned long delta = expires - base->clk;
-       struct hlist_head *vec;
+       unsigned long delta = expires - clk;
        unsigned int idx;
 
        if (delta < LVL_START(1)) {
@@ -496,7 +493,7 @@ __internal_add_timer(struct timer_base *base, struct timer_list *timer)
        } else if (LVL_DEPTH > 8 && delta < LVL_START(8)) {
                idx = calc_index(expires, 7);
        } else if ((long) delta < 0) {
-               idx = base->clk & LVL_MASK;
+               idx = clk & LVL_MASK;
        } else {
                /*
                 * Force expire obscene large timeouts to expire at the
@@ -507,20 +504,33 @@ __internal_add_timer(struct timer_base *base, struct timer_list *timer)
 
                idx = calc_index(expires, LVL_DEPTH - 1);
        }
-       /*
-        * Enqueue the timer into the array bucket, mark it pending in
-        * the bitmap and store the index in the timer flags.
-        */
-       vec = base->vectors + idx;
-       hlist_add_head(&timer->entry, vec);
+       return idx;
+}
+
+/*
+ * Enqueue the timer into the hash bucket, mark it pending in
+ * the bitmap and store the index in the timer flags.
+ */
+static void enqueue_timer(struct timer_base *base, struct timer_list *timer,
+                         unsigned int idx)
+{
+       hlist_add_head(&timer->entry, base->vectors + idx);
        __set_bit(idx, base->pending_map);
        timer_set_idx(timer, idx);
 }
 
-static void internal_add_timer(struct timer_base *base, struct timer_list *timer)
+static void
+__internal_add_timer(struct timer_base *base, struct timer_list *timer)
 {
-       __internal_add_timer(base, timer);
+       unsigned int idx;
 
+       idx = calc_wheel_index(timer->expires, base->clk);
+       enqueue_timer(base, timer, idx);
+}
+
+static void
+trigger_dyntick_cpu(struct timer_base *base, struct timer_list *timer)
+{
        if (!IS_ENABLED(CONFIG_NO_HZ_COMMON) || !base->nohz_active)
                return;
 
@@ -551,7 +561,14 @@ static void internal_add_timer(struct timer_base *base, struct timer_list *timer
         * wheel:
         */
        base->next_expiry = timer->expires;
-       wake_up_nohz_cpu(base->cpu);
+               wake_up_nohz_cpu(base->cpu);
+}
+
+static void
+internal_add_timer(struct timer_base *base, struct timer_list *timer)
+{
+       __internal_add_timer(base, timer);
+       trigger_dyntick_cpu(base, timer);
 }
 
 #ifdef CONFIG_TIMER_STATS
@@ -943,28 +960,36 @@ static inline int
 __mod_timer(struct timer_list *timer, unsigned long expires, bool pending_only)
 {
        struct timer_base *base, *new_base;
-       unsigned long flags;
+       unsigned int idx = UINT_MAX;
+       unsigned long clk = 0, flags;
        int ret = 0;
 
        /*
-        * TODO: Calculate the array bucket of the timer right here w/o
-        * holding the base lock. This allows to check not only
-        * timer->expires == expires below, but also whether the timer
-        * ends up in the same bucket. If we really need to requeue
-        * the timer then we check whether base->clk have
-        * advanced between here and locking the timer base. If
-        * jiffies advanced we have to recalc the array bucket with the
-        * lock held.
-        */
-
-       /*
-        * This is a common optimization triggered by the
-        * networking code - if the timer is re-modified
-        * to be the same thing then just return:
+        * This is a common optimization triggered by the networking code - if
+        * the timer is re-modified to have the same timeout or ends up in the
+        * same array bucket then just return:
         */
        if (timer_pending(timer)) {
                if (timer->expires == expires)
                        return 1;
+               /*
+                * Take the current timer_jiffies of base, but without holding
+                * the lock!
+                */
+               base = get_timer_base(timer->flags);
+               clk = base->clk;
+
+               idx = calc_wheel_index(expires, clk);
+
+               /*
+                * Retrieve and compare the array index of the pending
+                * timer. If it matches set the expiry to the new value so a
+                * subsequent call will exit in the expires check above.
+                */
+               if (idx == timer_get_idx(timer)) {
+                       timer->expires = expires;
+                       return 1;
+               }
        }
 
        timer_stats_timer_set_start_info(timer);
@@ -1001,7 +1026,18 @@ __mod_timer(struct timer_list *timer, unsigned long expires, bool pending_only)
        }
 
        timer->expires = expires;
-       internal_add_timer(base, timer);
+       /*
+        * If 'idx' was calculated above and the base time did not advance
+        * between calculating 'idx' and taking the lock, only enqueue_timer()
+        * and trigger_dyntick_cpu() is required. Otherwise we need to
+        * (re)calculate the wheel index via internal_add_timer().
+        */
+       if (idx != UINT_MAX && clk == base->clk) {
+               enqueue_timer(base, timer, idx);
+               trigger_dyntick_cpu(base, timer);
+       } else {
+               internal_add_timer(base, timer);
+       }
 
 out_unlock:
        spin_unlock_irqrestore(&base->lock, flags);
@@ -1608,7 +1644,18 @@ static void run_timer_softirq(struct softirq_action *h)
  */
 void run_local_timers(void)
 {
+       struct timer_base *base = this_cpu_ptr(&timer_bases[BASE_STD]);
+
        hrtimer_run_queues();
+       /* Raise the softirq only if required. */
+       if (time_before(jiffies, base->clk)) {
+               if (!IS_ENABLED(CONFIG_NO_HZ_COMMON) || !base->nohz_active)
+                       return;
+               /* CPU is awake, so check the deferrable base. */
+               base++;
+               if (time_before(jiffies, base->clk))
+                       return;
+       }
        raise_softirq(TIMER_SOFTIRQ);
 }
 
@@ -1878,9 +1925,15 @@ static void __sched do_usleep_range(unsigned long min, unsigned long max)
 }
 
 /**
- * usleep_range - Drop in replacement for udelay where wakeup is flexible
+ * usleep_range - Sleep for an approximate time
  * @min: Minimum time in usecs to sleep
  * @max: Maximum time in usecs to sleep
+ *
+ * In non-atomic context where the exact wakeup time is flexible, use
+ * usleep_range() instead of udelay().  The sleep improves responsiveness
+ * by avoiding the CPU-hogging busy-wait of udelay(), and the range reduces
+ * power usage by allowing hrtimers to take advantage of an already-
+ * scheduled interrupt instead of scheduling a new one just for this sleep.
  */
 void __sched usleep_range(unsigned long min, unsigned long max)
 {
This page took 0.028877 seconds and 5 git commands to generate.