Commit f13d4f979c518119bba5439dd2364d76d31dcd3f

Authored by Salman Qazi
Committed by Thomas Gleixner
1 parent 53eeb64e80

hrtimer: Preserve timer state in remove_hrtimer()

The race is described as follows:

CPU X                                 CPU Y
remove_hrtimer
// state & QUEUED == 0
timer->state = CALLBACK
unlock timer base
timer->f(n) //very long
                                  hrtimer_start
                                    lock timer base
                                    remove_hrtimer // no effect
                                    hrtimer_enqueue
                                    timer->state = CALLBACK |
                                                   QUEUED
                                    unlock timer base
                                  hrtimer_start
                                    lock timer base
                                    remove_hrtimer
                                        mode = INACTIVE
                                        // CALLBACK bit lost!
                                    switch_hrtimer_base
                                            CALLBACK bit not set:
                                                    timer->base
                                                    changes to a
                                                    different CPU.
lock this CPU's timer base

The bug was introduced with commit ca109491f (hrtimer: removing all ur
callback modes) in 2.6.29

[ tglx: Feed new state via local variable and add a comment. ]

Signed-off-by: Salman Qazi <sqazi@google.com>
Cc: akpm@linux-foundation.org
Cc: Peter Zijlstra <peterz@infradead.org>
LKML-Reference: <20101012142351.8485.21823.stgit@dungbeetle.mtv.corp.google.com>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Cc: stable@kernel.org

Showing 1 changed file with 11 additions and 2 deletions Side-by-side Diff

... ... @@ -931,6 +931,7 @@
931 931 remove_hrtimer(struct hrtimer *timer, struct hrtimer_clock_base *base)
932 932 {
933 933 if (hrtimer_is_queued(timer)) {
  934 + unsigned long state;
934 935 int reprogram;
935 936  
936 937 /*
... ... @@ -944,8 +945,13 @@
944 945 debug_deactivate(timer);
945 946 timer_stats_hrtimer_clear_start_info(timer);
946 947 reprogram = base->cpu_base == &__get_cpu_var(hrtimer_bases);
947   - __remove_hrtimer(timer, base, HRTIMER_STATE_INACTIVE,
948   - reprogram);
  948 + /*
  949 + * We must preserve the CALLBACK state flag here,
  950 + * otherwise we could move the timer base in
  951 + * switch_hrtimer_base.
  952 + */
  953 + state = timer->state & HRTIMER_STATE_CALLBACK;
  954 + __remove_hrtimer(timer, base, state, reprogram);
949 955 return 1;
950 956 }
951 957 return 0;
... ... @@ -1231,6 +1237,9 @@
1231 1237 BUG_ON(timer->state != HRTIMER_STATE_CALLBACK);
1232 1238 enqueue_hrtimer(timer, base);
1233 1239 }
  1240 +
  1241 + WARN_ON_ONCE(!(timer->state & HRTIMER_STATE_CALLBACK));
  1242 +
1234 1243 timer->state &= ~HRTIMER_STATE_CALLBACK;
1235 1244 }
1236 1245