diff options
author | Thomas Gleixner <tglx@linutronix.de> | 2011-06-16 10:22:08 -0400 |
---|---|---|
committer | Thomas Gleixner <tglx@linutronix.de> | 2011-06-16 13:30:53 -0400 |
commit | b5199515c25cca622495eb9c6a8a1d275e775088 (patch) | |
tree | 250fc4f3d02c58eddf770e9d040edeafd0012e76 | |
parent | 1123d93963cbd2546449d4d9f0c568e323cb0ac6 (diff) |
clocksource: Make watchdog robust vs. interruption
The clocksource watchdog code is interruptible and it has been
observed that this can trigger false positives which disable the TSC.
The reason is that an interrupt storm or a long running interrupt
handler between the read of the watchdog source and the read of the
TSC brings the two far enough apart that the delta is larger than the
unstable treshold. Move both reads into a short interrupt disabled
region to avoid that.
Reported-and-tested-by: Vernon Mauery <vernux@us.ibm.com>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Cc: stable@kernel.org
-rw-r--r-- | include/linux/clocksource.h | 1 | ||||
-rw-r--r-- | kernel/time/clocksource.c | 24 |
2 files changed, 14 insertions, 11 deletions
diff --git a/include/linux/clocksource.h b/include/linux/clocksource.h index d4646b48dc4a..18a1baf31f2d 100644 --- a/include/linux/clocksource.h +++ b/include/linux/clocksource.h | |||
@@ -188,6 +188,7 @@ struct clocksource { | |||
188 | #ifdef CONFIG_CLOCKSOURCE_WATCHDOG | 188 | #ifdef CONFIG_CLOCKSOURCE_WATCHDOG |
189 | /* Watchdog related data, used by the framework */ | 189 | /* Watchdog related data, used by the framework */ |
190 | struct list_head wd_list; | 190 | struct list_head wd_list; |
191 | cycle_t cs_last; | ||
191 | cycle_t wd_last; | 192 | cycle_t wd_last; |
192 | #endif | 193 | #endif |
193 | } ____cacheline_aligned; | 194 | } ____cacheline_aligned; |
diff --git a/kernel/time/clocksource.c b/kernel/time/clocksource.c index 1c95fd677328..e0980f0d9a0a 100644 --- a/kernel/time/clocksource.c +++ b/kernel/time/clocksource.c | |||
@@ -185,7 +185,6 @@ static struct clocksource *watchdog; | |||
185 | static struct timer_list watchdog_timer; | 185 | static struct timer_list watchdog_timer; |
186 | static DECLARE_WORK(watchdog_work, clocksource_watchdog_work); | 186 | static DECLARE_WORK(watchdog_work, clocksource_watchdog_work); |
187 | static DEFINE_SPINLOCK(watchdog_lock); | 187 | static DEFINE_SPINLOCK(watchdog_lock); |
188 | static cycle_t watchdog_last; | ||
189 | static int watchdog_running; | 188 | static int watchdog_running; |
190 | 189 | ||
191 | static int clocksource_watchdog_kthread(void *data); | 190 | static int clocksource_watchdog_kthread(void *data); |
@@ -254,11 +253,6 @@ static void clocksource_watchdog(unsigned long data) | |||
254 | if (!watchdog_running) | 253 | if (!watchdog_running) |
255 | goto out; | 254 | goto out; |
256 | 255 | ||
257 | wdnow = watchdog->read(watchdog); | ||
258 | wd_nsec = clocksource_cyc2ns((wdnow - watchdog_last) & watchdog->mask, | ||
259 | watchdog->mult, watchdog->shift); | ||
260 | watchdog_last = wdnow; | ||
261 | |||
262 | list_for_each_entry(cs, &watchdog_list, wd_list) { | 256 | list_for_each_entry(cs, &watchdog_list, wd_list) { |
263 | 257 | ||
264 | /* Clocksource already marked unstable? */ | 258 | /* Clocksource already marked unstable? */ |
@@ -268,19 +262,28 @@ static void clocksource_watchdog(unsigned long data) | |||
268 | continue; | 262 | continue; |
269 | } | 263 | } |
270 | 264 | ||
265 | local_irq_disable(); | ||
271 | csnow = cs->read(cs); | 266 | csnow = cs->read(cs); |
267 | wdnow = watchdog->read(watchdog); | ||
268 | local_irq_enable(); | ||
272 | 269 | ||
273 | /* Clocksource initialized ? */ | 270 | /* Clocksource initialized ? */ |
274 | if (!(cs->flags & CLOCK_SOURCE_WATCHDOG)) { | 271 | if (!(cs->flags & CLOCK_SOURCE_WATCHDOG)) { |
275 | cs->flags |= CLOCK_SOURCE_WATCHDOG; | 272 | cs->flags |= CLOCK_SOURCE_WATCHDOG; |
276 | cs->wd_last = csnow; | 273 | cs->wd_last = wdnow; |
274 | cs->cs_last = csnow; | ||
277 | continue; | 275 | continue; |
278 | } | 276 | } |
279 | 277 | ||
280 | /* Check the deviation from the watchdog clocksource. */ | 278 | wd_nsec = clocksource_cyc2ns((wdnow - cs->wd_last) & watchdog->mask, |
281 | cs_nsec = clocksource_cyc2ns((csnow - cs->wd_last) & | 279 | watchdog->mult, watchdog->shift); |
280 | |||
281 | cs_nsec = clocksource_cyc2ns((csnow - cs->cs_last) & | ||
282 | cs->mask, cs->mult, cs->shift); | 282 | cs->mask, cs->mult, cs->shift); |
283 | cs->wd_last = csnow; | 283 | cs->cs_last = csnow; |
284 | cs->wd_last = wdnow; | ||
285 | |||
286 | /* Check the deviation from the watchdog clocksource. */ | ||
284 | if (abs(cs_nsec - wd_nsec) > WATCHDOG_THRESHOLD) { | 287 | if (abs(cs_nsec - wd_nsec) > WATCHDOG_THRESHOLD) { |
285 | clocksource_unstable(cs, cs_nsec - wd_nsec); | 288 | clocksource_unstable(cs, cs_nsec - wd_nsec); |
286 | continue; | 289 | continue; |
@@ -318,7 +321,6 @@ static inline void clocksource_start_watchdog(void) | |||
318 | return; | 321 | return; |
319 | init_timer(&watchdog_timer); | 322 | init_timer(&watchdog_timer); |
320 | watchdog_timer.function = clocksource_watchdog; | 323 | watchdog_timer.function = clocksource_watchdog; |
321 | watchdog_last = watchdog->read(watchdog); | ||
322 | watchdog_timer.expires = jiffies + WATCHDOG_INTERVAL; | 324 | watchdog_timer.expires = jiffies + WATCHDOG_INTERVAL; |
323 | add_timer_on(&watchdog_timer, cpumask_first(cpu_online_mask)); | 325 | add_timer_on(&watchdog_timer, cpumask_first(cpu_online_mask)); |
324 | watchdog_running = 1; | 326 | watchdog_running = 1; |