diff options
author | Peter Zijlstra <a.p.zijlstra@chello.nl> | 2009-03-13 07:21:27 -0400 |
---|---|---|
committer | Ingo Molnar <mingo@elte.hu> | 2009-03-31 08:52:52 -0400 |
commit | 7f1e2ca9f04b02794597f60e7b1d43f0a1317939 (patch) | |
tree | dad1d80ab4232f3ca12d7ff9a58f0dc76133dab1 | |
parent | 7bee946358c3cb957d4aa648fc5ab3cad0b232d0 (diff) |
hrtimer: fix rq->lock inversion (again)
It appears I inadvertly introduced rq->lock recursion to the
hrtimer_start() path when I delegated running already expired
timers to softirq context.
This patch fixes it by introducing a __hrtimer_start_range_ns()
method that will not use raise_softirq_irqoff() but
__raise_softirq_irqoff() which avoids the wakeup.
It then also changes schedule() to check for pending softirqs and
do the wakeup then, I'm not quite sure I like this last bit, nor
am I convinced its really needed.
Signed-off-by: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: paulus@samba.org
LKML-Reference: <20090313112301.096138802@chello.nl>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
-rw-r--r-- | include/linux/hrtimer.h | 5 | ||||
-rw-r--r-- | include/linux/interrupt.h | 1 | ||||
-rw-r--r-- | kernel/hrtimer.c | 55 | ||||
-rw-r--r-- | kernel/sched.c | 14 | ||||
-rw-r--r-- | kernel/softirq.c | 2 |
5 files changed, 52 insertions, 25 deletions
diff --git a/include/linux/hrtimer.h b/include/linux/hrtimer.h index bd37078c2d7d..0d2f7c8a33d6 100644 --- a/include/linux/hrtimer.h +++ b/include/linux/hrtimer.h | |||
@@ -336,6 +336,11 @@ extern int hrtimer_start(struct hrtimer *timer, ktime_t tim, | |||
336 | const enum hrtimer_mode mode); | 336 | const enum hrtimer_mode mode); |
337 | extern int hrtimer_start_range_ns(struct hrtimer *timer, ktime_t tim, | 337 | extern int hrtimer_start_range_ns(struct hrtimer *timer, ktime_t tim, |
338 | unsigned long range_ns, const enum hrtimer_mode mode); | 338 | unsigned long range_ns, const enum hrtimer_mode mode); |
339 | extern int | ||
340 | __hrtimer_start_range_ns(struct hrtimer *timer, ktime_t tim, | ||
341 | unsigned long delta_ns, | ||
342 | const enum hrtimer_mode mode, int wakeup); | ||
343 | |||
339 | extern int hrtimer_cancel(struct hrtimer *timer); | 344 | extern int hrtimer_cancel(struct hrtimer *timer); |
340 | extern int hrtimer_try_to_cancel(struct hrtimer *timer); | 345 | extern int hrtimer_try_to_cancel(struct hrtimer *timer); |
341 | 346 | ||
diff --git a/include/linux/interrupt.h b/include/linux/interrupt.h index c68bffd182bb..4528bf70866a 100644 --- a/include/linux/interrupt.h +++ b/include/linux/interrupt.h | |||
@@ -294,6 +294,7 @@ extern void softirq_init(void); | |||
294 | #define __raise_softirq_irqoff(nr) do { or_softirq_pending(1UL << (nr)); } while (0) | 294 | #define __raise_softirq_irqoff(nr) do { or_softirq_pending(1UL << (nr)); } while (0) |
295 | extern void raise_softirq_irqoff(unsigned int nr); | 295 | extern void raise_softirq_irqoff(unsigned int nr); |
296 | extern void raise_softirq(unsigned int nr); | 296 | extern void raise_softirq(unsigned int nr); |
297 | extern void wakeup_softirqd(void); | ||
297 | 298 | ||
298 | /* This is the worklist that queues up per-cpu softirq work. | 299 | /* This is the worklist that queues up per-cpu softirq work. |
299 | * | 300 | * |
diff --git a/kernel/hrtimer.c b/kernel/hrtimer.c index f394d2a42ca3..cb8a15c19583 100644 --- a/kernel/hrtimer.c +++ b/kernel/hrtimer.c | |||
@@ -651,14 +651,20 @@ static inline void hrtimer_init_timer_hres(struct hrtimer *timer) | |||
651 | * and expiry check is done in the hrtimer_interrupt or in the softirq. | 651 | * and expiry check is done in the hrtimer_interrupt or in the softirq. |
652 | */ | 652 | */ |
653 | static inline int hrtimer_enqueue_reprogram(struct hrtimer *timer, | 653 | static inline int hrtimer_enqueue_reprogram(struct hrtimer *timer, |
654 | struct hrtimer_clock_base *base) | 654 | struct hrtimer_clock_base *base, |
655 | int wakeup) | ||
655 | { | 656 | { |
656 | if (base->cpu_base->hres_active && hrtimer_reprogram(timer, base)) { | 657 | if (base->cpu_base->hres_active && hrtimer_reprogram(timer, base)) { |
657 | spin_unlock(&base->cpu_base->lock); | 658 | if (wakeup) { |
658 | raise_softirq_irqoff(HRTIMER_SOFTIRQ); | 659 | spin_unlock(&base->cpu_base->lock); |
659 | spin_lock(&base->cpu_base->lock); | 660 | raise_softirq_irqoff(HRTIMER_SOFTIRQ); |
661 | spin_lock(&base->cpu_base->lock); | ||
662 | } else | ||
663 | __raise_softirq_irqoff(HRTIMER_SOFTIRQ); | ||
664 | |||
660 | return 1; | 665 | return 1; |
661 | } | 666 | } |
667 | |||
662 | return 0; | 668 | return 0; |
663 | } | 669 | } |
664 | 670 | ||
@@ -703,7 +709,8 @@ static inline int hrtimer_is_hres_enabled(void) { return 0; } | |||
703 | static inline int hrtimer_switch_to_hres(void) { return 0; } | 709 | static inline int hrtimer_switch_to_hres(void) { return 0; } |
704 | static inline void hrtimer_force_reprogram(struct hrtimer_cpu_base *base) { } | 710 | static inline void hrtimer_force_reprogram(struct hrtimer_cpu_base *base) { } |
705 | static inline int hrtimer_enqueue_reprogram(struct hrtimer *timer, | 711 | static inline int hrtimer_enqueue_reprogram(struct hrtimer *timer, |
706 | struct hrtimer_clock_base *base) | 712 | struct hrtimer_clock_base *base, |
713 | int wakeup) | ||
707 | { | 714 | { |
708 | return 0; | 715 | return 0; |
709 | } | 716 | } |
@@ -886,20 +893,9 @@ remove_hrtimer(struct hrtimer *timer, struct hrtimer_clock_base *base) | |||
886 | return 0; | 893 | return 0; |
887 | } | 894 | } |
888 | 895 | ||
889 | /** | 896 | int __hrtimer_start_range_ns(struct hrtimer *timer, ktime_t tim, |
890 | * hrtimer_start_range_ns - (re)start an hrtimer on the current CPU | 897 | unsigned long delta_ns, const enum hrtimer_mode mode, |
891 | * @timer: the timer to be added | 898 | int wakeup) |
892 | * @tim: expiry time | ||
893 | * @delta_ns: "slack" range for the timer | ||
894 | * @mode: expiry mode: absolute (HRTIMER_ABS) or relative (HRTIMER_REL) | ||
895 | * | ||
896 | * Returns: | ||
897 | * 0 on success | ||
898 | * 1 when the timer was active | ||
899 | */ | ||
900 | int | ||
901 | hrtimer_start_range_ns(struct hrtimer *timer, ktime_t tim, unsigned long delta_ns, | ||
902 | const enum hrtimer_mode mode) | ||
903 | { | 899 | { |
904 | struct hrtimer_clock_base *base, *new_base; | 900 | struct hrtimer_clock_base *base, *new_base; |
905 | unsigned long flags; | 901 | unsigned long flags; |
@@ -940,12 +936,29 @@ hrtimer_start_range_ns(struct hrtimer *timer, ktime_t tim, unsigned long delta_n | |||
940 | * XXX send_remote_softirq() ? | 936 | * XXX send_remote_softirq() ? |
941 | */ | 937 | */ |
942 | if (leftmost && new_base->cpu_base == &__get_cpu_var(hrtimer_bases)) | 938 | if (leftmost && new_base->cpu_base == &__get_cpu_var(hrtimer_bases)) |
943 | hrtimer_enqueue_reprogram(timer, new_base); | 939 | hrtimer_enqueue_reprogram(timer, new_base, wakeup); |
944 | 940 | ||
945 | unlock_hrtimer_base(timer, &flags); | 941 | unlock_hrtimer_base(timer, &flags); |
946 | 942 | ||
947 | return ret; | 943 | return ret; |
948 | } | 944 | } |
945 | |||
946 | /** | ||
947 | * hrtimer_start_range_ns - (re)start an hrtimer on the current CPU | ||
948 | * @timer: the timer to be added | ||
949 | * @tim: expiry time | ||
950 | * @delta_ns: "slack" range for the timer | ||
951 | * @mode: expiry mode: absolute (HRTIMER_ABS) or relative (HRTIMER_REL) | ||
952 | * | ||
953 | * Returns: | ||
954 | * 0 on success | ||
955 | * 1 when the timer was active | ||
956 | */ | ||
957 | int hrtimer_start_range_ns(struct hrtimer *timer, ktime_t tim, | ||
958 | unsigned long delta_ns, const enum hrtimer_mode mode) | ||
959 | { | ||
960 | return __hrtimer_start_range_ns(timer, tim, delta_ns, mode, 1); | ||
961 | } | ||
949 | EXPORT_SYMBOL_GPL(hrtimer_start_range_ns); | 962 | EXPORT_SYMBOL_GPL(hrtimer_start_range_ns); |
950 | 963 | ||
951 | /** | 964 | /** |
@@ -961,7 +974,7 @@ EXPORT_SYMBOL_GPL(hrtimer_start_range_ns); | |||
961 | int | 974 | int |
962 | hrtimer_start(struct hrtimer *timer, ktime_t tim, const enum hrtimer_mode mode) | 975 | hrtimer_start(struct hrtimer *timer, ktime_t tim, const enum hrtimer_mode mode) |
963 | { | 976 | { |
964 | return hrtimer_start_range_ns(timer, tim, 0, mode); | 977 | return __hrtimer_start_range_ns(timer, tim, 0, mode, 1); |
965 | } | 978 | } |
966 | EXPORT_SYMBOL_GPL(hrtimer_start); | 979 | EXPORT_SYMBOL_GPL(hrtimer_start); |
967 | 980 | ||
diff --git a/kernel/sched.c b/kernel/sched.c index 196d48babbef..63256e3ede2a 100644 --- a/kernel/sched.c +++ b/kernel/sched.c | |||
@@ -231,13 +231,20 @@ static void start_rt_bandwidth(struct rt_bandwidth *rt_b) | |||
231 | 231 | ||
232 | spin_lock(&rt_b->rt_runtime_lock); | 232 | spin_lock(&rt_b->rt_runtime_lock); |
233 | for (;;) { | 233 | for (;;) { |
234 | unsigned long delta; | ||
235 | ktime_t soft, hard; | ||
236 | |||
234 | if (hrtimer_active(&rt_b->rt_period_timer)) | 237 | if (hrtimer_active(&rt_b->rt_period_timer)) |
235 | break; | 238 | break; |
236 | 239 | ||
237 | now = hrtimer_cb_get_time(&rt_b->rt_period_timer); | 240 | now = hrtimer_cb_get_time(&rt_b->rt_period_timer); |
238 | hrtimer_forward(&rt_b->rt_period_timer, now, rt_b->rt_period); | 241 | hrtimer_forward(&rt_b->rt_period_timer, now, rt_b->rt_period); |
239 | hrtimer_start_expires(&rt_b->rt_period_timer, | 242 | |
240 | HRTIMER_MODE_ABS); | 243 | soft = hrtimer_get_softexpires(&rt_b->rt_period_timer); |
244 | hard = hrtimer_get_expires(&rt_b->rt_period_timer); | ||
245 | delta = ktime_to_ns(ktime_sub(hard, soft)); | ||
246 | __hrtimer_start_range_ns(&rt_b->rt_period_timer, soft, delta, | ||
247 | HRTIMER_MODE_ABS, 0); | ||
241 | } | 248 | } |
242 | spin_unlock(&rt_b->rt_runtime_lock); | 249 | spin_unlock(&rt_b->rt_runtime_lock); |
243 | } | 250 | } |
@@ -1146,7 +1153,8 @@ static __init void init_hrtick(void) | |||
1146 | */ | 1153 | */ |
1147 | static void hrtick_start(struct rq *rq, u64 delay) | 1154 | static void hrtick_start(struct rq *rq, u64 delay) |
1148 | { | 1155 | { |
1149 | hrtimer_start(&rq->hrtick_timer, ns_to_ktime(delay), HRTIMER_MODE_REL); | 1156 | __hrtimer_start_range_ns(&rq->hrtick_timer, ns_to_ktime(delay), 0, |
1157 | HRTIMER_MODE_REL, 0); | ||
1150 | } | 1158 | } |
1151 | 1159 | ||
1152 | static inline void init_hrtick(void) | 1160 | static inline void init_hrtick(void) |
diff --git a/kernel/softirq.c b/kernel/softirq.c index 487751604300..accc85197c49 100644 --- a/kernel/softirq.c +++ b/kernel/softirq.c | |||
@@ -58,7 +58,7 @@ static DEFINE_PER_CPU(struct task_struct *, ksoftirqd); | |||
58 | * to the pending events, so lets the scheduler to balance | 58 | * to the pending events, so lets the scheduler to balance |
59 | * the softirq load for us. | 59 | * the softirq load for us. |
60 | */ | 60 | */ |
61 | static inline void wakeup_softirqd(void) | 61 | void wakeup_softirqd(void) |
62 | { | 62 | { |
63 | /* Interrupts are disabled: no need to stop preemption */ | 63 | /* Interrupts are disabled: no need to stop preemption */ |
64 | struct task_struct *tsk = __get_cpu_var(ksoftirqd); | 64 | struct task_struct *tsk = __get_cpu_var(ksoftirqd); |