Commit ec145bab authored by John Stultz's avatar John Stultz Committed by Ingo Molnar

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
1e75fa8b ("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: default avatarDaniel Lezcano <daniel.lezcano@linaro.org>
Tested-by: default avatarDaniel Lezcano <daniel.lezcano@linaro.org>
Signed-off-by: default avatarJohn Stultz <john.stultz@linaro.org>
Acked-by: default avatarPrarit 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.orgSigned-off-by: default avatarIngo Molnar <mingo@kernel.org>
parent 0bd1189e
...@@ -303,10 +303,11 @@ void getnstimeofday(struct timespec *ts) ...@@ -303,10 +303,11 @@ void getnstimeofday(struct timespec *ts)
seq = read_seqbegin(&tk->lock); seq = read_seqbegin(&tk->lock);
ts->tv_sec = tk->xtime_sec; ts->tv_sec = tk->xtime_sec;
ts->tv_nsec = timekeeping_get_ns(tk); nsecs = timekeeping_get_ns(tk);
} while (read_seqretry(&tk->lock, seq)); } while (read_seqretry(&tk->lock, seq));
ts->tv_nsec = 0;
timespec_add_ns(ts, nsecs); timespec_add_ns(ts, nsecs);
} }
EXPORT_SYMBOL(getnstimeofday); EXPORT_SYMBOL(getnstimeofday);
...@@ -345,6 +346,7 @@ void ktime_get_ts(struct timespec *ts) ...@@ -345,6 +346,7 @@ void ktime_get_ts(struct timespec *ts)
{ {
struct timekeeper *tk = &timekeeper; struct timekeeper *tk = &timekeeper;
struct timespec tomono; struct timespec tomono;
s64 nsec;
unsigned int seq; unsigned int seq;
WARN_ON(timekeeping_suspended); WARN_ON(timekeeping_suspended);
...@@ -352,13 +354,14 @@ void ktime_get_ts(struct timespec *ts) ...@@ -352,13 +354,14 @@ void ktime_get_ts(struct timespec *ts)
do { do {
seq = read_seqbegin(&tk->lock); seq = read_seqbegin(&tk->lock);
ts->tv_sec = tk->xtime_sec; ts->tv_sec = tk->xtime_sec;
ts->tv_nsec = timekeeping_get_ns(tk); nsec = timekeeping_get_ns(tk);
tomono = tk->wall_to_monotonic; tomono = tk->wall_to_monotonic;
} while (read_seqretry(&tk->lock, seq)); } while (read_seqretry(&tk->lock, seq));
set_normalized_timespec(ts, ts->tv_sec + tomono.tv_sec, ts->tv_sec += tomono.tv_sec;
ts->tv_nsec + tomono.tv_nsec); ts->tv_nsec = 0;
timespec_add_ns(ts, nsec + tomono.tv_nsec);
} }
EXPORT_SYMBOL_GPL(ktime_get_ts); EXPORT_SYMBOL_GPL(ktime_get_ts);
...@@ -1244,6 +1247,7 @@ void get_monotonic_boottime(struct timespec *ts) ...@@ -1244,6 +1247,7 @@ void get_monotonic_boottime(struct timespec *ts)
{ {
struct timekeeper *tk = &timekeeper; struct timekeeper *tk = &timekeeper;
struct timespec tomono, sleep; struct timespec tomono, sleep;
s64 nsec;
unsigned int seq; unsigned int seq;
WARN_ON(timekeeping_suspended); WARN_ON(timekeeping_suspended);
...@@ -1251,14 +1255,15 @@ void get_monotonic_boottime(struct timespec *ts) ...@@ -1251,14 +1255,15 @@ void get_monotonic_boottime(struct timespec *ts)
do { do {
seq = read_seqbegin(&tk->lock); seq = read_seqbegin(&tk->lock);
ts->tv_sec = tk->xtime_sec; ts->tv_sec = tk->xtime_sec;
ts->tv_nsec = timekeeping_get_ns(tk); nsec = timekeeping_get_ns(tk);
tomono = tk->wall_to_monotonic; tomono = tk->wall_to_monotonic;
sleep = tk->total_sleep_time; sleep = tk->total_sleep_time;
} while (read_seqretry(&tk->lock, seq)); } while (read_seqretry(&tk->lock, seq));
set_normalized_timespec(ts, ts->tv_sec + tomono.tv_sec + sleep.tv_sec, ts->tv_sec += tomono.tv_sec + sleep.tv_sec;
ts->tv_nsec + tomono.tv_nsec + sleep.tv_nsec); ts->tv_nsec = 0;
timespec_add_ns(ts, nsec + tomono.tv_nsec + sleep.tv_nsec);
} }
EXPORT_SYMBOL_GPL(get_monotonic_boottime); EXPORT_SYMBOL_GPL(get_monotonic_boottime);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment