]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/blob - releases/2.6.35.8/hrtimer-preserve-timer-state-in-remove_hrtimer.patch
4.9-stable patches
[thirdparty/kernel/stable-queue.git] / releases / 2.6.35.8 / hrtimer-preserve-timer-state-in-remove_hrtimer.patch
1 From f13d4f979c518119bba5439dd2364d76d31dcd3f Mon Sep 17 00:00:00 2001
2 From: Salman Qazi <sqazi@google.com>
3 Date: Tue, 12 Oct 2010 07:25:19 -0700
4 Subject: hrtimer: Preserve timer state in remove_hrtimer()
5
6 From: Salman Qazi <sqazi@google.com>
7
8 commit f13d4f979c518119bba5439dd2364d76d31dcd3f upstream.
9
10 The race is described as follows:
11
12 CPU X CPU Y
13 remove_hrtimer
14 // state & QUEUED == 0
15 timer->state = CALLBACK
16 unlock timer base
17 timer->f(n) //very long
18 hrtimer_start
19 lock timer base
20 remove_hrtimer // no effect
21 hrtimer_enqueue
22 timer->state = CALLBACK |
23 QUEUED
24 unlock timer base
25 hrtimer_start
26 lock timer base
27 remove_hrtimer
28 mode = INACTIVE
29 // CALLBACK bit lost!
30 switch_hrtimer_base
31 CALLBACK bit not set:
32 timer->base
33 changes to a
34 different CPU.
35 lock this CPU's timer base
36
37 The bug was introduced with commit ca109491f (hrtimer: removing all ur
38 callback modes) in 2.6.29
39
40 [ tglx: Feed new state via local variable and add a comment. ]
41
42 Signed-off-by: Salman Qazi <sqazi@google.com>
43 Cc: akpm@linux-foundation.org
44 Cc: Peter Zijlstra <peterz@infradead.org>
45 LKML-Reference: <20101012142351.8485.21823.stgit@dungbeetle.mtv.corp.google.com>
46 Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
47 Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
48
49 ---
50 kernel/hrtimer.c | 13 +++++++++++--
51 1 file changed, 11 insertions(+), 2 deletions(-)
52
53 --- a/kernel/hrtimer.c
54 +++ b/kernel/hrtimer.c
55 @@ -936,6 +936,7 @@ static inline int
56 remove_hrtimer(struct hrtimer *timer, struct hrtimer_clock_base *base)
57 {
58 if (hrtimer_is_queued(timer)) {
59 + unsigned long state;
60 int reprogram;
61
62 /*
63 @@ -949,8 +950,13 @@ remove_hrtimer(struct hrtimer *timer, st
64 debug_deactivate(timer);
65 timer_stats_hrtimer_clear_start_info(timer);
66 reprogram = base->cpu_base == &__get_cpu_var(hrtimer_bases);
67 - __remove_hrtimer(timer, base, HRTIMER_STATE_INACTIVE,
68 - reprogram);
69 + /*
70 + * We must preserve the CALLBACK state flag here,
71 + * otherwise we could move the timer base in
72 + * switch_hrtimer_base.
73 + */
74 + state = timer->state & HRTIMER_STATE_CALLBACK;
75 + __remove_hrtimer(timer, base, state, reprogram);
76 return 1;
77 }
78 return 0;
79 @@ -1237,6 +1243,9 @@ static void __run_hrtimer(struct hrtimer
80 BUG_ON(timer->state != HRTIMER_STATE_CALLBACK);
81 enqueue_hrtimer(timer, base);
82 }
83 +
84 + WARN_ON_ONCE(!(timer->state & HRTIMER_STATE_CALLBACK));
85 +
86 timer->state &= ~HRTIMER_STATE_CALLBACK;
87 }
88