From e13a2e61dd5152f5499d2003470acf9c838eab84 Mon Sep 17 00:00:00 2001 From: john stultz Date: Sun, 10 Feb 2008 10:48:03 +0100 Subject: ntp: correct inconsistent interval/tick_length usage clocksource initialization and error accumulation. This corrects a 280ppm drift seen on some systems using acpi_pm, and affects other clocksources as well (likely to a lesser degree). Signed-off-by: John Stultz Cc: Roman Zippel Cc: Steven Rostedt Signed-off-by: Andrew Morton Signed-off-by: Ingo Molnar Signed-off-by: Thomas Gleixner --- include/linux/timex.h | 9 ++++++++- kernel/time/ntp.c | 4 ---- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/include/linux/timex.h b/include/linux/timex.h index 8ea3e71ba7fa..c3f374786a43 100644 --- a/include/linux/timex.h +++ b/include/linux/timex.h @@ -232,7 +232,14 @@ static inline int ntp_synced(void) #else #define NTP_INTERVAL_FREQ (HZ) #endif -#define NTP_INTERVAL_LENGTH (NSEC_PER_SEC/NTP_INTERVAL_FREQ) + +#define CLOCK_TICK_OVERFLOW (LATCH * HZ - CLOCK_TICK_RATE) +#define CLOCK_TICK_ADJUST (((s64)CLOCK_TICK_OVERFLOW * NSEC_PER_SEC) / \ + (s64)CLOCK_TICK_RATE) + +/* Because using NSEC_PER_SEC would be too easy */ +#define NTP_INTERVAL_LENGTH ((((s64)TICK_USEC * NSEC_PER_USEC * USER_HZ) + \ + CLOCK_TICK_ADJUST) / NTP_INTERVAL_FREQ) /* Returns how long ticks are at present, in ns / 2^(SHIFT_SCALE-10). */ extern u64 current_tick_length(void); diff --git a/kernel/time/ntp.c b/kernel/time/ntp.c index e64efaf957e8..c88b5910e7ab 100644 --- a/kernel/time/ntp.c +++ b/kernel/time/ntp.c @@ -43,10 +43,6 @@ long time_freq; /* frequency offset (scaled ppm)*/ static long time_reftime; /* time at last adjustment (s) */ long time_adjust; -#define CLOCK_TICK_OVERFLOW (LATCH * HZ - CLOCK_TICK_RATE) -#define CLOCK_TICK_ADJUST (((s64)CLOCK_TICK_OVERFLOW * NSEC_PER_SEC) / \ - (s64)CLOCK_TICK_RATE) - static void ntp_update_frequency(void) { u64 second_length = (u64)(tick_usec * NSEC_PER_USEC * USER_HZ) -- cgit v1.2.3 From 080344b98805553f9b01de0f59a41b1533036d8d Mon Sep 17 00:00:00 2001 From: Oleg Nesterov Date: Fri, 1 Feb 2008 17:29:05 +0300 Subject: hrtimer: fix *rmtp handling in hrtimer_nanosleep() Spotted by Pavel Emelyanov and Alexey Dobriyan. hrtimer_nanosleep() sets restart_block->arg1 = rmtp, but this rmtp points to the local variable which lives in the caller's stack frame. This means that if sys_restart_syscall() actually happens and it is interrupted as well, we don't update the user-space variable, but write into the already dead stack frame. Introduced by commit 04c227140fed77587432667a574b14736a06dd7f hrtimer: Rework hrtimer_nanosleep to make sys_compat_nanosleep easier Change the callers to pass "__user *rmtp" to hrtimer_nanosleep(), and change hrtimer_nanosleep() to use copy_to_user() to actually update *rmtp. Small problem remains. man 2 nanosleep states that *rtmp should be written if nanosleep() was interrupted (it says nothing whether it is OK to update *rmtp if nanosleep returns 0), but (with or without this patch) we can dirty *rem even if nanosleep() returns 0. NOTE: this patch doesn't change compat_sys_nanosleep(), because it has other bugs. Fixed by the next patch. Signed-off-by: Oleg Nesterov Cc: Alexey Dobriyan Cc: Michael Kerrisk Cc: Pavel Emelyanov Cc: Peter Zijlstra Cc: Toyo Abe Cc: Andrew Morton Signed-off-by: Thomas Gleixner include/linux/hrtimer.h | 2 - kernel/hrtimer.c | 51 +++++++++++++++++++++++++----------------------- kernel/posix-timers.c | 14 +------------ 3 files changed, 30 insertions(+), 37 deletions(-) --- include/linux/hrtimer.h | 2 +- kernel/hrtimer.c | 51 ++++++++++++++++++++++++++----------------------- kernel/posix-timers.c | 17 +++-------------- 3 files changed, 31 insertions(+), 39 deletions(-) diff --git a/include/linux/hrtimer.h b/include/linux/hrtimer.h index 600fc3bcf63e..1ad56a7b2f74 100644 --- a/include/linux/hrtimer.h +++ b/include/linux/hrtimer.h @@ -316,7 +316,7 @@ static inline u64 hrtimer_forward_now(struct hrtimer *timer, /* Precise sleep: */ extern long hrtimer_nanosleep(struct timespec *rqtp, - struct timespec *rmtp, + struct timespec __user *rmtp, const enum hrtimer_mode mode, const clockid_t clockid); extern long hrtimer_nanosleep_restart(struct restart_block *restart_block); diff --git a/kernel/hrtimer.c b/kernel/hrtimer.c index 668f3967eb39..355085f0896e 100644 --- a/kernel/hrtimer.c +++ b/kernel/hrtimer.c @@ -1319,11 +1319,26 @@ static int __sched do_nanosleep(struct hrtimer_sleeper *t, enum hrtimer_mode mod return t->task == NULL; } +static int update_rmtp(struct hrtimer *timer, struct timespec __user *rmtp) +{ + struct timespec rmt; + ktime_t rem; + + rem = ktime_sub(timer->expires, timer->base->get_time()); + if (rem.tv64 <= 0) + return 0; + rmt = ktime_to_timespec(rem); + + if (copy_to_user(rmtp, &rmt, sizeof(*rmtp))) + return -EFAULT; + + return 1; +} + long __sched hrtimer_nanosleep_restart(struct restart_block *restart) { struct hrtimer_sleeper t; - struct timespec *rmtp; - ktime_t time; + struct timespec __user *rmtp; restart->fn = do_no_restart_syscall; @@ -1333,12 +1348,11 @@ long __sched hrtimer_nanosleep_restart(struct restart_block *restart) if (do_nanosleep(&t, HRTIMER_MODE_ABS)) return 0; - rmtp = (struct timespec *)restart->arg1; + rmtp = (struct timespec __user *)restart->arg1; if (rmtp) { - time = ktime_sub(t.timer.expires, t.timer.base->get_time()); - if (time.tv64 <= 0) - return 0; - *rmtp = ktime_to_timespec(time); + int ret = update_rmtp(&t.timer, rmtp); + if (ret <= 0) + return ret; } restart->fn = hrtimer_nanosleep_restart; @@ -1347,12 +1361,11 @@ long __sched hrtimer_nanosleep_restart(struct restart_block *restart) return -ERESTART_RESTARTBLOCK; } -long hrtimer_nanosleep(struct timespec *rqtp, struct timespec *rmtp, +long hrtimer_nanosleep(struct timespec *rqtp, struct timespec __user *rmtp, const enum hrtimer_mode mode, const clockid_t clockid) { struct restart_block *restart; struct hrtimer_sleeper t; - ktime_t rem; hrtimer_init(&t.timer, clockid, mode); t.timer.expires = timespec_to_ktime(*rqtp); @@ -1364,10 +1377,9 @@ long hrtimer_nanosleep(struct timespec *rqtp, struct timespec *rmtp, return -ERESTARTNOHAND; if (rmtp) { - rem = ktime_sub(t.timer.expires, t.timer.base->get_time()); - if (rem.tv64 <= 0) - return 0; - *rmtp = ktime_to_timespec(rem); + int ret = update_rmtp(&t.timer, rmtp); + if (ret <= 0) + return ret; } restart = ¤t_thread_info()->restart_block; @@ -1383,8 +1395,7 @@ long hrtimer_nanosleep(struct timespec *rqtp, struct timespec *rmtp, asmlinkage long sys_nanosleep(struct timespec __user *rqtp, struct timespec __user *rmtp) { - struct timespec tu, rmt; - int ret; + struct timespec tu; if (copy_from_user(&tu, rqtp, sizeof(tu))) return -EFAULT; @@ -1392,15 +1403,7 @@ sys_nanosleep(struct timespec __user *rqtp, struct timespec __user *rmtp) if (!timespec_valid(&tu)) return -EINVAL; - ret = hrtimer_nanosleep(&tu, rmtp ? &rmt : NULL, HRTIMER_MODE_REL, - CLOCK_MONOTONIC); - - if (ret && rmtp) { - if (copy_to_user(rmtp, &rmt, sizeof(*rmtp))) - return -EFAULT; - } - - return ret; + return hrtimer_nanosleep(&tu, rmtp, HRTIMER_MODE_REL, CLOCK_MONOTONIC); } /* diff --git a/kernel/posix-timers.c b/kernel/posix-timers.c index ce268966007d..022c9c3cee6f 100644 --- a/kernel/posix-timers.c +++ b/kernel/posix-timers.c @@ -982,20 +982,9 @@ sys_clock_getres(const clockid_t which_clock, struct timespec __user *tp) static int common_nsleep(const clockid_t which_clock, int flags, struct timespec *tsave, struct timespec __user *rmtp) { - struct timespec rmt; - int ret; - - ret = hrtimer_nanosleep(tsave, rmtp ? &rmt : NULL, - flags & TIMER_ABSTIME ? - HRTIMER_MODE_ABS : HRTIMER_MODE_REL, - which_clock); - - if (ret && rmtp) { - if (copy_to_user(rmtp, &rmt, sizeof(*rmtp))) - return -EFAULT; - } - - return ret; + return hrtimer_nanosleep(tsave, rmtp, flags & TIMER_ABSTIME ? + HRTIMER_MODE_ABS : HRTIMER_MODE_REL, + which_clock); } asmlinkage long -- cgit v1.2.3 From 416529374b4793ba2d2e97e736d108a2e0f3ef07 Mon Sep 17 00:00:00 2001 From: Oleg Nesterov Date: Fri, 1 Feb 2008 20:35:31 +0300 Subject: hrtimer: fix *rmtp/restarts handling in compat_sys_nanosleep() Spotted by Pavel Emelyanov and Alexey Dobriyan. compat_sys_nanosleep() implicitly uses hrtimer_nanosleep_restart(), this can't work. Make a suitable compat_nanosleep_restart() helper. Introduced by commit c70878b4e0b6cf8d2f1e46319e48e821ef4a8aba hrtimer: hook compat_sys_nanosleep up to high res timer code Also, set ->addr_limit = KERNEL_DS before doing hrtimer_nanosleep(), this func was changed by the previous patch and now takes the "__user *" parameter. Thanks to Ingo Molnar for fixing the bug in this patch. Signed-off-by: Oleg Nesterov Cc: Andrew Morton Cc: Alexey Dobriyan Cc: Pavel Emelyanov Cc: Peter Zijlstra Cc: Toyo Abe Signed-off-by: Thomas Gleixner --- kernel/compat.c | 44 ++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 40 insertions(+), 4 deletions(-) diff --git a/kernel/compat.c b/kernel/compat.c index 42a1ed4b61b1..f2a297504287 100644 --- a/kernel/compat.c +++ b/kernel/compat.c @@ -40,10 +40,36 @@ int put_compat_timespec(const struct timespec *ts, struct compat_timespec __user __put_user(ts->tv_nsec, &cts->tv_nsec)) ? -EFAULT : 0; } +static long compat_nanosleep_restart(struct restart_block *restart) +{ + struct compat_timespec __user *rmtp; + struct timespec rmt; + mm_segment_t oldfs; + long ret; + + rmtp = (struct compat_timespec __user *)(restart->arg1); + restart->arg1 = (unsigned long)&rmt; + oldfs = get_fs(); + set_fs(KERNEL_DS); + ret = hrtimer_nanosleep_restart(restart); + set_fs(oldfs); + + if (ret) { + restart->fn = compat_nanosleep_restart; + restart->arg1 = (unsigned long)rmtp; + + if (rmtp && put_compat_timespec(&rmt, rmtp)) + return -EFAULT; + } + + return ret; +} + asmlinkage long compat_sys_nanosleep(struct compat_timespec __user *rqtp, struct compat_timespec __user *rmtp) { struct timespec tu, rmt; + mm_segment_t oldfs; long ret; if (get_compat_timespec(&tu, rqtp)) @@ -52,11 +78,21 @@ asmlinkage long compat_sys_nanosleep(struct compat_timespec __user *rqtp, if (!timespec_valid(&tu)) return -EINVAL; - ret = hrtimer_nanosleep(&tu, rmtp ? &rmt : NULL, HRTIMER_MODE_REL, - CLOCK_MONOTONIC); + oldfs = get_fs(); + set_fs(KERNEL_DS); + ret = hrtimer_nanosleep(&tu, + rmtp ? (struct timespec __user *)&rmt : NULL, + HRTIMER_MODE_REL, CLOCK_MONOTONIC); + set_fs(oldfs); + + if (ret) { + struct restart_block *restart + = ¤t_thread_info()->restart_block; + + restart->fn = compat_nanosleep_restart; + restart->arg1 = (unsigned long)rmtp; - if (ret && rmtp) { - if (put_compat_timespec(&rmt, rmtp)) + if (rmtp && put_compat_timespec(&rmt, rmtp)) return -EFAULT; } -- cgit v1.2.3 From c289b074b66e2e59c65aba73f40b99e797e92d2f Mon Sep 17 00:00:00 2001 From: Oleg Nesterov Date: Fri, 1 Feb 2008 20:41:30 +0300 Subject: hrtimer: don't modify restart_block->fn in restart functions hrtimer_nanosleep_restart() clears/restores restart_block->fn. This is pointless and complicates its usage. Note that if sys_restart_syscall() doesn't actually happen, we have a bogus "pending" restart->fn anyway, this is harmless. Signed-off-by: Oleg Nesterov Cc: Alexey Dobriyan Cc: Pavel Emelyanov Cc: Peter Zijlstra Cc: Toyo Abe Cc: Andrew Morton Signed-off-by: Thomas Gleixner --- kernel/compat.c | 1 - kernel/hrtimer.c | 4 ---- 2 files changed, 5 deletions(-) diff --git a/kernel/compat.c b/kernel/compat.c index f2a297504287..5f0e201bcfd3 100644 --- a/kernel/compat.c +++ b/kernel/compat.c @@ -55,7 +55,6 @@ static long compat_nanosleep_restart(struct restart_block *restart) set_fs(oldfs); if (ret) { - restart->fn = compat_nanosleep_restart; restart->arg1 = (unsigned long)rmtp; if (rmtp && put_compat_timespec(&rmt, rmtp)) diff --git a/kernel/hrtimer.c b/kernel/hrtimer.c index 355085f0896e..3f4a57c7895d 100644 --- a/kernel/hrtimer.c +++ b/kernel/hrtimer.c @@ -1340,8 +1340,6 @@ long __sched hrtimer_nanosleep_restart(struct restart_block *restart) struct hrtimer_sleeper t; struct timespec __user *rmtp; - restart->fn = do_no_restart_syscall; - hrtimer_init(&t.timer, restart->arg0, HRTIMER_MODE_ABS); t.timer.expires.tv64 = ((u64)restart->arg3 << 32) | (u64) restart->arg2; @@ -1355,8 +1353,6 @@ long __sched hrtimer_nanosleep_restart(struct restart_block *restart) return ret; } - restart->fn = hrtimer_nanosleep_restart; - /* The other values in restart are already filled in */ return -ERESTART_RESTARTBLOCK; } -- cgit v1.2.3