aboutsummaryrefslogtreecommitdiffstats
path: root/kernel
diff options
context:
space:
mode:
authorJohn Stultz <john.stultz@linaro.org>2015-03-12 00:16:35 -0400
committerIngo Molnar <mingo@kernel.org>2015-03-13 03:07:05 -0400
commit4ca22c2648f9c1cec0b242f58d7302136f5a4cbb (patch)
treea97024c64148b2286d9bce2470ac07b7614a5b82 /kernel
parent057b87e3161d1194a095718f9918c01b2c389e74 (diff)
timekeeping: Add warnings when overflows or underflows are observed
It was suggested that the underflow/overflow protection should probably throw some sort of warning out, rather than just silently fixing the issue. So this patch adds some warnings here. The flag variables used are not protected by locks, but since we can't print from the reading functions, just being able to say we saw an issue in the update interval is useful enough, and can be slightly racy without real consequence. The big complication is that we're only under a read seqlock, so the data could shift under us during our calculation to see if there was a problem. This patch avoids this issue by nesting another seqlock which allows us to snapshot the just required values atomically. So we shouldn't see false positives. I also added some basic rate-limiting here, since on one build machine w/ skewed TSCs it was fairly noisy at bootup. Signed-off-by: John Stultz <john.stultz@linaro.org> Cc: Dave Jones <davej@codemonkey.org.uk> Cc: Linus Torvalds <torvalds@linux-foundation.org> Cc: Peter Zijlstra <peterz@infradead.org> Cc: Prarit Bhargava <prarit@redhat.com> Cc: Richard Cochran <richardcochran@gmail.com> Cc: Stephen Boyd <sboyd@codeaurora.org> Cc: Thomas Gleixner <tglx@linutronix.de> Link: http://lkml.kernel.org/r/1426133800-29329-8-git-send-email-john.stultz@linaro.org Signed-off-by: Ingo Molnar <mingo@kernel.org>
Diffstat (limited to 'kernel')
-rw-r--r--kernel/time/timekeeping.c64
1 files changed, 57 insertions, 7 deletions
diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c
index 187149be83ea..892f6cbf1e67 100644
--- a/kernel/time/timekeeping.c
+++ b/kernel/time/timekeeping.c
@@ -119,6 +119,20 @@ static inline void tk_update_sleep_time(struct timekeeper *tk, ktime_t delta)
119} 119}
120 120
121#ifdef CONFIG_DEBUG_TIMEKEEPING 121#ifdef CONFIG_DEBUG_TIMEKEEPING
122#define WARNING_FREQ (HZ*300) /* 5 minute rate-limiting */
123/*
124 * These simple flag variables are managed
125 * without locks, which is racy, but ok since
126 * we don't really care about being super
127 * precise about how many events were seen,
128 * just that a problem was observed.
129 */
130static int timekeeping_underflow_seen;
131static int timekeeping_overflow_seen;
132
133/* last_warning is only modified under the timekeeping lock */
134static long timekeeping_last_warning;
135
122static void timekeeping_check_update(struct timekeeper *tk, cycle_t offset) 136static void timekeeping_check_update(struct timekeeper *tk, cycle_t offset)
123{ 137{
124 138
@@ -136,28 +150,64 @@ static void timekeeping_check_update(struct timekeeper *tk, cycle_t offset)
136 printk_deferred(" timekeeping: Your kernel is still fine, but is feeling a bit nervous\n"); 150 printk_deferred(" timekeeping: Your kernel is still fine, but is feeling a bit nervous\n");
137 } 151 }
138 } 152 }
153
154 if (timekeeping_underflow_seen) {
155 if (jiffies - timekeeping_last_warning > WARNING_FREQ) {
156 printk_deferred("WARNING: Underflow in clocksource '%s' observed, time update ignored.\n", name);
157 printk_deferred(" Please report this, consider using a different clocksource, if possible.\n");
158 printk_deferred(" Your kernel is probably still fine.\n");
159 timekeeping_last_warning = jiffies;
160 }
161 timekeeping_underflow_seen = 0;
162 }
163
164 if (timekeeping_overflow_seen) {
165 if (jiffies - timekeeping_last_warning > WARNING_FREQ) {
166 printk_deferred("WARNING: Overflow in clocksource '%s' observed, time update capped.\n", name);
167 printk_deferred(" Please report this, consider using a different clocksource, if possible.\n");
168 printk_deferred(" Your kernel is probably still fine.\n");
169 timekeeping_last_warning = jiffies;
170 }
171 timekeeping_overflow_seen = 0;
172 }
139} 173}
140 174
141static inline cycle_t timekeeping_get_delta(struct tk_read_base *tkr) 175static inline cycle_t timekeeping_get_delta(struct tk_read_base *tkr)
142{ 176{
143 cycle_t cycle_now, delta; 177 cycle_t now, last, mask, max, delta;
178 unsigned int seq;
144 179
145 /* read clocksource */ 180 /*
146 cycle_now = tkr->read(tkr->clock); 181 * Since we're called holding a seqlock, the data may shift
182 * under us while we're doing the calculation. This can cause
183 * false positives, since we'd note a problem but throw the
184 * results away. So nest another seqlock here to atomically
185 * grab the points we are checking with.
186 */
187 do {
188 seq = read_seqcount_begin(&tk_core.seq);
189 now = tkr->read(tkr->clock);
190 last = tkr->cycle_last;
191 mask = tkr->mask;
192 max = tkr->clock->max_cycles;
193 } while (read_seqcount_retry(&tk_core.seq, seq));
147 194
148 /* calculate the delta since the last update_wall_time */ 195 delta = clocksource_delta(now, last, mask);
149 delta = clocksource_delta(cycle_now, tkr->cycle_last, tkr->mask);
150 196
151 /* 197 /*
152 * Try to catch underflows by checking if we are seeing small 198 * Try to catch underflows by checking if we are seeing small
153 * mask-relative negative values. 199 * mask-relative negative values.
154 */ 200 */
155 if (unlikely((~delta & tkr->mask) < (tkr->mask >> 3))) 201 if (unlikely((~delta & mask) < (mask >> 3))) {
202 timekeeping_underflow_seen = 1;
156 delta = 0; 203 delta = 0;
204 }
157 205
158 /* Cap delta value to the max_cycles values to avoid mult overflows */ 206 /* Cap delta value to the max_cycles values to avoid mult overflows */
159 if (unlikely(delta > tkr->clock->max_cycles)) 207 if (unlikely(delta > max)) {
208 timekeeping_overflow_seen = 1;
160 delta = tkr->clock->max_cycles; 209 delta = tkr->clock->max_cycles;
210 }
161 211
162 return delta; 212 return delta;
163} 213}