diff options
author | Jason Liu <r64343@freescale.com> | 2013-02-25 19:55:04 +0800 |
---|---|---|
committer | Jason Liu <r64343@freescale.com> | 2013-03-01 17:30:05 +0800 |
commit | 33ddde4e10a08294274d018a758cd9288f646f21 (patch) | |
tree | 40e749e66ba83b2c0792eadbe1d931d851995fb0 /include | |
parent | 4316456edd187dcf5ff0b814c26c7bb39855bf40 (diff) |
timer: fix the too many reries on the per-cpu event device
There are so many retries happen on the per-cpu event device
when run the command 'cat /proc/timer_list', as following:
root@~$ cat /proc/timer_list
Timer List Version: v0.6
HRTIMER_MAX_CLOCK_BASES: 3
now at 3297691988044 nsecs
Tick Device: mode: 1
Per CPU device: 0
Clock Event Device: local_timer
max_delta_ns: 8624432320
min_delta_ns: 1000
mult: 2138893713
shift: 32
mode: 3
next_event: 3297700000000 nsecs
set_next_event: twd_set_next_event
set_mode: twd_set_mode
event_handler: hrtimer_interrupt
retries: 36383
the reason is that the local timer will stop when enter C3 state,
we need switch the local timer to bc timer when enter the state
and switch back when exit from the that state.The code is like this:
void arch_idle(void)
{
....
clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_ENTER, &cpu);
enter_the_wait_mode();
clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_EXIT, &cpu);
}
when the broadcast timer interrupt arrives(this interrupt just wakeup
the ARM, and ARM has no chance to handle it since local irq is disabled.
In fact it's disabled in cpu_idle() of arch/arm/kernel/process.c)
the broadcast timer interrupt will wake up the CPU and run:
clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_EXIT, &cpu); ->
tick_broadcast_oneshot_control(...);
->
tick_program_event(dev->next_event, 1);
->
tick_dev_program_event(dev, expires, force);
->
for (i = 0;;) {
int ret = clockevents_program_event(dev, expires, now);
if (!ret || !force)
return ret;
dev->retries++;
....
now = ktime_get();
expires = ktime_add_ns(now, dev->min_delta_ns);
}
clockevents_program_event(dev, expires, now);
delta = ktime_to_ns(ktime_sub(expires, now));
if (delta <= 0)
return -ETIME;
when the bc timer interrupt arrives, which means the last local timer
expires too. so, clockevents_program_event will return -ETIME, which will
cause the dev->retries++ when retry to program the expired timer.
Even under the worst case, after the re-program the expired timer,
then CPU enter idle quickly before the re-progam timer expired,
it will make system ping-pang forever if no interrupt happen.
We have found the ping-pang issue during the video play-back test.
system will freeze and video not playing for sometime until other interrupt
occured to break the error condition.
The detailed information, please refer to the LKML:https://lkml.org/lkml/2013/2/20/216
which posted by Jason Liu.
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Signed-off-by: Jason Liu <r64343@freescale.com>
Tested-by: Jason Liu <r64343@freescale.com>
Tested-by: Santosh Shilimkar <santosh.shilimkar@ti.com>
Tested-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
Diffstat (limited to 'include')
-rw-r--r-- | include/linux/clockchips.h | 6 |
1 files changed, 6 insertions, 0 deletions
diff --git a/include/linux/clockchips.h b/include/linux/clockchips.h index d6733e27af34..acc14353dea5 100644 --- a/include/linux/clockchips.h +++ b/include/linux/clockchips.h @@ -151,6 +151,12 @@ clockevents_calc_mult_shift(struct clock_event_device *ce, u32 freq, u32 minsec) freq, minsec); } +#if defined(CONFIG_GENERIC_CLOCKEVENTS_BROADCAST) && defined(CONFIG_TICK_ONESHOT) +extern int tick_check_broadcast_pending(void); +#else +static inline int tick_check_broadcast_pending(void) { return 0; } +#endif + #ifdef CONFIG_GENERIC_CLOCKEVENTS extern void clockevents_notify(unsigned long reason, void *arg); #else |