| From 07f4beb0b5bbfaf36a64aa00d59e670ec578a95a Mon Sep 17 00:00:00 2001 |
| From: Thomas Gleixner <tglx@linutronix.de> |
| Date: Mon, 16 May 2011 11:07:48 +0200 |
| Subject: tick: Clear broadcast active bit when switching to oneshot |
| |
| From: Thomas Gleixner <tglx@linutronix.de> |
| |
| commit 07f4beb0b5bbfaf36a64aa00d59e670ec578a95a upstream. |
| |
| The first cpu which switches from periodic to oneshot mode switches |
| also the broadcast device into oneshot mode. The broadcast device |
| serves as a backup for per cpu timers which stop in deeper |
| C-states. To avoid starvation of the cpus which might be in idle and |
| depend on broadcast mode it marks the other cpus as broadcast active |
| and sets the brodcast expiry value of those cpus to the next tick. |
| |
| The oneshot mode broadcast bit for the other cpus is sticky and gets |
| only cleared when those cpus exit idle. If a cpu was not idle while |
| the bit got set in consequence the bit prevents that the broadcast |
| device is armed on behalf of that cpu when it enters idle for the |
| first time after it switched to oneshot mode. |
| |
| In most cases that goes unnoticed as one of the other cpus has usually |
| a timer pending which keeps the broadcast device armed with a short |
| timeout. Now if the only cpu which has a short timer active has the |
| bit set then the broadcast device will not be armed on behalf of that |
| cpu and will fire way after the expected timer expiry. In the case of |
| Christians bug report it took ~145 seconds which is about half of the |
| wrap around time of HPET (the limit for that device) due to the fact |
| that all other cpus had no timers armed which expired before the 145 |
| seconds timeframe. |
| |
| The solution is simply to clear the broadcast active bit |
| unconditionally when a cpu switches to oneshot mode after the first |
| cpu switched the broadcast device over. It's not idle at that point |
| otherwise it would not be executing that code. |
| |
| [ I fundamentally hate that broadcast crap. Why the heck thought some |
| folks that when going into deep idle it's a brilliant concept to |
| switch off the last device which brings the cpu back from that |
| state? ] |
| |
| Thanks to Christian for providing all the valuable debug information! |
| |
| Reported-and-tested-by: Christian Hoffmann <email@christianhoffmann.info> |
| Cc: John Stultz <johnstul@us.ibm.com> |
| Link: http://lkml.kernel.org/r/%3Calpine.LFD.2.02.1105161105170.3078%40ionos%3E |
| Signed-off-by: Thomas Gleixner <tglx@linutronix.de> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> |
| |
| --- |
| kernel/time/tick-broadcast.c | 12 +++++++++++- |
| 1 file changed, 11 insertions(+), 1 deletion(-) |
| |
| --- a/kernel/time/tick-broadcast.c |
| +++ b/kernel/time/tick-broadcast.c |
| @@ -523,10 +523,11 @@ static void tick_broadcast_init_next_eve |
| */ |
| void tick_broadcast_setup_oneshot(struct clock_event_device *bc) |
| { |
| + int cpu = smp_processor_id(); |
| + |
| /* Set it up only once ! */ |
| if (bc->event_handler != tick_handle_oneshot_broadcast) { |
| int was_periodic = bc->mode == CLOCK_EVT_MODE_PERIODIC; |
| - int cpu = smp_processor_id(); |
| |
| bc->event_handler = tick_handle_oneshot_broadcast; |
| clockevents_set_mode(bc, CLOCK_EVT_MODE_ONESHOT); |
| @@ -552,6 +553,15 @@ void tick_broadcast_setup_oneshot(struct |
| tick_broadcast_set_event(tick_next_period, 1); |
| } else |
| bc->next_event.tv64 = KTIME_MAX; |
| + } else { |
| + /* |
| + * The first cpu which switches to oneshot mode sets |
| + * the bit for all other cpus which are in the general |
| + * (periodic) broadcast mask. So the bit is set and |
| + * would prevent the first broadcast enter after this |
| + * to program the bc device. |
| + */ |
| + tick_broadcast_clear_oneshot(cpu); |
| } |
| } |
| |