aboutsummaryrefslogtreecommitdiffstats
path: root/kernel/time/clocksource.c
diff options
context:
space:
mode:
Diffstat (limited to 'kernel/time/clocksource.c')
-rw-r--r--kernel/time/clocksource.c126
1 files changed, 122 insertions, 4 deletions
diff --git a/kernel/time/clocksource.c b/kernel/time/clocksource.c
index 2f6a3d6e43bc..3cb8ac978270 100644
--- a/kernel/time/clocksource.c
+++ b/kernel/time/clocksource.c
@@ -63,9 +63,116 @@ static int __init clocksource_done_booting(void)
63 finished_booting = 1; 63 finished_booting = 1;
64 return 0; 64 return 0;
65} 65}
66
67late_initcall(clocksource_done_booting); 66late_initcall(clocksource_done_booting);
68 67
68#ifdef CONFIG_CLOCKSOURCE_WATCHDOG
69static LIST_HEAD(watchdog_list);
70static struct clocksource *watchdog;
71static struct timer_list watchdog_timer;
72static DEFINE_SPINLOCK(watchdog_lock);
73static cycle_t watchdog_last;
74/*
75 * Interval: 0.5sec Treshold: 0.0625s
76 */
77#define WATCHDOG_INTERVAL (HZ >> 1)
78#define WATCHDOG_TRESHOLD (NSEC_PER_SEC >> 4)
79
80static void clocksource_ratewd(struct clocksource *cs, int64_t delta)
81{
82 if (delta > -WATCHDOG_TRESHOLD && delta < WATCHDOG_TRESHOLD)
83 return;
84
85 printk(KERN_WARNING "Clocksource %s unstable (delta = %Ld ns)\n",
86 cs->name, delta);
87 cs->flags &= ~(CLOCK_SOURCE_VALID_FOR_HRES | CLOCK_SOURCE_WATCHDOG);
88 clocksource_change_rating(cs, 0);
89 cs->flags &= ~CLOCK_SOURCE_WATCHDOG;
90 list_del(&cs->wd_list);
91}
92
93static void clocksource_watchdog(unsigned long data)
94{
95 struct clocksource *cs, *tmp;
96 cycle_t csnow, wdnow;
97 int64_t wd_nsec, cs_nsec;
98
99 spin_lock(&watchdog_lock);
100
101 wdnow = watchdog->read();
102 wd_nsec = cyc2ns(watchdog, (wdnow - watchdog_last) & watchdog->mask);
103 watchdog_last = wdnow;
104
105 list_for_each_entry_safe(cs, tmp, &watchdog_list, wd_list) {
106 csnow = cs->read();
107 /* Initialized ? */
108 if (!(cs->flags & CLOCK_SOURCE_WATCHDOG)) {
109 if ((cs->flags & CLOCK_SOURCE_IS_CONTINUOUS) &&
110 (watchdog->flags & CLOCK_SOURCE_IS_CONTINUOUS)) {
111 cs->flags |= CLOCK_SOURCE_VALID_FOR_HRES;
112 }
113 cs->flags |= CLOCK_SOURCE_WATCHDOG;
114 cs->wd_last = csnow;
115 } else {
116 cs_nsec = cyc2ns(cs, (csnow - cs->wd_last) & cs->mask);
117 cs->wd_last = csnow;
118 /* Check the delta. Might remove from the list ! */
119 clocksource_ratewd(cs, cs_nsec - wd_nsec);
120 }
121 }
122
123 if (!list_empty(&watchdog_list)) {
124 __mod_timer(&watchdog_timer,
125 watchdog_timer.expires + WATCHDOG_INTERVAL);
126 }
127 spin_unlock(&watchdog_lock);
128}
129static void clocksource_check_watchdog(struct clocksource *cs)
130{
131 struct clocksource *cse;
132 unsigned long flags;
133
134 spin_lock_irqsave(&watchdog_lock, flags);
135 if (cs->flags & CLOCK_SOURCE_MUST_VERIFY) {
136 int started = !list_empty(&watchdog_list);
137
138 list_add(&cs->wd_list, &watchdog_list);
139 if (!started && watchdog) {
140 watchdog_last = watchdog->read();
141 watchdog_timer.expires = jiffies + WATCHDOG_INTERVAL;
142 add_timer(&watchdog_timer);
143 }
144 } else if (cs->flags & CLOCK_SOURCE_IS_CONTINUOUS) {
145 cs->flags |= CLOCK_SOURCE_VALID_FOR_HRES;
146
147 if (!watchdog || cs->rating > watchdog->rating) {
148 if (watchdog)
149 del_timer(&watchdog_timer);
150 watchdog = cs;
151 init_timer(&watchdog_timer);
152 watchdog_timer.function = clocksource_watchdog;
153
154 /* Reset watchdog cycles */
155 list_for_each_entry(cse, &watchdog_list, wd_list)
156 cse->flags &= ~CLOCK_SOURCE_WATCHDOG;
157 /* Start if list is not empty */
158 if (!list_empty(&watchdog_list)) {
159 watchdog_last = watchdog->read();
160 watchdog_timer.expires =
161 jiffies + WATCHDOG_INTERVAL;
162 add_timer(&watchdog_timer);
163 }
164 }
165 }
166 spin_unlock_irqrestore(&watchdog_lock, flags);
167}
168#else
169static void clocksource_check_watchdog(struct clocksource *cs)
170{
171 if (cs->flags & CLOCK_SOURCE_IS_CONTINUOUS)
172 cs->flags |= CLOCK_SOURCE_VALID_FOR_HRES;
173}
174#endif
175
69/** 176/**
70 * clocksource_get_next - Returns the selected clocksource 177 * clocksource_get_next - Returns the selected clocksource
71 * 178 *
@@ -94,13 +201,21 @@ struct clocksource *clocksource_get_next(void)
94 */ 201 */
95static struct clocksource *select_clocksource(void) 202static struct clocksource *select_clocksource(void)
96{ 203{
204 struct clocksource *next;
205
97 if (list_empty(&clocksource_list)) 206 if (list_empty(&clocksource_list))
98 return NULL; 207 return NULL;
99 208
100 if (clocksource_override) 209 if (clocksource_override)
101 return clocksource_override; 210 next = clocksource_override;
211 else
212 next = list_entry(clocksource_list.next, struct clocksource,
213 list);
102 214
103 return list_entry(clocksource_list.next, struct clocksource, list); 215 if (next == curr_clocksource)
216 return NULL;
217
218 return next;
104} 219}
105 220
106/* 221/*
@@ -138,13 +253,15 @@ static int clocksource_enqueue(struct clocksource *c)
138int clocksource_register(struct clocksource *c) 253int clocksource_register(struct clocksource *c)
139{ 254{
140 unsigned long flags; 255 unsigned long flags;
141 int ret = 0; 256 int ret;
142 257
143 spin_lock_irqsave(&clocksource_lock, flags); 258 spin_lock_irqsave(&clocksource_lock, flags);
144 ret = clocksource_enqueue(c); 259 ret = clocksource_enqueue(c);
145 if (!ret) 260 if (!ret)
146 next_clocksource = select_clocksource(); 261 next_clocksource = select_clocksource();
147 spin_unlock_irqrestore(&clocksource_lock, flags); 262 spin_unlock_irqrestore(&clocksource_lock, flags);
263 if (!ret)
264 clocksource_check_watchdog(c);
148 return ret; 265 return ret;
149} 266}
150EXPORT_SYMBOL(clocksource_register); 267EXPORT_SYMBOL(clocksource_register);
@@ -159,6 +276,7 @@ void clocksource_change_rating(struct clocksource *cs, int rating)
159 276
160 spin_lock_irqsave(&clocksource_lock, flags); 277 spin_lock_irqsave(&clocksource_lock, flags);
161 list_del(&cs->list); 278 list_del(&cs->list);
279 cs->rating = rating;
162 clocksource_enqueue(cs); 280 clocksource_enqueue(cs);
163 next_clocksource = select_clocksource(); 281 next_clocksource = select_clocksource();
164 spin_unlock_irqrestore(&clocksource_lock, flags); 282 spin_unlock_irqrestore(&clocksource_lock, flags);