diff options
author | Thomas Gleixner <tglx@linutronix.de> | 2009-08-19 05:26:09 -0400 |
---|---|---|
committer | Thomas Gleixner <tglx@linutronix.de> | 2009-08-19 05:42:48 -0400 |
commit | d0981a1b21a03866c8da7f44e35e389c2e0d6061 (patch) | |
tree | 7fe013c7de9adb1d4f20163669571fddfe09881c /kernel/time/clocksource.c | |
parent | 6ea41d252f35465a2308a4038a323b6b07de06f6 (diff) |
clocksource: Protect the watchdog rating changes with clocksource_mutex
Martin pointed out that commit 6ea41d2529 (clocksource: Call
clocksource_change_rating() outside of watchdog_lock) has a
theoretical reference count problem. The calls to
clocksource_change_rating() are now done outside of the clocksource
mutex and outside of the watchdog lock. A concurrent
clocksource_unregister() could remove the clock.
Split out the code which changes the rating from
clocksource_change_rating() into __clocksource_change_rating().
Protect the clocksource_watchdog_work() code sequence with the
clocksource_mutex() and call __clocksource_change_rating().
LKML-Reference: <alpine.LFD.2.00.0908171038420.2782@localhost.localdomain>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Cc: Martin Schwidefsky <schwidefsky@de.ibm.com>
Diffstat (limited to 'kernel/time/clocksource.c')
-rw-r--r-- | kernel/time/clocksource.c | 18 |
1 files changed, 13 insertions, 5 deletions
diff --git a/kernel/time/clocksource.c b/kernel/time/clocksource.c index 02dc22d888fe..c6bff11f7957 100644 --- a/kernel/time/clocksource.c +++ b/kernel/time/clocksource.c | |||
@@ -131,6 +131,7 @@ static cycle_t watchdog_last; | |||
131 | static int watchdog_running; | 131 | static int watchdog_running; |
132 | 132 | ||
133 | static void clocksource_watchdog_work(struct work_struct *work); | 133 | static void clocksource_watchdog_work(struct work_struct *work); |
134 | static void __clocksource_change_rating(struct clocksource *cs, int rating); | ||
134 | 135 | ||
135 | /* | 136 | /* |
136 | * Interval: 0.5sec Threshold: 0.0625s | 137 | * Interval: 0.5sec Threshold: 0.0625s |
@@ -309,6 +310,7 @@ static void clocksource_watchdog_work(struct work_struct *work) | |||
309 | unsigned long flags; | 310 | unsigned long flags; |
310 | LIST_HEAD(unstable); | 311 | LIST_HEAD(unstable); |
311 | 312 | ||
313 | mutex_lock(&clocksource_mutex); | ||
312 | spin_lock_irqsave(&watchdog_lock, flags); | 314 | spin_lock_irqsave(&watchdog_lock, flags); |
313 | list_for_each_entry_safe(cs, tmp, &watchdog_list, wd_list) | 315 | list_for_each_entry_safe(cs, tmp, &watchdog_list, wd_list) |
314 | if (cs->flags & CLOCK_SOURCE_UNSTABLE) { | 316 | if (cs->flags & CLOCK_SOURCE_UNSTABLE) { |
@@ -322,8 +324,9 @@ static void clocksource_watchdog_work(struct work_struct *work) | |||
322 | /* Needs to be done outside of watchdog lock */ | 324 | /* Needs to be done outside of watchdog lock */ |
323 | list_for_each_entry_safe(cs, tmp, &unstable, wd_list) { | 325 | list_for_each_entry_safe(cs, tmp, &unstable, wd_list) { |
324 | list_del_init(&cs->wd_list); | 326 | list_del_init(&cs->wd_list); |
325 | clocksource_change_rating(cs, 0); | 327 | __clocksource_change_rating(cs, 0); |
326 | } | 328 | } |
329 | mutex_unlock(&clocksource_mutex); | ||
327 | } | 330 | } |
328 | 331 | ||
329 | #else /* CONFIG_CLOCKSOURCE_WATCHDOG */ | 332 | #else /* CONFIG_CLOCKSOURCE_WATCHDOG */ |
@@ -470,16 +473,21 @@ int clocksource_register(struct clocksource *cs) | |||
470 | } | 473 | } |
471 | EXPORT_SYMBOL(clocksource_register); | 474 | EXPORT_SYMBOL(clocksource_register); |
472 | 475 | ||
476 | static void __clocksource_change_rating(struct clocksource *cs, int rating) | ||
477 | { | ||
478 | list_del(&cs->list); | ||
479 | cs->rating = rating; | ||
480 | clocksource_enqueue(cs); | ||
481 | clocksource_select(); | ||
482 | } | ||
483 | |||
473 | /** | 484 | /** |
474 | * clocksource_change_rating - Change the rating of a registered clocksource | 485 | * clocksource_change_rating - Change the rating of a registered clocksource |
475 | */ | 486 | */ |
476 | void clocksource_change_rating(struct clocksource *cs, int rating) | 487 | void clocksource_change_rating(struct clocksource *cs, int rating) |
477 | { | 488 | { |
478 | mutex_lock(&clocksource_mutex); | 489 | mutex_lock(&clocksource_mutex); |
479 | list_del(&cs->list); | 490 | __clocksource_change_rating(cs, rating); |
480 | cs->rating = rating; | ||
481 | clocksource_enqueue(cs); | ||
482 | clocksource_select(); | ||
483 | mutex_unlock(&clocksource_mutex); | 491 | mutex_unlock(&clocksource_mutex); |
484 | } | 492 | } |
485 | EXPORT_SYMBOL(clocksource_change_rating); | 493 | EXPORT_SYMBOL(clocksource_change_rating); |