From: Al Viro <[email protected]>

Turn restart_block.nanosleep.{rmtp,compat_rmtp} into a tagged union
(kind = 1 -> native, kind = 2 -> compat, kind = 0 -> nothing) and
make the places doing actual copyout handle compat as well as
native (that will become a helper in the next commit).  Result:
compat wrappers, messing with reassignments, etc. are gone.

Signed-off-by: Al Viro <[email protected]>
---
 include/linux/posix-timers.h   |   2 -
 include/linux/restart_block.h  |   7 ++-
 kernel/compat.c                | 131 -----------------------------------------
 kernel/time/alarmtimer.c       |  16 +++--
 kernel/time/hrtimer.c          |  38 ++++++++++--
 kernel/time/posix-cpu-timers.c |  18 ++++--
 kernel/time/posix-stubs.c      |  29 ++++++++-
 kernel/time/posix-timers.c     |  32 +++++++---
 8 files changed, 115 insertions(+), 158 deletions(-)

diff --git a/include/linux/posix-timers.h b/include/linux/posix-timers.h
index 667095dbcd37..29f1b7f09ced 100644
--- a/include/linux/posix-timers.h
+++ b/include/linux/posix-timers.h
@@ -110,8 +110,6 @@ void posix_cpu_timers_exit_group(struct task_struct *task);
 void set_process_cpu_timer(struct task_struct *task, unsigned int clock_idx,
                           u64 *newval, u64 *oldval);
 
-long clock_nanosleep_restart(struct restart_block *restart_block);
-
 void update_rlimit_cpu(struct task_struct *task, unsigned long rlim_new);
 
 void posixtimer_rearm(struct siginfo *info);
