aboutsummaryrefslogtreecommitdiffstats
path: root/kernel/time
diff options
context:
space:
mode:
authorJohn Stultz <john.stultz@linaro.org>2012-09-11 19:26:03 -0400
committerIngo Molnar <mingo@kernel.org>2012-09-13 11:39:14 -0400
commitec145babe754f9ea1079034a108104b6001e001c (patch)
tree2b65fdc6916989cacdc2bbcc94a48ef847eae099 /kernel/time
parent0bd1189e239c76eb3a50e458548fbe7e4a5dfff1 (diff)
time: Fix timeekeping_get_ns overflow on 32bit systems
Daniel Lezcano reported seeing multi-second stalls from keyboard input on his T61 laptop when NOHZ and CPU_IDLE were enabled on a 32bit kernel. He bisected the problem down to commit 1e75fa8be9fb6 ("time: Condense timekeeper.xtime into xtime_sec"). After reproducing this issue, I narrowed the problem down to the fact that timekeeping_get_ns() returns a 64bit nsec value that hasn't been accumulated. In some cases this value was being then stored in timespec.tv_nsec (which is a long). On 32bit systems, with idle times larger then 4 seconds (or less, depending on the value of xtime_nsec), the returned nsec value would overflow 32bits. This limited kept time from increasing, causing timers to not expire. The fix is to make sure we don't directly store the result of timekeeping_get_ns() into a tv_nsec field, instead using a 64bit nsec value which can then be added into the timespec via timespec_add_ns(). Reported-and-bisected-by: Daniel Lezcano <daniel.lezcano@linaro.org> Tested-by: Daniel Lezcano <daniel.lezcano@linaro.org> Signed-off-by: John Stultz <john.stultz@linaro.org> Acked-by: Prarit Bhargava <prarit@redhat.com> Cc: Richard Cochran <richardcochran@gmail.com> Link: http://lkml.kernel.org/r/1347405963-35715-1-git-send-email-john.stultz@linaro.org Signed-off-by: Ingo Molnar <mingo@kernel.org>
Diffstat (limited to 'kernel/time')
-rw-r--r--kernel/time/timekeeping.c19
1 files changed, 12 insertions, 7 deletions
diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c
index 34e5eac8142..d3b91e75cec 100644
--- a/kernel/time/timekeeping.c
+++ b/kernel/time/timekeeping.c
@@ -303,10 +303,11 @@ void getnstimeofday(struct timespec *ts)
303 seq = read_seqbegin(&tk->lock); 303 seq = read_seqbegin(&tk->lock);
304 304
305 ts->tv_sec = tk->xtime_sec; 305 ts->tv_sec = tk->xtime_sec;
306 ts->tv_nsec = timekeeping_get_ns(tk); 306 nsecs = timekeeping_get_ns(tk);
307 307
308 } while (read_seqretry(&tk->lock, seq)); 308 } while (read_seqretry(&tk->lock, seq));
309 309
310 ts->tv_nsec = 0;
310 timespec_add_ns(ts, nsecs); 311 timespec_add_ns(ts, nsecs);
311} 312}
312EXPORT_SYMBOL(getnstimeofday); 313EXPORT_SYMBOL(getnstimeofday);
@@ -345,6 +346,7 @@ void ktime_get_ts(struct timespec *ts)
345{ 346{
346 struct timekeeper *tk = &timekeeper; 347 struct timekeeper *tk = &timekeeper;
347 struct timespec tomono; 348 struct timespec tomono;
349 s64 nsec;
348 unsigned int seq; 350 unsigned int seq;
349 351
350 WARN_ON(timekeeping_suspended); 352 WARN_ON(timekeeping_suspended);
@@ -352,13 +354,14 @@ void ktime_get_ts(struct timespec *ts)
352 do { 354 do {
353 seq = read_seqbegin(&tk->lock); 355 seq = read_seqbegin(&tk->lock);
354 ts->tv_sec = tk->xtime_sec; 356 ts->tv_sec = tk->xtime_sec;
355 ts->tv_nsec = timekeeping_get_ns(tk); 357 nsec = timekeeping_get_ns(tk);
356 tomono = tk->wall_to_monotonic; 358 tomono = tk->wall_to_monotonic;
357 359
358 } while (read_seqretry(&tk->lock, seq)); 360 } while (read_seqretry(&tk->lock, seq));
359 361
360 set_normalized_timespec(ts, ts->tv_sec + tomono.tv_sec, 362 ts->tv_sec += tomono.tv_sec;
361 ts->tv_nsec + tomono.tv_nsec); 363 ts->tv_nsec = 0;
364 timespec_add_ns(ts, nsec + tomono.tv_nsec);
362} 365}
363EXPORT_SYMBOL_GPL(ktime_get_ts); 366EXPORT_SYMBOL_GPL(ktime_get_ts);
364 367
@@ -1244,6 +1247,7 @@ void get_monotonic_boottime(struct timespec *ts)
1244{ 1247{
1245 struct timekeeper *tk = &timekeeper; 1248 struct timekeeper *tk = &timekeeper;
1246 struct timespec tomono, sleep; 1249 struct timespec tomono, sleep;
1250 s64 nsec;
1247 unsigned int seq; 1251 unsigned int seq;
1248 1252
1249 WARN_ON(timekeeping_suspended); 1253 WARN_ON(timekeeping_suspended);
@@ -1251,14 +1255,15 @@ void get_monotonic_boottime(struct timespec *ts)
1251 do { 1255 do {
1252 seq = read_seqbegin(&tk->lock); 1256 seq = read_seqbegin(&tk->lock);
1253 ts->tv_sec = tk->xtime_sec; 1257 ts->tv_sec = tk->xtime_sec;
1254 ts->tv_nsec = timekeeping_get_ns(tk); 1258 nsec = timekeeping_get_ns(tk);
1255 tomono = tk->wall_to_monotonic; 1259 tomono = tk->wall_to_monotonic;
1256 sleep = tk->total_sleep_time; 1260 sleep = tk->total_sleep_time;
1257 1261
1258 } while (read_seqretry(&tk->lock, seq)); 1262 } while (read_seqretry(&tk->lock, seq));
1259 1263
1260 set_normalized_timespec(ts, ts->tv_sec + tomono.tv_sec + sleep.tv_sec, 1264 ts->tv_sec += tomono.tv_sec + sleep.tv_sec;
1261 ts->tv_nsec + tomono.tv_nsec + sleep.tv_nsec); 1265 ts->tv_nsec = 0;
1266 timespec_add_ns(ts, nsec + tomono.tv_nsec + sleep.tv_nsec);
1262} 1267}
1263EXPORT_SYMBOL_GPL(get_monotonic_boottime); 1268EXPORT_SYMBOL_GPL(get_monotonic_boottime);
1264 1269