Messages in this thread Patch in this message | | | Date | Fri, 22 Nov 2013 16:38:15 +0100 | From | Martin Schwidefsky <> | Subject | Clock drift with GENERIC_TIME_VSYSCALL_OLD |
| |
Greetings,
I just hunted down a time related bug which caused the Linux internal xtime to drift away from the precise hardware clock provided by the TOD clock found in the s390 architecture.
After a long search I came along this lovely piece of code in kernel/time/timekeeping.c:
#ifdef CONFIG_GENERIC_TIME_VSYSCALL_OLD static inline void old_vsyscall_fixup(struct timekeeper *tk)
s64 remainder;
/* * Store only full nanoseconds into xtime_nsec after rounding * it up and add the remainder to the error difference. * XXX - This is necessary to avoid small 1ns inconsistnecies caused * by truncating the remainder in vsyscalls. However, it causes * additional work to be done in timekeeping_adjust(). Once * the vsyscall implementations are converted to use xtime_nsec * (shifted nanoseconds), and CONFIG_GENERIC_TIME_VSYSCALL_OLD * users are removed, this can be killed. */ remainder = tk->xtime_nsec & ((1ULL << tk->shift) - 1); tk->xtime_nsec -= remainder; tk->xtime_nsec += 1ULL << tk->shift; tk->ntp_error += remainder << tk->ntp_error_shift;
} #else #define old_vsyscall_fixup(tk) #endif
The highly precise result of our TOD clock source ends up in tk->xtime_sec / tk->xtime_nsec where old_vsyscall_fixup just rounds it up to the next nano-second (booo). To add insult to injury an incorrect delta gets added to ntp_error, xtime has been forwarded by ((1ULL << tk->shift) - (tk->xtime_nsec & ((1ULL << tk->shift) - 1))) and not set back by (tk->xtime_nsec & ((1ULL << tk->shift) - 1)). xtime is too fast by one nano-second per tick. To verify that this is indeed the problem I removed the line that adds the nano-second to xtime_nsec and voila the clocks are in sync.
A possible patch to fix this would be:
--- a/kernel/time/timekeeping.c +++ b/kernel/time/timekeeping.c @@ -1347,6 +1347,7 @@ static inline void old_vsyscall_fixup(struct timekeeper *t k) tk->xtime_nsec -= remainder; tk->xtime_nsec += 1ULL << tk->shift; tk->ntp_error += remainder << tk->ntp_error_shift; + tk->ntp_error -= (1ULL << tk->shift) << tk->ntp_error_shift; } #else But that has the downside that it creates a negative ntp_error that can only be corrected with an adjustment of tk->mult which takes a long time.
The fix I am going to use is to convert s390 to GENERIC_TIME_VSYSCALL, you might want to think about doing that for powerpc and ia64 as well.
-- blue skies, Martin.
"Reality continues to ruin my life." - Calvin.
| |