diff --git a/include/linux/restart_block.h b/include/linux/restart_block.h
index 0d905d8ec553..4509944bd953 100644
--- a/include/linux/restart_block.h
+++ b/include/linux/restart_block.h
@@ -29,10 +29,13 @@ struct restart_block {
                /* For nanosleep */
                struct {
                        clockid_t clockid;
-                       struct timespec __user *rmtp;
+                       int kind;
+                       union {
+                               struct timespec __user *rmtp;
 #ifdef CONFIG_COMPAT
-                       struct compat_timespec __user *compat_rmtp;
+                               struct compat_timespec __user *compat_rmtp;
 #endif
+                       };
                        u64 expires;
                } nanosleep;
                /* For poll */
diff --git a/kernel/compat.c b/kernel/compat.c
index cc9ba9d29b47..23afa26f574b 100644
--- a/kernel/compat.c
+++ b/kernel/compat.c
@@ -213,82 +213,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);
-       current->restart_block.nanosleep.rmtp =
-                               rmtp ? (struct timespec __user *)&rmt : NULL;
-       ret = hrtimer_nanosleep(&tu64, 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 = &current->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)
 {
@@ -821,61 +745,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 = &current->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/alarmtimer.c b/kernel/time/alarmtimer.c
index d859a3601ddd..868083ae7434 100644
--- a/kernel/time/alarmtimer.c
+++ b/kernel/time/alarmtimer.c
@@ -27,6 +27,7 @@
 #include <linux/posix-timers.h>
 #include <linux/workqueue.h>
 #include <linux/freezer.h>
+#include <linux/compat.h>
 
 #include "posix-timers.h"
 
@@ -691,7 +692,7 @@ static enum alarmtimer_restart 
alarmtimer_nsleep_wakeup(struct alarm *alarm,
 static int alarmtimer_do_nsleep(struct alarm *alarm, ktime_t absexp,
                                enum alarmtimer_type type)
 {
-       struct timespec __user *rmtp;
+       struct restart_block *restart;
        alarm->data = (void *)current;
        do {
                set_current_state(TASK_INTERRUPTIBLE);
@@ -709,8 +710,8 @@ static int alarmtimer_do_nsleep(struct alarm *alarm, 
ktime_t absexp,
 
        if (freezing(current))
                alarmtimer_freezerset(absexp, type);
-       rmtp = current->restart_block.nanosleep.rmtp;
-       if (rmtp) {
+       restart = &current->restart_block;
+       if (restart->nanosleep.kind) {
                struct timespec rmt;
                ktime_t rem;
 
@@ -720,7 +721,14 @@ static int alarmtimer_do_nsleep(struct alarm *alarm, 
ktime_t absexp,
                        return 0;
                rmt = ktime_to_timespec(rem);
 
-               if (copy_to_user(rmtp, &rmt, sizeof(*rmtp)))
+#ifdef CONFIG_COMPAT
+               if (restart->nanosleep.kind == 2) {
+                       if (compat_put_timespec(&rmt,
+                                               restart->nanosleep.compat_rmtp))
+                               return -EFAULT;
+               } else
+#endif
+               if (copy_to_user(restart->nanosleep.rmtp, &rmt, sizeof(rmt)))
                        return -EFAULT;
        }
        return -ERESTART_RESTARTBLOCK;
diff --git a/kernel/time/hrtimer.c b/kernel/time/hrtimer.c
index baa7b846b6e3..5534606e3985 100644
--- a/kernel/time/hrtimer.c
+++ b/kernel/time/hrtimer.c
@@ -51,6 +51,7 @@
 #include <linux/sched/debug.h>
 #include <linux/timer.h>
 #include <linux/freezer.h>
+#include <linux/compat.h>
 
 #include <linux/uaccess.h>
 
@@ -1441,7 +1442,7 @@ EXPORT_SYMBOL_GPL(hrtimer_init_sleeper);
 
 static int __sched do_nanosleep(struct hrtimer_sleeper *t, enum hrtimer_mode 
mode)
 {
-       struct timespec __user *rmtp;
+       struct restart_block *restart;
        hrtimer_init_sleeper(t, current);
 
        do {
@@ -1461,15 +1462,22 @@ static int __sched do_nanosleep(struct hrtimer_sleeper 
*t, enum hrtimer_mode mod
        if (!t->task)
                return 0;
 
-       rmtp = current->restart_block.nanosleep.rmtp;
-       if (rmtp) {
+       restart = &current->restart_block;
+       if (restart->nanosleep.kind) {
                struct timespec rmt;
                ktime_t rem = hrtimer_expires_remaining(&t->timer);
                if (rem <= 0)
                        return 0;
                rmt = ktime_to_timespec(rem);
 
-               if (copy_to_user(rmtp, &rmt, sizeof(*rmtp)))
+#ifdef CONFIG_COMPAT
+               if (restart->nanosleep.kind == 2) {
+                       if (compat_put_timespec(&rmt,
+                                               restart->nanosleep.compat_rmtp))
+                               return -EFAULT;
+               } else
+#endif
+               if (copy_to_user(restart->nanosleep.rmtp, &rmt, sizeof(rmt)))
                        return -EFAULT;
        }
        return -ERESTART_RESTARTBLOCK;
@@ -1535,10 +1543,32 @@ SYSCALL_DEFINE2(nanosleep, struct timespec __user *, 
rqtp,
        if (!timespec64_valid(&tu64))
                return -EINVAL;
 
+       current->restart_block.nanosleep.kind = rmtp ? 1 : 0;
        current->restart_block.nanosleep.rmtp = rmtp;
        return hrtimer_nanosleep(&tu64, HRTIMER_MODE_REL, CLOCK_MONOTONIC);
 }
 
+#ifdef CONFIG_COMPAT
+
+COMPAT_SYSCALL_DEFINE2(nanosleep, struct compat_timespec __user *, rqtp,
+                      struct compat_timespec __user *, rmtp)
+{
+       struct timespec tu;
+       struct timespec64 tu64;
+
+       if (compat_get_timespec(&tu, rqtp))
+               return -EFAULT;
+
+       tu64 = timespec_to_timespec64(tu);
+       if (!timespec64_valid(&tu64))
+               return -EINVAL;
+
+       current->restart_block.nanosleep.kind = rmtp ? 2 : 0;
+       current->restart_block.nanosleep.compat_rmtp = rmtp;
+       return hrtimer_nanosleep(&tu64, HRTIMER_MODE_REL, CLOCK_MONOTONIC);
+}
+#endif
+
 /*
  * Functions related to boot-time initialization:
  */
diff --git a/kernel/time/posix-cpu-timers.c b/kernel/time/posix-cpu-timers.c
index f999b3ebba7e..b88bc3fc56c5 100644
--- a/kernel/time/posix-cpu-timers.c
+++ b/kernel/time/posix-cpu-timers.c
@@ -12,6 +12,7 @@
 #include <trace/events/timer.h>
 #include <linux/tick.h>
 #include <linux/workqueue.h>
+#include <linux/compat.h>
 
 #include "posix-timers.h"
 
@@ -1243,8 +1244,7 @@ static int do_cpu_nanosleep(const clockid_t which_clock, 
int flags,
        timer.it_process = current;
        if (!error) {
                static struct itimerspec64 zero_it;
-               struct restart_block *restart = &current->restart_block;
-               struct timespec __user *rmtp;
+               struct restart_block *restart;
 
                memset(&it, 0, sizeof it);
                it.it_value = *rqtp;
@@ -1311,11 +1311,19 @@ static int do_cpu_nanosleep(const clockid_t 
which_clock, int flags,
                /*
                 * Report back to the user the time still remaining.
                 */
-               rmtp = restart->nanosleep.rmtp;
-               if (rmtp) {
+               restart = &current->restart_block;
+               if (restart->nanosleep.kind) {
                        struct timespec ts;
                        ts = timespec64_to_timespec(it.it_value);
-                       if (copy_to_user(rmtp, &ts, sizeof(*rmtp)))
+#ifdef CONFIG_COMPAT
+                       if (restart->nanosleep.kind == 2) {
+                               if (compat_put_timespec(&ts,
+                                               restart->nanosleep.compat_rmtp))
+                                       return -EFAULT;
+                       } else
+#endif
+                       if (copy_to_user(restart->nanosleep.rmtp, &ts,
+                                       sizeof(ts)))
                                return -EFAULT;
                }
                restart->nanosleep.expires = timespec64_to_ns(rqtp);
diff --git a/kernel/time/posix-stubs.c b/kernel/time/posix-stubs.c
index 156a5e6f3bd2..2fe2322d1243 100644
--- a/kernel/time/posix-stubs.c
+++ b/kernel/time/posix-stubs.c
@@ -17,6 +17,7 @@
 #include <linux/ktime.h>
 #include <linux/timekeeping.h>
 #include <linux/posix-timers.h>
+#include <linux/compat.h>
 
 asmlinkage long sys_ni_posix_timers(void)
 {
@@ -117,6 +118,7 @@ SYSCALL_DEFINE4(clock_nanosleep, const clockid_t, 
which_clock, int, flags,
                        return -EINVAL;
                if (flags & TIMER_ABSTIME)
                        rmtp = NULL;
+               current->restart_block.nanosleep.kind = rmtp ? 1 : 0;
                current->restart_block.nanosleep.rmtp = rmtp;
                return hrtimer_nanosleep(&t64, flags & TIMER_ABSTIME ?
                                         HRTIMER_MODE_ABS : HRTIMER_MODE_REL,
@@ -127,8 +129,31 @@ SYSCALL_DEFINE4(clock_nanosleep, const clockid_t, 
which_clock, int, flags,
 }
 
 #ifdef CONFIG_COMPAT
-long clock_nanosleep_restart(struct restart_block *restart_block)
+COMPAT_SYSCALL_DEFINE4(clock_nanosleep, clockid_t, which_clock, int, flags,
+                      struct compat_timespec __user *, rqtp,
+                      struct compat_timespec __user *, rmtp)
 {
-       return hrtimer_nanosleep_restart(restart_block);
+       struct timespec64 t64;
+       struct timespec t;
+
+       switch (which_clock) {
+       case CLOCK_REALTIME:
+       case CLOCK_MONOTONIC:
+       case CLOCK_BOOTTIME:
+               if (compat_get_timespec(&t, rqtp))
+                       return -EFAULT;
+               t64 = timespec_to_timespec64(t);
+               if (!timespec64_valid(&t64))
+                       return -EINVAL;
+               if (flags & TIMER_ABSTIME)
+                       rmtp = NULL;
+               current->restart_block.nanosleep.kind = rmtp ? 2 : 0;
+               current->restart_block.nanosleep.compat_rmtp = rmtp;
+               return hrtimer_nanosleep(&t64, flags & TIMER_ABSTIME ?
+                                        HRTIMER_MODE_ABS : HRTIMER_MODE_REL,
+                                        which_clock);
+       default:
+               return -EINVAL;
+       }
 }
 #endif
diff --git a/kernel/time/posix-timers.c b/kernel/time/posix-timers.c
index f39ed4bdcc5a..b729e1c0b57d 100644
--- a/kernel/time/posix-timers.c
+++ b/kernel/time/posix-timers.c
@@ -49,6 +49,7 @@
 #include <linux/workqueue.h>
 #include <linux/export.h>
 #include <linux/hashtable.h>
+#include <linux/compat.h>
 
 #include "timekeeping.h"
 #include "posix-timers.h"
@@ -1054,25 +1055,40 @@ SYSCALL_DEFINE4(clock_nanosleep, const clockid_t, 
which_clock, int, flags,
                return -EINVAL;
        if (flags & TIMER_ABSTIME)
                rmtp = NULL;
+       current->restart_block.nanosleep.kind = rmtp ? 1 : 0;
        current->restart_block.nanosleep.rmtp = rmtp;
 
        return kc->nsleep(which_clock, flags, &t64);
 }
 
-/*
- * This will restart clock_nanosleep. This is required only by
- * compat_clock_nanosleep_restart for now.
- */
-long clock_nanosleep_restart(struct restart_block *restart_block)
+#ifdef CONFIG_COMPAT
+COMPAT_SYSCALL_DEFINE4(clock_nanosleep, clockid_t, which_clock, int, flags,
+                      struct compat_timespec __user *, rqtp,
+                      struct compat_timespec __user *, rmtp)
 {
-       clockid_t which_clock = restart_block->nanosleep.clockid;
        const struct k_clock *kc = clockid_to_kclock(which_clock);
+       struct timespec64 t64;
+       struct timespec t;
 
-       if (WARN_ON_ONCE(!kc || !kc->nsleep_restart))
+       if (!kc)
                return -EINVAL;
+       if (!kc->nsleep)
+               return -ENANOSLEEP_NOTSUP;
+
+       if (compat_get_timespec(&t, rqtp))
+               return -EFAULT;
 
-       return kc->nsleep_restart(restart_block);
+       t64 = timespec_to_timespec64(t);
+       if (!timespec64_valid(&t64))
+               return -EINVAL;
+       if (flags & TIMER_ABSTIME)
+               rmtp = NULL;
+       current->restart_block.nanosleep.kind = rmtp ? 2 : 0;
+       current->restart_block.nanosleep.compat_rmtp = rmtp;
+
+       return kc->nsleep(which_clock, flags, &t64);
 }
+#endif
 
 static const struct k_clock clock_realtime = {
        .clock_getres           = posix_get_hrtimer_res,
-- 
2.11.0

Reply via email to