aboutsummaryrefslogtreecommitdiffstats
path: root/kernel/time/hrtimer.c
diff options
context:
space:
mode:
authorPeter Zijlstra <peterz@infradead.org>2014-05-20 09:49:48 -0400
committerThomas Gleixner <tglx@linutronix.de>2015-04-22 11:06:52 -0400
commit5de2755c8c8b3a6b8414870e2c284914a2b42e4d (patch)
treefa728d9e6ab8242d1a65ffeea69053f0262c999d /kernel/time/hrtimer.c
parent2ad5d3272d8e20e24d8242ebac9f3007f1ea56bc (diff)
hrtimer: Allow concurrent hrtimer_start() for self restarting timers
Because we drop cpu_base->lock around calling hrtimer::function, it is possible for hrtimer_start() to come in between and enqueue the timer. If hrtimer::function then returns HRTIMER_RESTART we'll hit the BUG_ON because HRTIMER_STATE_ENQUEUED will be set. Since the above is a perfectly valid scenario, remove the BUG_ON and make the enqueue_hrtimer() call conditional on the timer not being enqueued already. NOTE: in that concurrent scenario its entirely common for both sites to want to modify the hrtimer, since hrtimers don't provide serialization themselves be sure to provide some such that the hrtimer::function and the hrtimer_start() caller don't both try and fudge the expiration state at the same time. To that effect, add a WARN when someone tries to forward an already enqueued timer, the most common way to change the expiry of self restarting timers. Ideally we'd put the WARN in everything modifying the expiry but most of that is inlines and we don't need the bloat. Fixes: 2d44ae4d7135 ("hrtimer: clean up cpu->base locking tricks") Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org> Cc: Ben Segall <bsegall@google.com> Cc: Roman Gushchin <klamm@yandex-team.ru> Cc: Paul Turner <pjt@google.com> Link: http://lkml.kernel.org/r/20150415113105.GT5029@twins.programming.kicks-ass.net Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Diffstat (limited to 'kernel/time/hrtimer.c')
-rw-r--r--kernel/time/hrtimer.c12
1 files changed, 9 insertions, 3 deletions
diff --git a/kernel/time/hrtimer.c b/kernel/time/hrtimer.c
index 3bac94269a98..4adf32067862 100644
--- a/kernel/time/hrtimer.c
+++ b/kernel/time/hrtimer.c
@@ -799,6 +799,9 @@ u64 hrtimer_forward(struct hrtimer *timer, ktime_t now, ktime_t interval)
799 if (delta.tv64 < 0) 799 if (delta.tv64 < 0)
800 return 0; 800 return 0;
801 801
802 if (WARN_ON(timer->state & HRTIMER_STATE_ENQUEUED))
803 return 0;
804
802 if (interval.tv64 < hrtimer_resolution) 805 if (interval.tv64 < hrtimer_resolution)
803 interval.tv64 = hrtimer_resolution; 806 interval.tv64 = hrtimer_resolution;
804 807
@@ -1139,11 +1142,14 @@ static void __run_hrtimer(struct hrtimer_cpu_base *cpu_base,
1139 * Note: We clear the CALLBACK bit after enqueue_hrtimer and 1142 * Note: We clear the CALLBACK bit after enqueue_hrtimer and
1140 * we do not reprogramm the event hardware. Happens either in 1143 * we do not reprogramm the event hardware. Happens either in
1141 * hrtimer_start_range_ns() or in hrtimer_interrupt() 1144 * hrtimer_start_range_ns() or in hrtimer_interrupt()
1145 *
1146 * Note: Because we dropped the cpu_base->lock above,
1147 * hrtimer_start_range_ns() can have popped in and enqueued the timer
1148 * for us already.
1142 */ 1149 */
1143 if (restart != HRTIMER_NORESTART) { 1150 if (restart != HRTIMER_NORESTART &&
1144 BUG_ON(timer->state != HRTIMER_STATE_CALLBACK); 1151 !(timer->state & HRTIMER_STATE_ENQUEUED))
1145 enqueue_hrtimer(timer, base); 1152 enqueue_hrtimer(timer, base);
1146 }
1147 1153
1148 WARN_ON_ONCE(!(timer->state & HRTIMER_STATE_CALLBACK)); 1154 WARN_ON_ONCE(!(timer->state & HRTIMER_STATE_CALLBACK));
1149 1155