Move nanosleep syscall to a new file that is compiled unconditionally. This helps share common nanosleep code between posix-timers and posix-stubs. The latter part is addressed in subsequent patches in the series.
In this series, this also servers as a preparatory patch to eliminate the use of set_fs()/get_fs() in the compat syscall path.
Note that the clock_nanosleep compat syscalls have to be moved twice, once into each file: posix_timers.c and posix-stubs.c.
Signed-off-by: Deepa Dinamani deepa.kernel@gmail.com --- kernel/compat.c | 131 --------------------------------------------- kernel/time/hrtimer.c | 17 +----- kernel/time/nanosleep.c | 94 ++++++++++++++++++++++++++++++++ kernel/time/posix-stubs.c | 56 +++++++++++++++++++ kernel/time/posix-timers.c | 58 ++++++++++++++++++++ 5 files changed, 209 insertions(+), 147 deletions(-) create mode 100644 kernel/time/nanosleep.c
diff --git a/kernel/compat.c b/kernel/compat.c index c7a01a01222d..89d10cf47e9c 100644 --- a/kernel/compat.c +++ b/kernel/compat.c @@ -257,82 +257,6 @@ int compat_convert_timespec(struct timespec __user **kts, return 0; }
-static long compat_nanosleep_restart(struct restart_block *restart) -{ - struct compat_timespec __user *rmtp; - struct timespec rmt; - mm_segment_t oldfs; - long ret; - - restart->nanosleep.rmtp = (struct timespec __user *) &rmt; - oldfs = get_fs(); - set_fs(KERNEL_DS); - ret = hrtimer_nanosleep_restart(restart); - set_fs(oldfs); - - if (ret == -ERESTART_RESTARTBLOCK) { - rmtp = restart->nanosleep.compat_rmtp; - - if (rmtp && compat_put_timespec(&rmt, rmtp)) - return -EFAULT; - } - - return ret; -} - -COMPAT_SYSCALL_DEFINE2(nanosleep, struct compat_timespec __user *, rqtp, - struct compat_timespec __user *, rmtp) -{ - struct timespec tu, rmt; - struct timespec64 tu64; - mm_segment_t oldfs; - long ret; - - if (compat_get_timespec(&tu, rqtp)) - return -EFAULT; - - tu64 = timespec_to_timespec64(tu); - if (!timespec64_valid(&tu64)) - return -EINVAL; - - oldfs = get_fs(); - set_fs(KERNEL_DS); - ret = hrtimer_nanosleep(&tu64, - rmtp ? (struct timespec __user *)&rmt : NULL, - HRTIMER_MODE_REL, CLOCK_MONOTONIC); - set_fs(oldfs); - - /* - * hrtimer_nanosleep() can only return 0 or - * -ERESTART_RESTARTBLOCK here because: - * - * - we call it with HRTIMER_MODE_REL and therefor exclude the - * -ERESTARTNOHAND return path. - * - * - we supply the rmtp argument from the task stack (due to - * the necessary compat conversion. So the update cannot - * fail, which excludes the -EFAULT return path as well. If - * it fails nevertheless we have a bigger problem and wont - * reach this place anymore. - * - * - if the return value is 0, we do not have to update rmtp - * because there is no remaining time. - * - * We check for -ERESTART_RESTARTBLOCK nevertheless if the - * core implementation decides to return random nonsense. - */ - if (ret == -ERESTART_RESTARTBLOCK) { - struct restart_block *restart = ¤t->restart_block; - - restart->fn = compat_nanosleep_restart; - restart->nanosleep.compat_rmtp = rmtp; - - if (rmtp && compat_put_timespec(&rmt, rmtp)) - return -EFAULT; - } - return ret; -} - static inline long get_compat_itimerval(struct itimerval *o, struct compat_itimerval __user *i) { @@ -865,61 +789,6 @@ COMPAT_SYSCALL_DEFINE2(clock_getres, clockid_t, which_clock, return err; }
-static long compat_clock_nanosleep_restart(struct restart_block *restart) -{ - long err; - mm_segment_t oldfs; - struct timespec tu; - struct compat_timespec __user *rmtp = restart->nanosleep.compat_rmtp; - - restart->nanosleep.rmtp = (struct timespec __user *) &tu; - oldfs = get_fs(); - set_fs(KERNEL_DS); - err = clock_nanosleep_restart(restart); - set_fs(oldfs); - - if ((err == -ERESTART_RESTARTBLOCK) && rmtp && - compat_put_timespec(&tu, rmtp)) - return -EFAULT; - - if (err == -ERESTART_RESTARTBLOCK) { - restart->fn = compat_clock_nanosleep_restart; - restart->nanosleep.compat_rmtp = rmtp; - } - return err; -} - -COMPAT_SYSCALL_DEFINE4(clock_nanosleep, clockid_t, which_clock, int, flags, - struct compat_timespec __user *, rqtp, - struct compat_timespec __user *, rmtp) -{ - long err; - mm_segment_t oldfs; - struct timespec in, out; - struct restart_block *restart; - - if (compat_get_timespec(&in, rqtp)) - return -EFAULT; - - oldfs = get_fs(); - set_fs(KERNEL_DS); - err = sys_clock_nanosleep(which_clock, flags, - (struct timespec __user *) &in, - (struct timespec __user *) &out); - set_fs(oldfs); - - if ((err == -ERESTART_RESTARTBLOCK) && rmtp && - compat_put_timespec(&out, rmtp)) - return -EFAULT; - - if (err == -ERESTART_RESTARTBLOCK) { - restart = ¤t->restart_block; - restart->fn = compat_clock_nanosleep_restart; - restart->nanosleep.compat_rmtp = rmtp; - } - return err; -} - /* * We currently only need the following fields from the sigevent * structure: sigev_value, sigev_signo, sig_notify and (sometimes diff --git a/kernel/time/hrtimer.c b/kernel/time/hrtimer.c index ac053bb5296e..e95628910b00 100644 --- a/kernel/time/hrtimer.c +++ b/kernel/time/hrtimer.c @@ -37,6 +37,7 @@ #include <linux/hrtimer.h> #include <linux/notifier.h> #include <linux/syscalls.h> +#include <linux/compat.h> #include <linux/kallsyms.h> #include <linux/interrupt.h> #include <linux/tick.h> @@ -1544,22 +1545,6 @@ long hrtimer_nanosleep(struct timespec64 *rqtp, struct timespec __user *rmtp, return ret; }
-SYSCALL_DEFINE2(nanosleep, struct timespec __user *, rqtp, - struct timespec __user *, rmtp) -{ - struct timespec64 tu64; - struct timespec tu; - - if (copy_from_user(&tu, rqtp, sizeof(tu))) - return -EFAULT; - - tu64 = timespec_to_timespec64(tu); - if (!timespec64_valid(&tu64)) - return -EINVAL; - - return hrtimer_nanosleep(&tu64, rmtp, HRTIMER_MODE_REL, CLOCK_MONOTONIC); -} - /* * Functions related to boot-time initialization: */ diff --git a/kernel/time/nanosleep.c b/kernel/time/nanosleep.c new file mode 100644 index 000000000000..2b6e6980b65d --- /dev/null +++ b/kernel/time/nanosleep.c @@ -0,0 +1,94 @@ +SYSCALL_DEFINE2(nanosleep, struct timespec __user *, rqtp, + struct timespec __user *, rmtp) +{ + struct timespec64 tu64; + struct timespec tu; + + if (copy_from_user(&tu, rqtp, sizeof(tu))) + return -EFAULT; + + tu64 = timespec_to_timespec64(tu); + if (!timespec64_valid(&tu64)) + return -EINVAL; + + return hrtimer_nanosleep(&tu64, rmtp, HRTIMER_MODE_REL, CLOCK_MONOTONIC); +} + +#ifdef CONFIG_COMPAT +static long compat_nanosleep_restart(struct restart_block *restart) +{ + struct compat_timespec __user *rmtp; + struct timespec rmt; + mm_segment_t oldfs; + long ret; + + restart->nanosleep.rmtp = (struct timespec __user *) &rmt; + oldfs = get_fs(); + set_fs(KERNEL_DS); + ret = hrtimer_nanosleep_restart(restart); + set_fs(oldfs); + + if (ret == -ERESTART_RESTARTBLOCK) { + rmtp = restart->nanosleep.compat_rmtp; + + if (rmtp && compat_put_timespec(&rmt, rmtp)) + return -EFAULT; + } + + return ret; +} + +COMPAT_SYSCALL_DEFINE2(nanosleep, struct compat_timespec __user *, rqtp, + struct compat_timespec __user *, rmtp) +{ + struct timespec tu, rmt; + struct timespec64 tu64; + mm_segment_t oldfs; + long ret; + + if (compat_get_timespec(&tu, rqtp)) + return -EFAULT; + + tu64 = timespec_to_timespec64(tu); + if (!timespec64_valid(&tu64)) + return -EINVAL; + + oldfs = get_fs(); + set_fs(KERNEL_DS); + ret = hrtimer_nanosleep(&tu64, + rmtp ? (struct timespec __user *)&rmt : NULL, + HRTIMER_MODE_REL, CLOCK_MONOTONIC); + set_fs(oldfs); + + /* + * hrtimer_nanosleep() can only return 0 or + * -ERESTART_RESTARTBLOCK here because: + * + * - we call it with HRTIMER_MODE_REL and therefor exclude the + * -ERESTARTNOHAND return path. + * + * - we supply the rmtp argument from the task stack (due to + * the necessary compat conversion. So the update cannot + * fail, which excludes the -EFAULT return path as well. If + * it fails nevertheless we have a bigger problem and wont + * reach this place anymore. + * + * - if the return value is 0, we do not have to update rmtp + * because there is no remaining time. + * + * We check for -ERESTART_RESTARTBLOCK nevertheless if the + * core implementation decides to return random nonsense. + */ + if (ret == -ERESTART_RESTARTBLOCK) { + struct restart_block *restart = ¤t->restart_block; + + restart->fn = compat_nanosleep_restart; + restart->nanosleep.compat_rmtp = rmtp; + + if (rmtp && compat_put_timespec(&rmt, rmtp)) + return -EFAULT; + } + return ret; +} +#endif + diff --git a/kernel/time/posix-stubs.c b/kernel/time/posix-stubs.c index c0cd53eb018a..3031a28921ba 100644 --- a/kernel/time/posix-stubs.c +++ b/kernel/time/posix-stubs.c @@ -14,6 +14,7 @@ #include <linux/sched.h> #include <linux/errno.h> #include <linux/syscalls.h> +#include <linux/compat.h> #include <linux/ktime.h> #include <linux/timekeeping.h> #include <linux/posix-timers.h> @@ -128,4 +129,59 @@ long clock_nanosleep_restart(struct restart_block *restart_block) { return hrtimer_nanosleep_restart(restart_block); } + +static long compat_clock_nanosleep_restart(struct restart_block *restart) +{ + long err; + mm_segment_t oldfs; + struct timespec tu; + struct compat_timespec __user *rmtp = restart->nanosleep.compat_rmtp; + + restart->nanosleep.rmtp = (struct timespec __user *) &tu; + oldfs = get_fs(); + set_fs(KERNEL_DS); + err = clock_nanosleep_restart(restart); + set_fs(oldfs); + + if ((err == -ERESTART_RESTARTBLOCK) && rmtp && + compat_put_timespec(&tu, rmtp)) + return -EFAULT; + + if (err == -ERESTART_RESTARTBLOCK) { + restart->fn = compat_clock_nanosleep_restart; + restart->nanosleep.compat_rmtp = rmtp; + } + return err; +} + +COMPAT_SYSCALL_DEFINE4(clock_nanosleep, clockid_t, which_clock, int, flags, + struct compat_timespec __user *, rqtp, + struct compat_timespec __user *, rmtp) +{ + long err; + mm_segment_t oldfs; + struct timespec in, out; + struct restart_block *restart; + + if (compat_get_timespec(&in, rqtp)) + return -EFAULT; + + oldfs = get_fs(); + set_fs(KERNEL_DS); + err = sys_clock_nanosleep(which_clock, flags, + (struct timespec __user *) &in, + (struct timespec __user *) &out); + set_fs(oldfs); + + if ((err == -ERESTART_RESTARTBLOCK) && rmtp && + compat_put_timespec(&out, rmtp)) + return -EFAULT; + + if (err == -ERESTART_RESTARTBLOCK) { + restart = ¤t->restart_block; + restart->fn = compat_clock_nanosleep_restart; + restart->nanosleep.compat_rmtp = rmtp; + } + return err; +} #endif diff --git a/kernel/time/posix-timers.c b/kernel/time/posix-timers.c index 6e7a70b1bf37..cf32adccd062 100644 --- a/kernel/time/posix-timers.c +++ b/kernel/time/posix-timers.c @@ -45,6 +45,7 @@ #include <linux/posix-clock.h> #include <linux/posix-timers.h> #include <linux/syscalls.h> +#include <linux/compat.h> #include <linux/wait.h> #include <linux/workqueue.h> #include <linux/export.h> @@ -1176,3 +1177,60 @@ static const struct k_clock *clockid_to_kclock(const clockid_t id) return NULL; return posix_clocks[id]; } + +#ifdef CONFIG_COMPAT +static long compat_clock_nanosleep_restart(struct restart_block *restart) +{ + long err; + mm_segment_t oldfs; + struct timespec tu; + struct compat_timespec __user *rmtp = restart->nanosleep.compat_rmtp; + + restart->nanosleep.rmtp = (struct timespec __user *) &tu; + oldfs = get_fs(); + set_fs(KERNEL_DS); + err = clock_nanosleep_restart(restart); + set_fs(oldfs); + + if ((err == -ERESTART_RESTARTBLOCK) && rmtp && + compat_put_timespec(&tu, rmtp)) + return -EFAULT; + + if (err == -ERESTART_RESTARTBLOCK) { + restart->fn = compat_clock_nanosleep_restart; + restart->nanosleep.compat_rmtp = rmtp; + } + return err; +} + +COMPAT_SYSCALL_DEFINE4(clock_nanosleep, clockid_t, which_clock, int, flags, + struct compat_timespec __user *, rqtp, + struct compat_timespec __user *, rmtp) +{ + long err; + mm_segment_t oldfs; + struct timespec in, out; + struct restart_block *restart; + + if (compat_get_timespec(&in, rqtp)) + return -EFAULT; + + oldfs = get_fs(); + set_fs(KERNEL_DS); + err = sys_clock_nanosleep(which_clock, flags, + (struct timespec __user *) &in, + (struct timespec __user *) &out); + set_fs(oldfs); + + if ((err == -ERESTART_RESTARTBLOCK) && rmtp && + compat_put_timespec(&out, rmtp)) + return -EFAULT; + + if (err == -ERESTART_RESTARTBLOCK) { + restart = ¤t->restart_block; + restart->fn = compat_clock_nanosleep_restart; + restart->nanosleep.compat_rmtp = rmtp; + } + return err; +} +#endif