diff options
author | Richard Cochran <richardcochran@gmail.com> | 2011-02-01 08:52:26 -0500 |
---|---|---|
committer | Thomas Gleixner <tglx@linutronix.de> | 2011-02-02 09:28:19 -0500 |
commit | f1f1d5ebd10ffa4242bce7a90a56a222d6b7bc77 (patch) | |
tree | ca04ea979512e0037c52bca855dbf050b1b08360 | |
parent | 65f5d80bdf83ec0d7f3887db10153bf3f36ed73c (diff) |
posix-timers: Introduce a syscall for clock tuning.
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>
-rw-r--r-- | include/linux/posix-timers.h | 2 | ||||
-rw-r--r-- | include/linux/syscalls.h | 2 | ||||
-rw-r--r-- | kernel/compat.c | 23 | ||||
-rw-r--r-- | kernel/posix-timers.c | 30 |
4 files changed, 57 insertions, 0 deletions
diff --git a/include/linux/posix-timers.h b/include/linux/posix-timers.h index 8206255a547c..79a1cea7f6ed 100644 --- a/include/linux/posix-timers.h +++ b/include/linux/posix-timers.h | |||
@@ -4,6 +4,7 @@ | |||
4 | #include <linux/spinlock.h> | 4 | #include <linux/spinlock.h> |
5 | #include <linux/list.h> | 5 | #include <linux/list.h> |
6 | #include <linux/sched.h> | 6 | #include <linux/sched.h> |
7 | #include <linux/timex.h> | ||
7 | 8 | ||
8 | union cpu_time_count { | 9 | union cpu_time_count { |
9 | cputime_t cpu; | 10 | cputime_t cpu; |
@@ -71,6 +72,7 @@ struct k_clock { | |||
71 | int (*clock_set) (const clockid_t which_clock, | 72 | int (*clock_set) (const clockid_t which_clock, |
72 | const struct timespec *tp); | 73 | const struct timespec *tp); |
73 | int (*clock_get) (const clockid_t which_clock, struct timespec * tp); | 74 | int (*clock_get) (const clockid_t which_clock, struct timespec * tp); |
75 | int (*clock_adj) (const clockid_t which_clock, struct timex *tx); | ||
74 | int (*timer_create) (struct k_itimer *timer); | 76 | int (*timer_create) (struct k_itimer *timer); |
75 | int (*nsleep) (const clockid_t which_clock, int flags, | 77 | int (*nsleep) (const clockid_t which_clock, int flags, |
76 | struct timespec *, struct timespec __user *); | 78 | struct timespec *, struct timespec __user *); |
diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h index 18cd0684fc4e..bfacab921239 100644 --- a/include/linux/syscalls.h +++ b/include/linux/syscalls.h | |||
@@ -311,6 +311,8 @@ asmlinkage long sys_clock_settime(clockid_t which_clock, | |||
311 | const struct timespec __user *tp); | 311 | const struct timespec __user *tp); |
312 | asmlinkage long sys_clock_gettime(clockid_t which_clock, | 312 | asmlinkage long sys_clock_gettime(clockid_t which_clock, |
313 | struct timespec __user *tp); | 313 | struct timespec __user *tp); |
314 | asmlinkage long sys_clock_adjtime(clockid_t which_clock, | ||
315 | struct timex __user *tx); | ||
314 | asmlinkage long sys_clock_getres(clockid_t which_clock, | 316 | asmlinkage long sys_clock_getres(clockid_t which_clock, |
315 | struct timespec __user *tp); | 317 | struct timespec __user *tp); |
316 | asmlinkage long sys_clock_nanosleep(clockid_t which_clock, int flags, | 318 | asmlinkage long sys_clock_nanosleep(clockid_t which_clock, int flags, |
diff --git a/kernel/compat.c b/kernel/compat.c index 449e853cf41d..38b1d2c1cbe8 100644 --- a/kernel/compat.c +++ b/kernel/compat.c | |||
@@ -675,6 +675,29 @@ long compat_sys_clock_gettime(clockid_t which_clock, | |||
675 | return err; | 675 | return err; |
676 | } | 676 | } |
677 | 677 | ||
678 | long compat_sys_clock_adjtime(clockid_t which_clock, | ||
679 | struct compat_timex __user *utp) | ||
680 | { | ||
681 | struct timex txc; | ||
682 | mm_segment_t oldfs; | ||
683 | int err, ret; | ||
684 | |||
685 | err = compat_get_timex(&txc, utp); | ||
686 | if (err) | ||
687 | return err; | ||
688 | |||
689 | oldfs = get_fs(); | ||
690 | set_fs(KERNEL_DS); | ||
691 | ret = sys_clock_adjtime(which_clock, (struct timex __user *) &txc); | ||
692 | set_fs(oldfs); | ||
693 | |||
694 | err = compat_put_timex(utp, &txc); | ||
695 | if (err) | ||
696 | return err; | ||
697 | |||
698 | return ret; | ||
699 | } | ||
700 | |||
678 | long compat_sys_clock_getres(clockid_t which_clock, | 701 | long compat_sys_clock_getres(clockid_t which_clock, |
679 | struct compat_timespec __user *tp) | 702 | struct compat_timespec __user *tp) |
680 | { | 703 | { |
diff --git a/kernel/posix-timers.c b/kernel/posix-timers.c index a3fdfd4be0ec..5a5a4f1c0971 100644 --- a/kernel/posix-timers.c +++ b/kernel/posix-timers.c | |||
@@ -170,6 +170,12 @@ static int posix_clock_realtime_set(const clockid_t which_clock, | |||
170 | return do_sys_settimeofday(tp, NULL); | 170 | return do_sys_settimeofday(tp, NULL); |
171 | } | 171 | } |
172 | 172 | ||
173 | static int posix_clock_realtime_adj(const clockid_t which_clock, | ||
174 | struct timex *t) | ||
175 | { | ||
176 | return do_adjtimex(t); | ||
177 | } | ||
178 | |||
173 | /* | 179 | /* |
174 | * Get monotonic time for posix timers | 180 | * Get monotonic time for posix timers |
175 | */ | 181 | */ |
@@ -216,6 +222,7 @@ static __init int init_posix_timers(void) | |||
216 | .clock_getres = hrtimer_get_res, | 222 | .clock_getres = hrtimer_get_res, |
217 | .clock_get = posix_clock_realtime_get, | 223 | .clock_get = posix_clock_realtime_get, |
218 | .clock_set = posix_clock_realtime_set, | 224 | .clock_set = posix_clock_realtime_set, |
225 | .clock_adj = posix_clock_realtime_adj, | ||
219 | .nsleep = common_nsleep, | 226 | .nsleep = common_nsleep, |
220 | .nsleep_restart = hrtimer_nanosleep_restart, | 227 | .nsleep_restart = hrtimer_nanosleep_restart, |
221 | .timer_create = common_timer_create, | 228 | .timer_create = common_timer_create, |
@@ -948,6 +955,29 @@ SYSCALL_DEFINE2(clock_gettime, const clockid_t, which_clock, | |||
948 | return error; | 955 | return error; |
949 | } | 956 | } |
950 | 957 | ||
958 | SYSCALL_DEFINE2(clock_adjtime, const clockid_t, which_clock, | ||
959 | struct timex __user *, utx) | ||
960 | { | ||
961 | struct k_clock *kc = clockid_to_kclock(which_clock); | ||
962 | struct timex ktx; | ||
963 | int err; | ||
964 | |||
965 | if (!kc) | ||
966 | return -EINVAL; | ||
967 | if (!kc->clock_adj) | ||
968 | return -EOPNOTSUPP; | ||
969 | |||
970 | if (copy_from_user(&ktx, utx, sizeof(ktx))) | ||
971 | return -EFAULT; | ||
972 | |||
973 | err = kc->clock_adj(which_clock, &ktx); | ||
974 | |||
975 | if (!err && copy_to_user(utx, &ktx, sizeof(ktx))) | ||
976 | return -EFAULT; | ||
977 | |||
978 | return err; | ||
979 | } | ||
980 | |||
951 | SYSCALL_DEFINE2(clock_getres, const clockid_t, which_clock, | 981 | SYSCALL_DEFINE2(clock_getres, const clockid_t, which_clock, |
952 | struct timespec __user *, tp) | 982 | struct timespec __user *, tp) |
953 | { | 983 | { |