Commit fb63a0eb authored by Martin Schwidefsky's avatar Martin Schwidefsky Committed by Thomas Gleixner

clocksource: Refactor clocksource watchdog

Refactor clocksource watchdog code to make it more readable. Add
clocksource_dequeue_watchdog to remove a clocksource from the watchdog
list when it is unregistered.
Signed-off-by: default avatarMartin Schwidefsky <schwidefsky@de.ibm.com>
Cc: Ingo Molnar <mingo@elte.hu>
Acked-by: default avatarJohn Stultz <johnstul@us.ibm.com>
Cc: Daniel Walker <dwalker@fifo99.com>
LKML-Reference: <20090814134809.110881699@de.ibm.com>
Signed-off-by: default avatarThomas Gleixner <tglx@linutronix.de>
parent 0f8e8ef7
...@@ -145,6 +145,7 @@ static struct clocksource *watchdog; ...@@ -145,6 +145,7 @@ static struct clocksource *watchdog;
static struct timer_list watchdog_timer; static struct timer_list watchdog_timer;
static DEFINE_SPINLOCK(watchdog_lock); static DEFINE_SPINLOCK(watchdog_lock);
static cycle_t watchdog_last; static cycle_t watchdog_last;
static int watchdog_running;
/* /*
* Interval: 0.5sec Threshold: 0.0625s * Interval: 0.5sec Threshold: 0.0625s
...@@ -168,6 +169,8 @@ static void clocksource_watchdog(unsigned long data) ...@@ -168,6 +169,8 @@ static void clocksource_watchdog(unsigned long data)
int64_t wd_nsec, cs_nsec; int64_t wd_nsec, cs_nsec;
spin_lock(&watchdog_lock); spin_lock(&watchdog_lock);
if (!watchdog_running)
goto out;
wdnow = watchdog->read(watchdog); wdnow = watchdog->read(watchdog);
wd_nsec = cyc2ns(watchdog, (wdnow - watchdog_last) & watchdog->mask); wd_nsec = cyc2ns(watchdog, (wdnow - watchdog_last) & watchdog->mask);
...@@ -217,9 +220,30 @@ static void clocksource_watchdog(unsigned long data) ...@@ -217,9 +220,30 @@ static void clocksource_watchdog(unsigned long data)
watchdog_timer.expires += WATCHDOG_INTERVAL; watchdog_timer.expires += WATCHDOG_INTERVAL;
add_timer_on(&watchdog_timer, next_cpu); add_timer_on(&watchdog_timer, next_cpu);
} }
out:
spin_unlock(&watchdog_lock); spin_unlock(&watchdog_lock);
} }
static inline void clocksource_start_watchdog(void)
{
if (watchdog_running || !watchdog || list_empty(&watchdog_list))
return;
init_timer(&watchdog_timer);
watchdog_timer.function = clocksource_watchdog;
watchdog_last = watchdog->read(watchdog);
watchdog_timer.expires = jiffies + WATCHDOG_INTERVAL;
add_timer_on(&watchdog_timer, cpumask_first(cpu_online_mask));
watchdog_running = 1;
}
static inline void clocksource_stop_watchdog(void)
{
if (!watchdog_running || (watchdog && !list_empty(&watchdog_list)))
return;
del_timer(&watchdog_timer);
watchdog_running = 0;
}
static inline void clocksource_reset_watchdog(void) static inline void clocksource_reset_watchdog(void)
{ {
struct clocksource *cs; struct clocksource *cs;
...@@ -237,55 +261,70 @@ static void clocksource_resume_watchdog(void) ...@@ -237,55 +261,70 @@ static void clocksource_resume_watchdog(void)
spin_unlock_irqrestore(&watchdog_lock, flags); spin_unlock_irqrestore(&watchdog_lock, flags);
} }
static void clocksource_check_watchdog(struct clocksource *cs) static void clocksource_enqueue_watchdog(struct clocksource *cs)
{ {
unsigned long flags; unsigned long flags;
spin_lock_irqsave(&watchdog_lock, flags); spin_lock_irqsave(&watchdog_lock, flags);
if (cs->flags & CLOCK_SOURCE_MUST_VERIFY) { if (cs->flags & CLOCK_SOURCE_MUST_VERIFY) {
int started = !list_empty(&watchdog_list); /* cs is a clocksource to be watched. */
list_add(&cs->wd_list, &watchdog_list); list_add(&cs->wd_list, &watchdog_list);
if (!started && watchdog) { cs->flags &= ~CLOCK_SOURCE_WATCHDOG;
watchdog_last = watchdog->read(watchdog);
watchdog_timer.expires = jiffies + WATCHDOG_INTERVAL;
add_timer_on(&watchdog_timer,
cpumask_first(cpu_online_mask));
}
} else { } else {
/* cs is a watchdog. */
if (cs->flags & CLOCK_SOURCE_IS_CONTINUOUS) if (cs->flags & CLOCK_SOURCE_IS_CONTINUOUS)
cs->flags |= CLOCK_SOURCE_VALID_FOR_HRES; cs->flags |= CLOCK_SOURCE_VALID_FOR_HRES;
/* Pick the best watchdog. */
if (!watchdog || cs->rating > watchdog->rating) { if (!watchdog || cs->rating > watchdog->rating) {
if (watchdog)
del_timer(&watchdog_timer);
watchdog = cs; watchdog = cs;
init_timer(&watchdog_timer);
watchdog_timer.function = clocksource_watchdog;
/* Reset watchdog cycles */ /* Reset watchdog cycles */
clocksource_reset_watchdog(); clocksource_reset_watchdog();
/* Start if list is not empty */
if (!list_empty(&watchdog_list)) {
watchdog_last = watchdog->read(watchdog);
watchdog_timer.expires =
jiffies + WATCHDOG_INTERVAL;
add_timer_on(&watchdog_timer,
cpumask_first(cpu_online_mask));
} }
} }
/* Check if the watchdog timer needs to be started. */
clocksource_start_watchdog();
spin_unlock_irqrestore(&watchdog_lock, flags);
}
static void clocksource_dequeue_watchdog(struct clocksource *cs)
{
struct clocksource *tmp;
unsigned long flags;
spin_lock_irqsave(&watchdog_lock, flags);
if (cs->flags & CLOCK_SOURCE_MUST_VERIFY) {
/* cs is a watched clocksource. */
list_del_init(&cs->wd_list);
} else if (cs == watchdog) {
/* Reset watchdog cycles */
clocksource_reset_watchdog();
/* Current watchdog is removed. Find an alternative. */
watchdog = NULL;
list_for_each_entry(tmp, &clocksource_list, list) {
if (tmp == cs || tmp->flags & CLOCK_SOURCE_MUST_VERIFY)
continue;
if (!watchdog || tmp->rating > watchdog->rating)
watchdog = tmp;
}
} }
cs->flags &= ~CLOCK_SOURCE_WATCHDOG;
/* Check if the watchdog timer needs to be stopped. */
clocksource_stop_watchdog();
spin_unlock_irqrestore(&watchdog_lock, flags); spin_unlock_irqrestore(&watchdog_lock, flags);
} }
#else
static void clocksource_check_watchdog(struct clocksource *cs) #else /* CONFIG_CLOCKSOURCE_WATCHDOG */
static void clocksource_enqueue_watchdog(struct clocksource *cs)
{ {
if (cs->flags & CLOCK_SOURCE_IS_CONTINUOUS) if (cs->flags & CLOCK_SOURCE_IS_CONTINUOUS)
cs->flags |= CLOCK_SOURCE_VALID_FOR_HRES; cs->flags |= CLOCK_SOURCE_VALID_FOR_HRES;
} }
static inline void clocksource_dequeue_watchdog(struct clocksource *cs) { }
static inline void clocksource_resume_watchdog(void) { } static inline void clocksource_resume_watchdog(void) { }
#endif
#endif /* CONFIG_CLOCKSOURCE_WATCHDOG */
/** /**
* clocksource_resume - resume the clocksource(s) * clocksource_resume - resume the clocksource(s)
...@@ -414,14 +453,13 @@ int clocksource_register(struct clocksource *cs) ...@@ -414,14 +453,13 @@ int clocksource_register(struct clocksource *cs)
clocksource_enqueue(cs); clocksource_enqueue(cs);
clocksource_select(); clocksource_select();
spin_unlock_irqrestore(&clocksource_lock, flags); spin_unlock_irqrestore(&clocksource_lock, flags);
clocksource_check_watchdog(cs); clocksource_enqueue_watchdog(cs);
return 0; return 0;
} }
EXPORT_SYMBOL(clocksource_register); EXPORT_SYMBOL(clocksource_register);
/** /**
* 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)
{ {
...@@ -434,6 +472,7 @@ void clocksource_change_rating(struct clocksource *cs, int rating) ...@@ -434,6 +472,7 @@ void clocksource_change_rating(struct clocksource *cs, int rating)
clocksource_select(); clocksource_select();
spin_unlock_irqrestore(&clocksource_lock, flags); spin_unlock_irqrestore(&clocksource_lock, flags);
} }
EXPORT_SYMBOL(clocksource_change_rating);
/** /**
* clocksource_unregister - remove a registered clocksource * clocksource_unregister - remove a registered clocksource
...@@ -442,11 +481,13 @@ void clocksource_unregister(struct clocksource *cs) ...@@ -442,11 +481,13 @@ void clocksource_unregister(struct clocksource *cs)
{ {
unsigned long flags; unsigned long flags;
clocksource_dequeue_watchdog(cs);
spin_lock_irqsave(&clocksource_lock, flags); spin_lock_irqsave(&clocksource_lock, flags);
list_del(&cs->list); list_del(&cs->list);
clocksource_select(); clocksource_select();
spin_unlock_irqrestore(&clocksource_lock, flags); spin_unlock_irqrestore(&clocksource_lock, flags);
} }
EXPORT_SYMBOL(clocksource_unregister);
#ifdef CONFIG_SYSFS #ifdef CONFIG_SYSFS
/** /**
......
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