diff options
author | Thomas Gleixner <tglx@linutronix.de> | 2007-03-06 04:42:08 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2007-03-06 12:30:25 -0500 |
commit | f8953856eb8dd62232aee6cacb46993dc2ac4869 (patch) | |
tree | ef8b7fd1b215b273b1cf5aca5dd3d7c02a497490 /kernel | |
parent | d1d67174b42a02c7d106894df0ed155d595871f7 (diff) |
[PATCH] highres: do not run the TIMER_SOFTIRQ after switching to highres mode
The TIMER_SOFTIRQ runs the hrtimers during bootup until a usable
clocksource and clock event sources are registered. The switch to high
resolution mode happens inside of the TIMER_SOFTIRQ, but runs the softirq
afterwards. That way the tick emulation timer, which was set up in the
switch to highres might be executed in the softirq context, which is a BUG.
The rbtree has not to be touched by the softirq after the highres switch.
This BUG was observed by Andres Salomon, who provided the information to
debug it.
Return early from the softirq, when the switch was sucessful.
[dilinger@debian.org: add debug warning]
[akpm@linux-foundation.org: make debug warning compile]
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Cc: Andres Salomon <dilinger@debian.org>
Acked-by: Ingo Molnar <mingo@elte.hu>
Signed-off-by: Andres Salomon <dilinger@debian.org>
Acked-by: Thomas Gleixner <tglx@linutronix.de>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'kernel')
-rw-r--r-- | kernel/hrtimer.c | 15 |
1 files changed, 10 insertions, 5 deletions
diff --git a/kernel/hrtimer.c b/kernel/hrtimer.c index de93a8176ca..ec4cb9f3e3b 100644 --- a/kernel/hrtimer.c +++ b/kernel/hrtimer.c | |||
@@ -540,19 +540,19 @@ static inline int hrtimer_enqueue_reprogram(struct hrtimer *timer, | |||
540 | /* | 540 | /* |
541 | * Switch to high resolution mode | 541 | * Switch to high resolution mode |
542 | */ | 542 | */ |
543 | static void hrtimer_switch_to_hres(void) | 543 | static int hrtimer_switch_to_hres(void) |
544 | { | 544 | { |
545 | struct hrtimer_cpu_base *base = &__get_cpu_var(hrtimer_bases); | 545 | struct hrtimer_cpu_base *base = &__get_cpu_var(hrtimer_bases); |
546 | unsigned long flags; | 546 | unsigned long flags; |
547 | 547 | ||
548 | if (base->hres_active) | 548 | if (base->hres_active) |
549 | return; | 549 | return 1; |
550 | 550 | ||
551 | local_irq_save(flags); | 551 | local_irq_save(flags); |
552 | 552 | ||
553 | if (tick_init_highres()) { | 553 | if (tick_init_highres()) { |
554 | local_irq_restore(flags); | 554 | local_irq_restore(flags); |
555 | return; | 555 | return 0; |
556 | } | 556 | } |
557 | base->hres_active = 1; | 557 | base->hres_active = 1; |
558 | base->clock_base[CLOCK_REALTIME].resolution = KTIME_HIGH_RES; | 558 | base->clock_base[CLOCK_REALTIME].resolution = KTIME_HIGH_RES; |
@@ -565,13 +565,14 @@ static void hrtimer_switch_to_hres(void) | |||
565 | local_irq_restore(flags); | 565 | local_irq_restore(flags); |
566 | printk(KERN_INFO "Switched to high resolution mode on CPU %d\n", | 566 | printk(KERN_INFO "Switched to high resolution mode on CPU %d\n", |
567 | smp_processor_id()); | 567 | smp_processor_id()); |
568 | return 1; | ||
568 | } | 569 | } |
569 | 570 | ||
570 | #else | 571 | #else |
571 | 572 | ||
572 | static inline int hrtimer_hres_active(void) { return 0; } | 573 | static inline int hrtimer_hres_active(void) { return 0; } |
573 | static inline int hrtimer_is_hres_enabled(void) { return 0; } | 574 | static inline int hrtimer_is_hres_enabled(void) { return 0; } |
574 | static inline void hrtimer_switch_to_hres(void) { } | 575 | static inline int hrtimer_switch_to_hres(void) { return 0; } |
575 | static inline void hrtimer_force_reprogram(struct hrtimer_cpu_base *base) { } | 576 | static inline void hrtimer_force_reprogram(struct hrtimer_cpu_base *base) { } |
576 | static inline int hrtimer_enqueue_reprogram(struct hrtimer *timer, | 577 | static inline int hrtimer_enqueue_reprogram(struct hrtimer *timer, |
577 | struct hrtimer_clock_base *base) | 578 | struct hrtimer_clock_base *base) |
@@ -1130,6 +1131,9 @@ static inline void run_hrtimer_queue(struct hrtimer_cpu_base *cpu_base, | |||
1130 | if (base->softirq_time.tv64 <= timer->expires.tv64) | 1131 | if (base->softirq_time.tv64 <= timer->expires.tv64) |
1131 | break; | 1132 | break; |
1132 | 1133 | ||
1134 | #ifdef CONFIG_HIGH_RES_TIMERS | ||
1135 | WARN_ON_ONCE(timer->cb_mode == HRTIMER_CB_IRQSAFE_NO_SOFTIRQ); | ||
1136 | #endif | ||
1133 | timer_stats_account_hrtimer(timer); | 1137 | timer_stats_account_hrtimer(timer); |
1134 | 1138 | ||
1135 | fn = timer->function; | 1139 | fn = timer->function; |
@@ -1173,7 +1177,8 @@ void hrtimer_run_queues(void) | |||
1173 | * deadlock vs. xtime_lock. | 1177 | * deadlock vs. xtime_lock. |
1174 | */ | 1178 | */ |
1175 | if (tick_check_oneshot_change(!hrtimer_is_hres_enabled())) | 1179 | if (tick_check_oneshot_change(!hrtimer_is_hres_enabled())) |
1176 | hrtimer_switch_to_hres(); | 1180 | if (hrtimer_switch_to_hres()) |
1181 | return; | ||
1177 | 1182 | ||
1178 | hrtimer_get_softirq_time(cpu_base); | 1183 | hrtimer_get_softirq_time(cpu_base); |
1179 | 1184 | ||