Commit d0981a1b authored by Thomas Gleixner's avatar Thomas Gleixner

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: default avatarThomas Gleixner <tglx@linutronix.de>
Cc: Martin Schwidefsky <schwidefsky@de.ibm.com>
parent 6ea41d25
...@@ -131,6 +131,7 @@ static cycle_t watchdog_last; ...@@ -131,6 +131,7 @@ static cycle_t watchdog_last;
static int watchdog_running; static int watchdog_running;
static void clocksource_watchdog_work(struct work_struct *work); static void clocksource_watchdog_work(struct work_struct *work);
static void __clocksource_change_rating(struct clocksource *cs, int rating);
/* /*
* Interval: 0.5sec Threshold: 0.0625s * Interval: 0.5sec Threshold: 0.0625s
...@@ -309,6 +310,7 @@ static void clocksource_watchdog_work(struct work_struct *work) ...@@ -309,6 +310,7 @@ static void clocksource_watchdog_work(struct work_struct *work)
unsigned long flags; unsigned long flags;
LIST_HEAD(unstable); LIST_HEAD(unstable);
mutex_lock(&clocksource_mutex);
spin_lock_irqsave(&watchdog_lock, flags); spin_lock_irqsave(&watchdog_lock, flags);
list_for_each_entry_safe(cs, tmp, &watchdog_list, wd_list) list_for_each_entry_safe(cs, tmp, &watchdog_list, wd_list)
if (cs->flags & CLOCK_SOURCE_UNSTABLE) { if (cs->flags & CLOCK_SOURCE_UNSTABLE) {
...@@ -322,8 +324,9 @@ static void clocksource_watchdog_work(struct work_struct *work) ...@@ -322,8 +324,9 @@ static void clocksource_watchdog_work(struct work_struct *work)
/* Needs to be done outside of watchdog lock */ /* Needs to be done outside of watchdog lock */
list_for_each_entry_safe(cs, tmp, &unstable, wd_list) { list_for_each_entry_safe(cs, tmp, &unstable, wd_list) {
list_del_init(&cs->wd_list); list_del_init(&cs->wd_list);
clocksource_change_rating(cs, 0); __clocksource_change_rating(cs, 0);
} }
mutex_unlock(&clocksource_mutex);
} }
#else /* CONFIG_CLOCKSOURCE_WATCHDOG */ #else /* CONFIG_CLOCKSOURCE_WATCHDOG */
...@@ -470,16 +473,21 @@ int clocksource_register(struct clocksource *cs) ...@@ -470,16 +473,21 @@ int clocksource_register(struct clocksource *cs)
} }
EXPORT_SYMBOL(clocksource_register); EXPORT_SYMBOL(clocksource_register);
static void __clocksource_change_rating(struct clocksource *cs, int rating)
{
list_del(&cs->list);
cs->rating = rating;
clocksource_enqueue(cs);
clocksource_select();
}
/** /**
* clocksource_change_rating - Change the rating of a registered clocksource * clocksource_change_rating - Change the rating of a registered clocksource
*/ */
void clocksource_change_rating(struct clocksource *cs, int rating) void clocksource_change_rating(struct clocksource *cs, int rating)
{ {
mutex_lock(&clocksource_mutex); mutex_lock(&clocksource_mutex);
list_del(&cs->list); __clocksource_change_rating(cs, rating);
cs->rating = rating;
clocksource_enqueue(cs);
clocksource_select();
mutex_unlock(&clocksource_mutex); mutex_unlock(&clocksource_mutex);
} }
EXPORT_SYMBOL(clocksource_change_rating); EXPORT_SYMBOL(clocksource_change_rating);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment