posix-timers: Introduce a syscall for clock tuning.
authorRichard Cochran <richardcochran@gmail.com>
Tue, 1 Feb 2011 13:52:26 +0000 (13:52 +0000)
committerThomas Gleixner <tglx@linutronix.de>
Wed, 2 Feb 2011 14:28:19 +0000 (15:28 +0100)
A new syscall is introduced that allows tuning of a POSIX clock. The
new call, clock_adjtime, takes two parameters, the clock ID and a
pointer to a struct timex. Any ADJTIMEX(2) operation may be requested
via this system call, but various POSIX clocks may or may not support
tuning.

[ tglx: Adapted to the posix-timer cleanup series. Avoid copy_to_user
   in the error case ]

Signed-off-by: Richard Cochran <richard.cochran@omicron.at>
Acked-by: John Stultz <johnstul@us.ibm.com>
LKML-Reference: <20110201134419.869804645@linutronix.de>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
include/linux/posix-timers.h
include/linux/syscalls.h
kernel/compat.c
kernel/posix-timers.c

index 8206255a547c7ca4551a709715eabd2cf0773a1a..79a1cea7f6ed6337601e21ffce74c3187a761b37 100644 (file)
@@ -4,6 +4,7 @@
 #include <linux/spinlock.h>
 #include <linux/list.h>
 #include <linux/sched.h>
+#include <linux/timex.h>
 
 union cpu_time_count {
        cputime_t cpu;
@@ -71,6 +72,7 @@ struct k_clock {
        int (*clock_set) (const clockid_t which_clock,
                          const struct timespec *tp);
        int (*clock_get) (const clockid_t which_clock, struct timespec * tp);
+       int (*clock_adj) (const clockid_t which_clock, struct timex *tx);
        int (*timer_create) (struct k_itimer *timer);
        int (*nsleep) (const clockid_t which_clock, int flags,
                       struct timespec *, struct timespec __user *);
index 18cd0684fc4ec4bb2e6fb52ed6a7838737688c17..bfacab921239045bb9b1a57a50c7521428ccb470 100644 (file)
@@ -311,6 +311,8 @@ asmlinkage long sys_clock_settime(clockid_t which_clock,
                                const struct timespec __user *tp);
 asmlinkage long sys_clock_gettime(clockid_t which_clock,
                                struct timespec __user *tp);
+asmlinkage long sys_clock_adjtime(clockid_t which_clock,
+                               struct timex __user *tx);
 asmlinkage long sys_clock_getres(clockid_t which_clock,
                                struct timespec __user *tp);
 asmlinkage long sys_clock_nanosleep(clockid_t which_clock, int flags,
index 449e853cf41df12b2de52576682f28da4b2452b0..38b1d2c1cbe80bd88371a09412b5e52abe86afc3 100644 (file)
@@ -675,6 +675,29 @@ long compat_sys_clock_gettime(clockid_t which_clock,
        return err;
 }
 
+long compat_sys_clock_adjtime(clockid_t which_clock,
+               struct compat_timex __user *utp)
+{
+       struct timex txc;
+       mm_segment_t oldfs;
+       int err, ret;
+
+       err = compat_get_timex(&txc, utp);
+       if (err)
+               return err;
+
+       oldfs = get_fs();
+       set_fs(KERNEL_DS);
+       ret = sys_clock_adjtime(which_clock, (struct timex __user *) &txc);
+       set_fs(oldfs);
+
+       err = compat_put_timex(utp, &txc);
+       if (err)
+               return err;
+
+       return ret;
+}
+
 long compat_sys_clock_getres(clockid_t which_clock,
                struct compat_timespec __user *tp)
 {
index a3fdfd4be0ec26bf84e91ee617c2ab5e6bdcccf2..5a5a4f1c0971424e2dae9e5cfd1e7df174cc659a 100644 (file)
@@ -170,6 +170,12 @@ static int posix_clock_realtime_set(const clockid_t which_clock,
        return do_sys_settimeofday(tp, NULL);
 }
 
+static int posix_clock_realtime_adj(const clockid_t which_clock,
+                                   struct timex *t)
+{
+       return do_adjtimex(t);
+}
+
 /*
  * Get monotonic time for posix timers
  */
@@ -216,6 +222,7 @@ static __init int init_posix_timers(void)
                .clock_getres   = hrtimer_get_res,
                .clock_get      = posix_clock_realtime_get,
                .clock_set      = posix_clock_realtime_set,
+               .clock_adj      = posix_clock_realtime_adj,
                .nsleep         = common_nsleep,
                .nsleep_restart = hrtimer_nanosleep_restart,
                .timer_create   = common_timer_create,
@@ -948,6 +955,29 @@ SYSCALL_DEFINE2(clock_gettime, const clockid_t, which_clock,
        return error;
 }
 
+SYSCALL_DEFINE2(clock_adjtime, const clockid_t, which_clock,
+               struct timex __user *, utx)
+{
+       struct k_clock *kc = clockid_to_kclock(which_clock);
+       struct timex ktx;
+       int err;
+
+       if (!kc)
+               return -EINVAL;
+       if (!kc->clock_adj)
+               return -EOPNOTSUPP;
+
+       if (copy_from_user(&ktx, utx, sizeof(ktx)))
+               return -EFAULT;
+
+       err = kc->clock_adj(which_clock, &ktx);
+
+       if (!err && copy_to_user(utx, &ktx, sizeof(ktx)))
+               return -EFAULT;
+
+       return err;
+}
+
 SYSCALL_DEFINE2(clock_getres, const clockid_t, which_clock,
                struct timespec __user *, tp)
 {