|
@@ -145,6 +145,7 @@ static struct clocksource *watchdog;
|
|
|
static struct timer_list watchdog_timer;
|
|
|
static DEFINE_SPINLOCK(watchdog_lock);
|
|
|
static cycle_t watchdog_last;
|
|
|
+static int watchdog_running;
|
|
|
|
|
|
/*
|
|
|
* Interval: 0.5sec Threshold: 0.0625s
|
|
@@ -168,6 +169,8 @@ static void clocksource_watchdog(unsigned long data)
|
|
|
int64_t wd_nsec, cs_nsec;
|
|
|
|
|
|
spin_lock(&watchdog_lock);
|
|
|
+ if (!watchdog_running)
|
|
|
+ goto out;
|
|
|
|
|
|
wdnow = watchdog->read(watchdog);
|
|
|
wd_nsec = cyc2ns(watchdog, (wdnow - watchdog_last) & watchdog->mask);
|
|
@@ -217,9 +220,30 @@ static void clocksource_watchdog(unsigned long data)
|
|
|
watchdog_timer.expires += WATCHDOG_INTERVAL;
|
|
|
add_timer_on(&watchdog_timer, next_cpu);
|
|
|
}
|
|
|
+out:
|
|
|
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)
|
|
|
{
|
|
|
struct clocksource *cs;
|
|
@@ -237,55 +261,70 @@ static void clocksource_resume_watchdog(void)
|
|
|
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;
|
|
|
|
|
|
spin_lock_irqsave(&watchdog_lock, flags);
|
|
|
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);
|
|
|
- if (!started && watchdog) {
|
|
|
- watchdog_last = watchdog->read(watchdog);
|
|
|
- watchdog_timer.expires = jiffies + WATCHDOG_INTERVAL;
|
|
|
- add_timer_on(&watchdog_timer,
|
|
|
- cpumask_first(cpu_online_mask));
|
|
|
- }
|
|
|
+ cs->flags &= ~CLOCK_SOURCE_WATCHDOG;
|
|
|
} else {
|
|
|
+ /* cs is a watchdog. */
|
|
|
if (cs->flags & CLOCK_SOURCE_IS_CONTINUOUS)
|
|
|
cs->flags |= CLOCK_SOURCE_VALID_FOR_HRES;
|
|
|
-
|
|
|
+ /* Pick the best watchdog. */
|
|
|
if (!watchdog || cs->rating > watchdog->rating) {
|
|
|
- if (watchdog)
|
|
|
- del_timer(&watchdog_timer);
|
|
|
watchdog = cs;
|
|
|
- init_timer(&watchdog_timer);
|
|
|
- watchdog_timer.function = clocksource_watchdog;
|
|
|
-
|
|
|
/* Reset watchdog cycles */
|
|
|
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);
|
|
|
}
|
|
|
-#else
|
|
|
-static void clocksource_check_watchdog(struct clocksource *cs)
|
|
|
+
|
|
|
+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);
|
|
|
+}
|
|
|
+
|
|
|
+#else /* CONFIG_CLOCKSOURCE_WATCHDOG */
|
|
|
+
|
|
|
+static void clocksource_enqueue_watchdog(struct clocksource *cs)
|
|
|
{
|
|
|
if (cs->flags & CLOCK_SOURCE_IS_CONTINUOUS)
|
|
|
cs->flags |= CLOCK_SOURCE_VALID_FOR_HRES;
|
|
|
}
|
|
|
|
|
|
+static inline void clocksource_dequeue_watchdog(struct clocksource *cs) { }
|
|
|
static inline void clocksource_resume_watchdog(void) { }
|
|
|
-#endif
|
|
|
+
|
|
|
+#endif /* CONFIG_CLOCKSOURCE_WATCHDOG */
|
|
|
|
|
|
/**
|
|
|
* clocksource_resume - resume the clocksource(s)
|
|
@@ -414,14 +453,13 @@ int clocksource_register(struct clocksource *cs)
|
|
|
clocksource_enqueue(cs);
|
|
|
clocksource_select();
|
|
|
spin_unlock_irqrestore(&clocksource_lock, flags);
|
|
|
- clocksource_check_watchdog(cs);
|
|
|
+ clocksource_enqueue_watchdog(cs);
|
|
|
return 0;
|
|
|
}
|
|
|
EXPORT_SYMBOL(clocksource_register);
|
|
|
|
|
|
/**
|
|
|
* clocksource_change_rating - Change the rating of a registered clocksource
|
|
|
- *
|
|
|
*/
|
|
|
void clocksource_change_rating(struct clocksource *cs, int rating)
|
|
|
{
|
|
@@ -434,6 +472,7 @@ void clocksource_change_rating(struct clocksource *cs, int rating)
|
|
|
clocksource_select();
|
|
|
spin_unlock_irqrestore(&clocksource_lock, flags);
|
|
|
}
|
|
|
+EXPORT_SYMBOL(clocksource_change_rating);
|
|
|
|
|
|
/**
|
|
|
* clocksource_unregister - remove a registered clocksource
|
|
@@ -442,11 +481,13 @@ void clocksource_unregister(struct clocksource *cs)
|
|
|
{
|
|
|
unsigned long flags;
|
|
|
|
|
|
+ clocksource_dequeue_watchdog(cs);
|
|
|
spin_lock_irqsave(&clocksource_lock, flags);
|
|
|
list_del(&cs->list);
|
|
|
clocksource_select();
|
|
|
spin_unlock_irqrestore(&clocksource_lock, flags);
|
|
|
}
|
|
|
+EXPORT_SYMBOL(clocksource_unregister);
|
|
|
|
|
|
#ifdef CONFIG_SYSFS
|
|
|
/**
|