aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAnna-Maria Gleixner <anna-maria@linutronix.de>2016-07-04 05:50:40 -0400
committerIngo Molnar <mingo@kernel.org>2016-07-07 04:35:12 -0400
commitf00c0afdfa625165a609513bc74164d56752ec3e (patch)
treef0cc5ba3ecee3178760cea39941490eea12433ef
parentffdf047728f8f93df896b58049c7513856027141 (diff)
timers: Implement optimization for same expiry time in mod_timer()
The existing optimization for same expiry time in mod_timer() checks whether the timer expiry time is the same as the new requested expiry time. In the old timer wheel implementation this does not take the slack batching into account, neither does the new implementation evaluate whether the new expiry time will requeue the timer to the same bucket. To optimize that, we can calculate the resulting bucket and check if the new expiry time is different from the current expiry time. This calculation happens outside the base lock held region. If the resulting bucket is the same we can avoid taking the base lock and requeueing the timer. If the timer needs to be requeued then we have to check under the base lock whether the base time has changed between the lockless calculation and taking the lock. If it has changed we need to recalculate under the lock. This optimization takes effect for timers which are enqueued into the less granular wheel levels (1 and above). With a simple test case the functionality has been verified: Before After Match: 5.5% 86.6% Requeue: 94.5% 13.4% Recalc: <0.01% In the non optimized case the timer is requeued in 94.5% of the cases. With the index optimization in place the requeue rate drops to 13.4%. The case where the lockless index calculation has to be redone is less than 0.01%. With a real world test case (networking) we observed the following changes: Before After Match: 97.8% 99.7% Requeue: 2.2% 0.3% Recalc: <0.001% That means two percent fewer lock/requeue/unlock operations done in one of the hot path use cases of timers. Signed-off-by: Anna-Maria Gleixner <anna-maria@linutronix.de> Signed-off-by: Thomas Gleixner <tglx@linutronix.de> Cc: Arjan van de Ven <arjan@infradead.org> Cc: Chris Mason <clm@fb.com> Cc: Eric Dumazet <edumazet@google.com> Cc: Frederic Weisbecker <fweisbec@gmail.com> Cc: George Spelvin <linux@sciencehorizons.net> Cc: Josh Triplett <josh@joshtriplett.org> Cc: Len Brown <lenb@kernel.org> Cc: Linus Torvalds <torvalds@linux-foundation.org> Cc: Paul E. McKenney <paulmck@linux.vnet.ibm.com> Cc: Peter Zijlstra <peterz@infradead.org> Cc: Rik van Riel <riel@redhat.com> Cc: rt@linutronix.de Link: http://lkml.kernel.org/r/20160704094342.778527749@linutronix.de Signed-off-by: Ingo Molnar <mingo@kernel.org>
-rw-r--r--kernel/time/timer.c51
1 files changed, 35 insertions, 16 deletions
diff --git a/kernel/time/timer.c b/kernel/time/timer.c
index 8d7c23e55c85..8f29abeb8c4d 100644
--- a/kernel/time/timer.c
+++ b/kernel/time/timer.c
@@ -960,28 +960,36 @@ static inline int
960__mod_timer(struct timer_list *timer, unsigned long expires, bool pending_only) 960__mod_timer(struct timer_list *timer, unsigned long expires, bool pending_only)
961{ 961{
962 struct timer_base *base, *new_base; 962 struct timer_base *base, *new_base;
963 unsigned long flags; 963 unsigned int idx = UINT_MAX;
964 unsigned long clk = 0, flags;
964 int ret = 0; 965 int ret = 0;
965 966
966 /* 967 /*
967 * TODO: Calculate the array bucket of the timer right here w/o 968 * This is a common optimization triggered by the networking code - if
968 * holding the base lock. This allows to check not only 969 * the timer is re-modified to have the same timeout or ends up in the
969 * timer->expires == expires below, but also whether the timer 970 * same array bucket then just return:
970 * ends up in the same bucket. If we really need to requeue
971 * the timer then we check whether base->clk have
972 * advanced between here and locking the timer base. If
973 * jiffies advanced we have to recalc the array bucket with the
974 * lock held.
975 */
976
977 /*
978 * This is a common optimization triggered by the
979 * networking code - if the timer is re-modified
980 * to be the same thing then just return:
981 */ 971 */
982 if (timer_pending(timer)) { 972 if (timer_pending(timer)) {
983 if (timer->expires == expires) 973 if (timer->expires == expires)
984 return 1; 974 return 1;
975 /*
976 * Take the current timer_jiffies of base, but without holding
977 * the lock!
978 */
979 base = get_timer_base(timer->flags);
980 clk = base->clk;
981
982 idx = calc_wheel_index(expires, clk);
983
984 /*
985 * Retrieve and compare the array index of the pending
986 * timer. If it matches set the expiry to the new value so a
987 * subsequent call will exit in the expires check above.
988 */
989 if (idx == timer_get_idx(timer)) {
990 timer->expires = expires;
991 return 1;
992 }
985 } 993 }
986 994
987 timer_stats_timer_set_start_info(timer); 995 timer_stats_timer_set_start_info(timer);
@@ -1018,7 +1026,18 @@ __mod_timer(struct timer_list *timer, unsigned long expires, bool pending_only)
1018 } 1026 }
1019 1027
1020 timer->expires = expires; 1028 timer->expires = expires;
1021 internal_add_timer(base, timer); 1029 /*
1030 * If 'idx' was calculated above and the base time did not advance
1031 * between calculating 'idx' and taking the lock, only enqueue_timer()
1032 * and trigger_dyntick_cpu() is required. Otherwise we need to
1033 * (re)calculate the wheel index via internal_add_timer().
1034 */
1035 if (idx != UINT_MAX && clk == base->clk) {
1036 enqueue_timer(base, timer, idx);
1037 trigger_dyntick_cpu(base, timer);
1038 } else {
1039 internal_add_timer(base, timer);
1040 }
1022 1041
1023out_unlock: 1042out_unlock:
1024 spin_unlock_irqrestore(&base->lock, flags); 1043 spin_unlock_irqrestore(&base->lock, flags);