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 ]
Unified · Split
| @@ -640,21 +640,9 @@ static inline void hrtimer_init_hres(struct hrtimer_cpu_base *base) · 639 unmodified lines | ||
| 640 | 640 | * and expiry check is done in the hrtimer_interrupt or in the softirq. |
| 641 | 641 | */ |
| 642 | 642 | static inline int hrtimer_enqueue_reprogram(struct hrtimer *timer, |
| 643 | − struct hrtimer_clock_base *base, | |
| 644 | − int wakeup) | |
| 643 | + struct hrtimer_clock_base *base) | |
| 645 | 644 | { |
| 646 | − if (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; | |
| 645 | + return base->cpu_base->hres_active && hrtimer_reprogram(timer, base); | |
| 658 | 646 | } |
| 659 | 647 | |
| 660 | 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; } · 74 unmodified lines | ||
| 735 | 723 | static inline void |
| 736 | 724 | hrtimer_force_reprogram(struct hrtimer_cpu_base *base, int skip_equal) { } |
| 737 | 725 | static inline int hrtimer_enqueue_reprogram(struct hrtimer *timer, |
| 738 | − struct hrtimer_clock_base *base, | |
| 739 | − int wakeup) | |
| 726 | + struct hrtimer_clock_base *base) | |
| 740 | 727 | { |
| 741 | 728 | return 0; |
| 742 | 729 | } |
| @@ -995,8 +982,21 @@ int __hrtimer_start_range_ns(struct hrtimer *timer, ktime_t tim, · 252 unmodified lines | ||
| 995 | 982 | * |
| 996 | 983 | * XXX send_remote_softirq() ? |
| 997 | 984 | */ |
| 998 | − if (leftmost && new_base->cpu_base == &__get_cpu_var(hrtimer_bases)) | |
| 999 | − hrtimer_enqueue_reprogram(timer, new_base, wakeup); | |
| 985 | + if (leftmost && new_base->cpu_base == &__get_cpu_var(hrtimer_bases) | |
| 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 | 1001 | unlock_hrtimer_base(timer, &flags); |
| 1002 | 1002 | |