diff options
author | Leonid Shatz <leonid.shatz@ravellosystems.com> | 2013-02-04 07:33:37 -0500 |
---|---|---|
committer | Thomas Gleixner <tglx@linutronix.de> | 2013-02-05 05:52:41 -0500 |
commit | b22affe0aef429d657bc6505aacb1c569340ddd2 (patch) | |
tree | 6cdb5271ab0f4d02ccca8130fb0c0de8f61fe37a /kernel/hrtimer.c | |
parent | 90889a635a9b5488624bccce3ff6b2eec68c007b (diff) |
hrtimer: Prevent hrtimer_enqueue_reprogram race
hrtimer_enqueue_reprogram contains a race which could result in
timer.base switch during unlock/lock sequence.
hrtimer_enqueue_reprogram is releasing the lock protecting the timer
base for calling raise_softirq_irqsoff() due to a lock ordering issue
versus rq->lock.
If during that time another CPU calls __hrtimer_start_range_ns() on
the same hrtimer, the timer base might switch, before the current CPU
can lock base->lock again and therefor the unlock_timer_base() call
will unlock the wrong lock.
[ tglx: Added comment and massaged changelog ]
Signed-off-by: Leonid Shatz <leonid.shatz@ravellosystems.com>
Signed-off-by: Izik Eidus <izik.eidus@ravellosystems.com>
Cc: Andrea Arcangeli <aarcange@redhat.com>
Cc: stable@vger.kernel.org
Link: http://lkml.kernel.org/r/1359981217-389-1-git-send-email-izik.eidus@ravellosystems.com
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Diffstat (limited to 'kernel/hrtimer.c')
-rw-r--r-- | kernel/hrtimer.c | 36 |
1 files changed, 18 insertions, 18 deletions
diff --git a/kernel/hrtimer.c b/kernel/hrtimer.c index 6db7a5ed52b5..cdd5607c0ceb 100644 --- a/kernel/hrtimer.c +++ b/kernel/hrtimer.c | |||
@@ -640,21 +640,9 @@ static inline void hrtimer_init_hres(struct hrtimer_cpu_base *base) | |||
640 | * and expiry check is done in the hrtimer_interrupt or in the softirq. | 640 | * and expiry check is done in the hrtimer_interrupt or in the softirq. |
641 | */ | 641 | */ |
642 | static inline int hrtimer_enqueue_reprogram(struct hrtimer *timer, | 642 | static inline int hrtimer_enqueue_reprogram(struct hrtimer *timer, |
643 | struct hrtimer_clock_base *base, | 643 | struct hrtimer_clock_base *base) |
644 | int wakeup) | ||
645 | { | 644 | { |
646 | if (base->cpu_base->hres_active && hrtimer_reprogram(timer, base)) { | 645 | return base->cpu_base->hres_active && hrtimer_reprogram(timer, base); |
647 | if (wakeup) { | ||
648 | raw_spin_unlock(&base->cpu_base->lock); | ||
649 | raise_softirq_irqoff(HRTIMER_SOFTIRQ); | ||
650 | raw_spin_lock(&base->cpu_base->lock); | ||
651 | } else | ||
652 | __raise_softirq_irqoff(HRTIMER_SOFTIRQ); | ||
653 | |||
654 | return 1; | ||
655 | } | ||
656 | |||
657 | return 0; | ||
658 | } | 646 | } |
659 | 647 | ||
660 | static inline ktime_t hrtimer_update_base(struct hrtimer_cpu_base *base) | 648 | static inline ktime_t hrtimer_update_base(struct hrtimer_cpu_base *base) |
@@ -735,8 +723,7 @@ static inline int hrtimer_switch_to_hres(void) { return 0; } | |||
735 | static inline void | 723 | static inline void |
736 | hrtimer_force_reprogram(struct hrtimer_cpu_base *base, int skip_equal) { } | 724 | hrtimer_force_reprogram(struct hrtimer_cpu_base *base, int skip_equal) { } |
737 | static inline int hrtimer_enqueue_reprogram(struct hrtimer *timer, | 725 | static inline int hrtimer_enqueue_reprogram(struct hrtimer *timer, |
738 | struct hrtimer_clock_base *base, | 726 | struct hrtimer_clock_base *base) |
739 | int wakeup) | ||
740 | { | 727 | { |
741 | return 0; | 728 | return 0; |
742 | } | 729 | } |
@@ -995,8 +982,21 @@ int __hrtimer_start_range_ns(struct hrtimer *timer, ktime_t tim, | |||
995 | * | 982 | * |
996 | * XXX send_remote_softirq() ? | 983 | * XXX send_remote_softirq() ? |
997 | */ | 984 | */ |
998 | if (leftmost && new_base->cpu_base == &__get_cpu_var(hrtimer_bases)) | 985 | if (leftmost && new_base->cpu_base == &__get_cpu_var(hrtimer_bases) |
999 | hrtimer_enqueue_reprogram(timer, new_base, wakeup); | 986 | && hrtimer_enqueue_reprogram(timer, new_base)) { |
987 | if (wakeup) { | ||
988 | /* | ||
989 | * We need to drop cpu_base->lock to avoid a | ||
990 | * lock ordering issue vs. rq->lock. | ||
991 | */ | ||
992 | raw_spin_unlock(&new_base->cpu_base->lock); | ||
993 | raise_softirq_irqoff(HRTIMER_SOFTIRQ); | ||
994 | local_irq_restore(flags); | ||
995 | return ret; | ||
996 | } else { | ||
997 | __raise_softirq_irqoff(HRTIMER_SOFTIRQ); | ||
998 | } | ||
999 | } | ||
1000 | 1000 | ||
1001 | unlock_hrtimer_base(timer, &flags); | 1001 | unlock_hrtimer_base(timer, &flags); |
1002 | 1002 | ||