aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorThomas Gleixner <tglx@linutronix.de>2009-08-19 05:26:09 -0400
committerThomas Gleixner <tglx@linutronix.de>2009-08-19 05:42:48 -0400
commitd0981a1b21a03866c8da7f44e35e389c2e0d6061 (patch)
tree7fe013c7de9adb1d4f20163669571fddfe09881c
parent6ea41d252f35465a2308a4038a323b6b07de06f6 (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>
-rw-r--r--kernel/time/clocksource.c18
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;
131static int watchdog_running; 131static int watchdog_running;
132 132
133static void clocksource_watchdog_work(struct work_struct *work); 133static void clocksource_watchdog_work(struct work_struct *work);
134static 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}
471EXPORT_SYMBOL(clocksource_register); 474EXPORT_SYMBOL(clocksource_register);
472 475
476static 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 */
476void clocksource_change_rating(struct clocksource *cs, int rating) 487void 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}
485EXPORT_SYMBOL(clocksource_change_rating); 493EXPORT_SYMBOL(clocksource_change_rating);