diff options
Diffstat (limited to 'kernel/time/clocksource.c')
-rw-r--r-- | kernel/time/clocksource.c | 126 |
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 | |||
67 | late_initcall(clocksource_done_booting); | 66 | late_initcall(clocksource_done_booting); |
68 | 67 | ||
68 | #ifdef CONFIG_CLOCKSOURCE_WATCHDOG | ||
69 | static LIST_HEAD(watchdog_list); | ||
70 | static struct clocksource *watchdog; | ||
71 | static struct timer_list watchdog_timer; | ||
72 | static DEFINE_SPINLOCK(watchdog_lock); | ||
73 | static 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 | |||
80 | static 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 | |||
93 | static 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 | } | ||
129 | static 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 | ||
169 | static 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 | */ |
95 | static struct clocksource *select_clocksource(void) | 202 | static 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) | |||
138 | int clocksource_register(struct clocksource *c) | 253 | int 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 | } |
150 | EXPORT_SYMBOL(clocksource_register); | 267 | EXPORT_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); |