| From 3a142a0672b48a853f00af61f184c7341ac9c99d Mon Sep 17 00:00:00 2001 |
| From: Thomas Gleixner <tglx@linutronix.de> |
| Date: Fri, 25 Feb 2011 22:34:23 +0100 |
| Subject: clockevents: Prevent oneshot mode when broadcast device is periodic |
| |
| From: Thomas Gleixner <tglx@linutronix.de> |
| |
| commit 3a142a0672b48a853f00af61f184c7341ac9c99d upstream. |
| |
| When the per cpu timer is marked CLOCK_EVT_FEAT_C3STOP, then we only |
| can switch into oneshot mode, when the backup broadcast device |
| supports oneshot mode as well. Otherwise we would try to switch the |
| broadcast device into an unsupported mode unconditionally. This went |
| unnoticed so far as the current available broadcast devices support |
| oneshot mode. Seth unearthed this problem while debugging and working |
| around an hpet related BIOS wreckage. |
| |
| Add the necessary check to tick_is_oneshot_available(). |
| |
| Reported-and-tested-by: Seth Forshee <seth.forshee@canonical.com> |
| Signed-off-by: Thomas Gleixner <tglx@linutronix.de> |
| LKML-Reference: <alpine.LFD.2.00.1102252231200.2701@localhost6.localdomain6> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> |
| |
| --- |
| kernel/time/tick-broadcast.c | 10 ++++++++++ |
| kernel/time/tick-common.c | 6 +++++- |
| kernel/time/tick-internal.h | 3 +++ |
| 3 files changed, 18 insertions(+), 1 deletion(-) |
| |
| --- a/kernel/time/tick-broadcast.c |
| +++ b/kernel/time/tick-broadcast.c |
| @@ -600,4 +600,14 @@ int tick_broadcast_oneshot_active(void) |
| return tick_broadcast_device.mode == TICKDEV_MODE_ONESHOT; |
| } |
| |
| +/* |
| + * Check whether the broadcast device supports oneshot. |
| + */ |
| +bool tick_broadcast_oneshot_available(void) |
| +{ |
| + struct clock_event_device *bc = tick_broadcast_device.evtdev; |
| + |
| + return bc ? bc->features & CLOCK_EVT_FEAT_ONESHOT : false; |
| +} |
| + |
| #endif |
| --- a/kernel/time/tick-common.c |
| +++ b/kernel/time/tick-common.c |
| @@ -51,7 +51,11 @@ int tick_is_oneshot_available(void) |
| { |
| struct clock_event_device *dev = __get_cpu_var(tick_cpu_device).evtdev; |
| |
| - return dev && (dev->features & CLOCK_EVT_FEAT_ONESHOT); |
| + if (!dev || !(dev->features & CLOCK_EVT_FEAT_ONESHOT)) |
| + return 0; |
| + if (!(dev->features & CLOCK_EVT_FEAT_C3STOP)) |
| + return 1; |
| + return tick_broadcast_oneshot_available(); |
| } |
| |
| /* |
| --- a/kernel/time/tick-internal.h |
| +++ b/kernel/time/tick-internal.h |
| @@ -37,6 +37,7 @@ extern void tick_shutdown_broadcast_ones |
| extern int tick_resume_broadcast_oneshot(struct clock_event_device *bc); |
| extern int tick_broadcast_oneshot_active(void); |
| extern void tick_check_oneshot_broadcast(int cpu); |
| +bool tick_broadcast_oneshot_available(void); |
| # else /* BROADCAST */ |
| static inline void tick_broadcast_setup_oneshot(struct clock_event_device *bc) |
| { |
| @@ -47,6 +48,7 @@ static inline void tick_broadcast_switch |
| static inline void tick_shutdown_broadcast_oneshot(unsigned int *cpup) { } |
| static inline int tick_broadcast_oneshot_active(void) { return 0; } |
| static inline void tick_check_oneshot_broadcast(int cpu) { } |
| +static inline bool tick_broadcast_oneshot_available(void) { return true; } |
| # endif /* !BROADCAST */ |
| |
| #else /* !ONESHOT */ |
| @@ -77,6 +79,7 @@ static inline int tick_resume_broadcast_ |
| return 0; |
| } |
| static inline int tick_broadcast_oneshot_active(void) { return 0; } |
| +static inline bool tick_broadcast_oneshot_available(void) { return false; } |
| #endif /* !TICK_ONESHOT */ |
| |
| /* |