[PATCH 4/8] nanosleep: Use get_timespec64() and set_timespec64()

From: Deepa Dinamani
Date: Mon Jun 19 2017 - 02:47:28 EST


Usage of these apis and their compat versions makes
the sys_nanosleep() and sys_compat_nanosleep()
implementations simpler.

This patch also serves as a preparatory patch for changing
syscalls to use new time_t data types to support the
y2038 effort by eliminating the processing of user pointers
down the call stack.

Signed-off-by: Deepa Dinamani <deepa.kernel@xxxxxxxxx>
---
include/linux/hrtimer.h | 5 +-
kernel/time/Makefile | 2 +-
kernel/time/alarmtimer.c | 26 +++------
kernel/time/hrtimer.c | 17 ++----
kernel/time/nanosleep.c | 130 +++++++++++++++++++++++++----------------
kernel/time/nanosleep.h | 19 ++++++
kernel/time/posix-cpu-timers.c | 27 ++++-----
kernel/time/posix-stubs.c | 93 +++++++++++------------------
kernel/time/posix-timers.c | 105 +++++++++++++++------------------
kernel/time/posix-timers.h | 5 +-
10 files changed, 211 insertions(+), 218 deletions(-)
create mode 100644 kernel/time/nanosleep.h

diff --git a/include/linux/hrtimer.h b/include/linux/hrtimer.h
index 8c5b10eb7265..e1e6ca9a4db4 100644
--- a/include/linux/hrtimer.h
+++ b/include/linux/hrtimer.h
@@ -453,10 +453,11 @@ static inline u64 hrtimer_forward_now(struct hrtimer *timer,

/* Precise sleep: */
extern long hrtimer_nanosleep(struct timespec64 *rqtp,
- struct timespec __user *rmtp,
+ struct timespec64 *rmtp,
const enum hrtimer_mode mode,
const clockid_t clockid);
-extern long hrtimer_nanosleep_restart(struct restart_block *restart_block);
+extern long hrtimer_nanosleep_restart(struct restart_block *restart_block,
+ struct timespec64 *rmtp);

extern void hrtimer_init_sleeper(struct hrtimer_sleeper *sl,
struct task_struct *tsk);
diff --git a/kernel/time/Makefile b/kernel/time/Makefile
index 938dbf33ef49..0dee7cfc792b 100644
--- a/kernel/time/Makefile
+++ b/kernel/time/Makefile
@@ -1,4 +1,4 @@
-obj-y += time.o timer.o hrtimer.o
+obj-y += time.o timer.o hrtimer.o nanosleep.o
obj-y += timekeeping.o ntp.o clocksource.o jiffies.o timer_list.o
obj-y += timeconv.o timecounter.o alarmtimer.o

diff --git a/kernel/time/alarmtimer.c b/kernel/time/alarmtimer.c
index d8a7a7e214de..567c9ca47974 100644
--- a/kernel/time/alarmtimer.c
+++ b/kernel/time/alarmtimer.c
@@ -710,28 +710,23 @@ static int alarmtimer_do_nsleep(struct alarm *alarm, ktime_t absexp)
* update_rmtp - Update remaining timespec value
* @exp: expiration time
* @type: timer type
- * @rmtp: user pointer to remaining timepsec value
+ * @rmtp: pointer to remaining timespec value
*
* Helper function that fills in rmtp value with time between
* now and the exp value
*/
-static int update_rmtp(ktime_t exp, enum alarmtimer_type type,
- struct timespec __user *rmtp)
+static int update_rmtp(ktime_t exp, enum alarmtimer_type type,
+ struct timespec64 *rmtp)
{
- struct timespec rmt;
ktime_t rem;

rem = ktime_sub(exp, alarm_bases[type].gettime());

if (rem <= 0)
return 0;
- rmt = ktime_to_timespec(rem);
-
- if (copy_to_user(rmtp, &rmt, sizeof(*rmtp)))
- return -EFAULT;
+ *rmtp = ktime_to_timespec64(rem);

return 1;
-
}

/**
@@ -740,12 +735,12 @@ static int update_rmtp(ktime_t exp, enum alarmtimer_type type,
*
* Handles restarted clock_nanosleep calls
*/
-static long __sched alarm_timer_nsleep_restart(struct restart_block *restart)
+static long __sched alarm_timer_nsleep_restart(struct restart_block *restart,
+ struct timespec64 *rmtp)
{
enum alarmtimer_type type = restart->nanosleep.clockid;
- ktime_t exp;
- struct timespec __user *rmtp;
struct alarm alarm;
+ ktime_t exp;
int ret = 0;

exp = restart->nanosleep.expires;
@@ -757,14 +752,12 @@ static long __sched alarm_timer_nsleep_restart(struct restart_block *restart)
if (freezing(current))
alarmtimer_freezerset(exp, type);

- rmtp = restart->nanosleep.rmtp;
if (rmtp) {
ret = update_rmtp(exp, type, rmtp);
if (ret <= 0)
goto out;
}

-
/* The other values in restart are already filled in */
ret = -ERESTART_RESTARTBLOCK;
out:
@@ -782,7 +775,7 @@ static long __sched alarm_timer_nsleep_restart(struct restart_block *restart)
*/
static int alarm_timer_nsleep(const clockid_t which_clock, int flags,
struct timespec64 *tsreq,
- struct timespec __user *rmtp)
+ struct timespec64 *rmtp)
{
enum alarmtimer_type type = clock2alarm(which_clock);
struct restart_block *restart;
@@ -827,10 +820,8 @@ static int alarm_timer_nsleep(const clockid_t which_clock, int flags,
}

restart = &current->restart_block;
- restart->fn = alarm_timer_nsleep_restart;
restart->nanosleep.clockid = type;
restart->nanosleep.expires = exp;
- restart->nanosleep.rmtp = rmtp;
ret = -ERESTART_RESTARTBLOCK;

out:
@@ -850,6 +841,7 @@ const struct k_clock alarm_clock = {
.timer_remaining = alarm_timer_remaining,
.timer_try_to_cancel = alarm_timer_try_to_cancel,
.nsleep = alarm_timer_nsleep,
+ .nsleep_restart = alarm_timer_nsleep_restart,
};
#endif /* CONFIG_POSIX_TIMERS */

diff --git a/kernel/time/hrtimer.c b/kernel/time/hrtimer.c
index e95628910b00..a53857ca28b4 100644
--- a/kernel/time/hrtimer.c
+++ b/kernel/time/hrtimer.c
@@ -1461,26 +1461,22 @@ 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)
+static int update_rmtp(struct hrtimer *timer, struct timespec64 *rmtp)
{
- struct timespec rmt;
ktime_t rem;

rem = hrtimer_expires_remaining(timer);
if (rem <= 0)
return 0;
- rmt = ktime_to_timespec(rem);
-
- if (copy_to_user(rmtp, &rmt, sizeof(*rmtp)))
- return -EFAULT;
+ *rmtp = ktime_to_timespec64(rem);

return 1;
}

-long __sched hrtimer_nanosleep_restart(struct restart_block *restart)
+long __sched hrtimer_nanosleep_restart(struct restart_block *restart,
+ struct timespec64 *rmtp)
{
struct hrtimer_sleeper t;
- struct timespec __user *rmtp;
int ret = 0;

hrtimer_init_on_stack(&t.timer, restart->nanosleep.clockid,
@@ -1490,7 +1486,6 @@ long __sched hrtimer_nanosleep_restart(struct restart_block *restart)
if (do_nanosleep(&t, HRTIMER_MODE_ABS))
goto out;

- rmtp = restart->nanosleep.rmtp;
if (rmtp) {
ret = update_rmtp(&t.timer, rmtp);
if (ret <= 0)
@@ -1504,7 +1499,7 @@ long __sched hrtimer_nanosleep_restart(struct restart_block *restart)
return ret;
}

-long hrtimer_nanosleep(struct timespec64 *rqtp, struct timespec __user *rmtp,
+long hrtimer_nanosleep(struct timespec64 *rqtp, struct timespec64 *rmtp,
const enum hrtimer_mode mode, const clockid_t clockid)
{
struct restart_block *restart;
@@ -1534,9 +1529,7 @@ long hrtimer_nanosleep(struct timespec64 *rqtp, struct timespec __user *rmtp,
}

restart = &current->restart_block;
- restart->fn = hrtimer_nanosleep_restart;
restart->nanosleep.clockid = t.timer.base->clockid;
- restart->nanosleep.rmtp = rmtp;
restart->nanosleep.expires = hrtimer_get_expires_tv64(&t.timer);

ret = -ERESTART_RESTARTBLOCK;
diff --git a/kernel/time/nanosleep.c b/kernel/time/nanosleep.c
index 2b6e6980b65d..dd7d792b008b 100644
--- a/kernel/time/nanosleep.c
+++ b/kernel/time/nanosleep.c
@@ -1,64 +1,67 @@
-SYSCALL_DEFINE2(nanosleep, struct timespec __user *, rqtp,
- struct timespec __user *, rmtp)
+#include <linux/syscalls.h>
+#include <linux/compat.h>
+
+#include "nanosleep.h"
+
+long nanosleep_process_return(long ret,
+ const struct timespec64 *rmtp_kernel,
+ struct timespec __user *rmtp,
+ long (*fn)(struct restart_block *))
{
- struct timespec64 tu64;
- struct timespec tu;
+ struct restart_block *restart = &current->restart_block;

- if (copy_from_user(&tu, rqtp, sizeof(tu)))
+ if ((ret == -ERESTART_RESTARTBLOCK) && rmtp &&
+ put_timespec64(rmtp_kernel, rmtp))
return -EFAULT;

- tu64 = timespec_to_timespec64(tu);
- if (!timespec64_valid(&tu64))
- return -EINVAL;
+ if (ret == -ERESTART_RESTARTBLOCK) {
+ restart->nanosleep.rmtp = rmtp;
+ restart->fn = fn;
+ }

- return hrtimer_nanosleep(&tu64, rmtp, HRTIMER_MODE_REL, CLOCK_MONOTONIC);
+ return ret;
}

-#ifdef CONFIG_COMPAT
-static long compat_nanosleep_restart(struct restart_block *restart)
+long nanosleep_restart(struct restart_block *restart_block)
{
- struct compat_timespec __user *rmtp;
- struct timespec rmt;
- mm_segment_t oldfs;
+ struct timespec __user *rmtp = restart_block->nanosleep.rmtp;
+ struct timespec64 rmt;
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;
- }
+ ret = hrtimer_nanosleep_restart(restart_block,
+ rmtp ? &rmt : NULL);

- return ret;
+ return nanosleep_process_return(ret, &rmt,
+ rmtp,
+ nanosleep_restart);
}

-COMPAT_SYSCALL_DEFINE2(nanosleep, struct compat_timespec __user *, rqtp,
- struct compat_timespec __user *, rmtp)
+SYSCALL_DEFINE2(nanosleep, struct timespec __user *, rqtp,
+ struct timespec __user *, rmtp)
{
- struct timespec tu, rmt;
- struct timespec64 tu64;
- mm_segment_t oldfs;
- long ret;
+ struct timespec64 in;
+ struct timespec64 out;
+ int err;

- if (compat_get_timespec(&tu, rqtp))
+ if (get_timespec64(&in, rqtp))
return -EFAULT;

- tu64 = timespec_to_timespec64(tu);
- if (!timespec64_valid(&tu64))
+ if (!timespec64_valid(&in))
return -EINVAL;

- oldfs = get_fs();
- set_fs(KERNEL_DS);
- ret = hrtimer_nanosleep(&tu64,
- rmtp ? (struct timespec __user *)&rmt : NULL,
+ err = hrtimer_nanosleep(&in, rmtp ? &out : NULL,
HRTIMER_MODE_REL, CLOCK_MONOTONIC);
- set_fs(oldfs);
+
+ return nanosleep_process_return(err, &out, rmtp, nanosleep_restart);
+}
+
+#ifdef CONFIG_COMPAT
+long compat_nanosleep_process_return(long err,
+ struct timespec64 *rmt,
+ struct compat_timespec __user *rmtp,
+ long (*fn)(struct restart_block *))
+{
+ struct restart_block *restart = &current->restart_block;

/*
* hrtimer_nanosleep() can only return 0 or
@@ -79,16 +82,45 @@ COMPAT_SYSCALL_DEFINE2(nanosleep, struct compat_timespec __user *, rqtp,
* 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;
+ if ((err == -ERESTART_RESTARTBLOCK) && rmtp &&
+ compat_put_timespec64(rmt, rmtp))
+ return -EFAULT;

- restart->fn = compat_nanosleep_restart;
+ if (err == -ERESTART_RESTARTBLOCK) {
+ restart->fn = fn;
restart->nanosleep.compat_rmtp = rmtp;
-
- if (rmtp && compat_put_timespec(&rmt, rmtp))
- return -EFAULT;
}
- return ret;
+ return err;
}
-#endif

+long compat_nanosleep_restart(struct restart_block *restart)
+{
+ struct compat_timespec __user *rmtp = restart->nanosleep.compat_rmtp;
+ struct timespec64 kernel_rmt;
+ long ret;
+
+ ret = hrtimer_nanosleep_restart(restart, rmtp ? &kernel_rmt : NULL);
+
+ return compat_nanosleep_process_return(ret, &kernel_rmt, rmtp,
+ compat_nanosleep_restart);
+}
+
+COMPAT_SYSCALL_DEFINE2(nanosleep, struct compat_timespec __user *, rqtp,
+ struct compat_timespec __user *, rmtp)
+{
+ struct timespec64 tu, rmt;
+ long ret;
+
+ if (compat_get_timespec64(&tu, rqtp))
+ return -EFAULT;
+
+ if (!timespec64_valid(&tu))
+ return -EINVAL;
+
+ ret = hrtimer_nanosleep(&tu, rmtp ? &rmt : NULL,
+ HRTIMER_MODE_REL, CLOCK_MONOTONIC);
+
+ return compat_nanosleep_process_return(ret, &rmt, rmtp,
+ compat_nanosleep_restart);
+}
+#endif
diff --git a/kernel/time/nanosleep.h b/kernel/time/nanosleep.h
new file mode 100644
index 000000000000..68c924e0af14
--- /dev/null
+++ b/kernel/time/nanosleep.h
@@ -0,0 +1,19 @@
+#include <linux/compat.h>
+
+long nanosleep_restart(struct restart_block *restart_block);
+
+long nanosleep_process_return(long ret,
+ const struct timespec64 *rmtp_kernel,
+ struct timespec __user *rmtp,
+ long (*fn)(struct restart_block *));
+
+
+#ifdef CONFIG_COMPAT
+long compat_nanosleep_restart(struct restart_block *restart);
+
+long compat_nanosleep_process_return(long err,
+ struct timespec64 *rmt,
+ struct compat_timespec __user *rmtp,
+ long (*fn)(struct restart_block *));
+
+#endif
diff --git a/kernel/time/posix-cpu-timers.c b/kernel/time/posix-cpu-timers.c
index cb4a4eb44279..24df407e2a6d 100644
--- a/kernel/time/posix-cpu-timers.c
+++ b/kernel/time/posix-cpu-timers.c
@@ -1310,14 +1310,14 @@ static int do_cpu_nanosleep(const clockid_t which_clock, int flags,
return error;
}

-static long posix_cpu_nsleep_restart(struct restart_block *restart_block);
+static long posix_cpu_nsleep_restart(struct restart_block *restart_block,
+ struct timespec64 *rmtp);

static int posix_cpu_nsleep(const clockid_t which_clock, int flags,
- struct timespec64 *rqtp, struct timespec __user *rmtp)
+ struct timespec64 *rqtp, struct timespec64 *rmtp)
{
struct restart_block *restart_block = &current->restart_block;
struct itimerspec64 it;
- struct timespec ts;
int error;

/*
@@ -1337,24 +1337,20 @@ static int posix_cpu_nsleep(const clockid_t which_clock, int flags,
/*
* Report back to the user the time still remaining.
*/
- ts = timespec64_to_timespec(it.it_value);
- if (rmtp && copy_to_user(rmtp, &ts, sizeof(*rmtp)))
- return -EFAULT;
+ *rmtp = it.it_value;

- restart_block->fn = posix_cpu_nsleep_restart;
restart_block->nanosleep.clockid = which_clock;
- restart_block->nanosleep.rmtp = rmtp;
restart_block->nanosleep.expires = timespec64_to_ns(rqtp);
}
return error;
}

-static long posix_cpu_nsleep_restart(struct restart_block *restart_block)
+static long posix_cpu_nsleep_restart(struct restart_block *restart_block,
+ struct timespec64 *rmtp)
{
clockid_t which_clock = restart_block->nanosleep.clockid;
struct itimerspec64 it;
struct timespec64 t;
- struct timespec tmp;
int error;

t = ns_to_timespec64(restart_block->nanosleep.expires);
@@ -1362,14 +1358,10 @@ static long posix_cpu_nsleep_restart(struct restart_block *restart_block)
error = do_cpu_nanosleep(which_clock, TIMER_ABSTIME, &t, &it);

if (error == -ERESTART_RESTARTBLOCK) {
- struct timespec __user *rmtp = restart_block->nanosleep.rmtp;
/*
* Report back to the user the time still remaining.
*/
- tmp = timespec64_to_timespec(it.it_value);
- if (rmtp && copy_to_user(rmtp, &tmp, sizeof(*rmtp)))
- return -EFAULT;
-
+ *rmtp = it.it_value;
restart_block->nanosleep.expires = timespec64_to_ns(&t);
}
return error;
@@ -1396,11 +1388,12 @@ static int process_cpu_timer_create(struct k_itimer *timer)
}
static int process_cpu_nsleep(const clockid_t which_clock, int flags,
struct timespec64 *rqtp,
- struct timespec __user *rmtp)
+ struct timespec64 *rmtp)
{
return posix_cpu_nsleep(PROCESS_CLOCK, flags, rqtp, rmtp);
}
-static long process_cpu_nsleep_restart(struct restart_block *restart_block)
+static long process_cpu_nsleep_restart(struct restart_block *restart_block,
+ struct timespec64 *tp)
{
return -EINVAL;
}
diff --git a/kernel/time/posix-stubs.c b/kernel/time/posix-stubs.c
index cd1b9a2e2618..61daf3576e85 100644
--- a/kernel/time/posix-stubs.c
+++ b/kernel/time/posix-stubs.c
@@ -11,6 +11,7 @@

#include <linux/linkage.h>
#include <linux/kernel.h>
+#include <linux/compat.h>
#include <linux/sched.h>
#include <linux/errno.h>
#include <linux/syscalls.h>
@@ -19,6 +20,8 @@
#include <linux/timekeeping.h>
#include <linux/posix-timers.h>

+#include "nanosleep.h"
+
asmlinkage long sys_ni_posix_timers(void)
{
pr_err_once("process %d (%s) attempted a POSIX timer syscall "
@@ -100,28 +103,44 @@ SYSCALL_DEFINE2(clock_getres, const clockid_t, which_clock, struct timespec __us
}
}

-SYSCALL_DEFINE4(clock_nanosleep, const clockid_t, which_clock, int, flags,
- const struct timespec __user *, rqtp,
- struct timespec __user *, rmtp)
+static long do_clock_nanosleep(const clockid_t which_clock,
+ int flags,
+ struct timespec64 *rqtp,
+ struct timespec64 *rmtp)
{
- struct timespec64 t64;
- struct timespec t;
+ long ret;

switch (which_clock) {
case CLOCK_REALTIME:
case CLOCK_MONOTONIC:
case CLOCK_BOOTTIME:
- if (copy_from_user(&t, rqtp, sizeof (struct timespec)))
- return -EFAULT;
- t64 = timespec_to_timespec64(t);
- if (!timespec64_valid(&t64))
+ if (!timespec64_valid(rqtp))
return -EINVAL;
- return hrtimer_nanosleep(&t64, rmtp, flags & TIMER_ABSTIME ?
+ ret = hrtimer_nanosleep(rqtp, rmtp, flags & TIMER_ABSTIME ?
HRTIMER_MODE_ABS : HRTIMER_MODE_REL,
which_clock);
+ break;
default:
return -EINVAL;
}
+
+ return ret;
+}
+
+SYSCALL_DEFINE4(clock_nanosleep, const clockid_t, which_clock, int, flags,
+ const struct timespec __user *, rqtp,
+ struct timespec __user *, rmtp)
+{
+ struct timespec64 in, out;
+ struct timespec64 *rtn = rmtp ? &out : NULL;
+ long ret;
+
+ if (get_timespec64(&in, rqtp))
+ return -EFAULT;
+
+ ret = do_clock_nanosleep(which_clock, flags, &in, rtn);
+
+ return nanosleep_process_return(ret, rtn, rmtp, nanosleep_restart);
}

#ifdef CONFIG_COMPAT
@@ -180,63 +199,19 @@ COMPAT_SYSCALL_DEFINE2(clock_getres, clockid_t, which_clock,
return err;
}

-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)
{
+ struct timespec64 in, out;
long err;
- mm_segment_t oldfs;
- struct timespec in, out;
- struct restart_block *restart;

- if (compat_get_timespec(&in, rqtp))
+ if (compat_get_timespec64(&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;
+ err = do_clock_nanosleep(which_clock, flags, &in, &out);

- if (err == -ERESTART_RESTARTBLOCK) {
- restart = &current->restart_block;
- restart->fn = compat_clock_nanosleep_restart;
- restart->nanosleep.compat_rmtp = rmtp;
- }
- return err;
+ return compat_nanosleep_process_return(err, &out, rmtp,
+ compat_nanosleep_restart);
}
#endif
diff --git a/kernel/time/posix-timers.c b/kernel/time/posix-timers.c
index 009a9145d64d..58c2f9c2c2c8 100644
--- a/kernel/time/posix-timers.c
+++ b/kernel/time/posix-timers.c
@@ -37,6 +37,7 @@
#include <linux/mutex.h>
#include <linux/sched/task.h>

+#include <linux/compat.h>
#include <linux/uaccess.h>
#include <linux/list.h>
#include <linux/init.h>
@@ -53,6 +54,7 @@

#include "timekeeping.h"
#include "posix-timers.h"
+#include "nanosleep.h"

/*
* Management arrays for POSIX timers. Timers are now kept in static hash table
@@ -1027,49 +1029,52 @@ SYSCALL_DEFINE2(clock_getres, const clockid_t, which_clock,
* nanosleep for monotonic and realtime clocks
*/
static int common_nsleep(const clockid_t which_clock, int flags,
- struct timespec64 *tsave, struct timespec __user *rmtp)
+ struct timespec64 *tsave, struct timespec64 *rmtp)
{
return hrtimer_nanosleep(tsave, rmtp, flags & TIMER_ABSTIME ?
HRTIMER_MODE_ABS : HRTIMER_MODE_REL,
which_clock);
}

+long clock_nanosleep_restart(struct restart_block *restart_block)
+{
+ clockid_t which_clock = restart_block->nanosleep.clockid;
+ const struct k_clock *kc = clockid_to_kclock(which_clock);
+ struct timespec64 rmt;
+ long ret;
+
+ if (WARN_ON_ONCE(!kc || !kc->nsleep_restart))
+ return -EINVAL;
+
+ ret = kc->nsleep_restart(restart_block,
+ restart_block->nanosleep.rmtp ? &rmt : NULL);
+ return nanosleep_process_return(ret, &rmt,
+ restart_block->nanosleep.rmtp,
+ clock_nanosleep_restart);
+}
+
SYSCALL_DEFINE4(clock_nanosleep, const clockid_t, which_clock, int, flags,
const struct timespec __user *, rqtp,
struct timespec __user *, rmtp)
{
const struct k_clock *kc = clockid_to_kclock(which_clock);
- struct timespec64 t64;
- struct timespec t;
+ struct timespec64 in, out;
+ long err;

if (!kc)
return -EINVAL;
if (!kc->nsleep)
return -ENANOSLEEP_NOTSUP;

- if (copy_from_user(&t, rqtp, sizeof (struct timespec)))
+ if (get_timespec64(&in, rqtp))
return -EFAULT;

- t64 = timespec_to_timespec64(t);
- if (!timespec64_valid(&t64))
+ if (!timespec64_valid(&in))
return -EINVAL;

- return kc->nsleep(which_clock, flags, &t64, rmtp);
-}
-
-/*
- * 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)
-{
- clockid_t which_clock = restart_block->nanosleep.clockid;
- const struct k_clock *kc = clockid_to_kclock(which_clock);
-
- if (WARN_ON_ONCE(!kc || !kc->nsleep_restart))
- return -EINVAL;
-
- return kc->nsleep_restart(restart_block);
+ err = kc->nsleep(which_clock, flags, &in, rmtp ? &out : NULL);
+ return nanosleep_process_return(err, &out, rmtp,
+ clock_nanosleep_restart);
}

static const struct k_clock clock_realtime = {
@@ -1181,57 +1186,40 @@ static const struct k_clock *clockid_to_kclock(const clockid_t 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;
+ clockid_t which_clock = restart->nanosleep.clockid;
+ const struct k_clock *kc = clockid_to_kclock(which_clock);
+ struct timespec64 rmt;
+ long err;

- restart->nanosleep.rmtp = (struct timespec __user *) &tu;
- oldfs = get_fs();
- set_fs(KERNEL_DS);
- err = clock_nanosleep_restart(restart);
- set_fs(oldfs);
+ if (WARN_ON_ONCE(!kc || !kc->nsleep_restart))
+ return -EINVAL;

- if ((err == -ERESTART_RESTARTBLOCK) && rmtp &&
- compat_put_timespec(&tu, rmtp))
- return -EFAULT;
+ err = kc->nsleep_restart(restart, rmtp ? &rmt : NULL);

- if (err == -ERESTART_RESTARTBLOCK) {
- restart->fn = compat_clock_nanosleep_restart;
- restart->nanosleep.compat_rmtp = rmtp;
- }
- return err;
+ return compat_nanosleep_process_return(err, &rmt, rmtp,
+ compat_clock_nanosleep_restart);
}

COMPAT_SYSCALL_DEFINE4(clock_nanosleep, clockid_t, which_clock, int, flags,
struct compat_timespec __user *, rqtp,
struct compat_timespec __user *, rmtp)
{
+ const struct k_clock *kc = clockid_to_kclock(which_clock);
+ struct timespec64 in, out;
long err;
- mm_segment_t oldfs;
- struct timespec in, out;
- struct restart_block *restart;

- if (compat_get_timespec(&in, rqtp))
+ if (compat_get_timespec64(&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 (!kc)
+ return -EINVAL;
+ if (!kc->nsleep)
+ return -ENANOSLEEP_NOTSUP;
+ err = kc->nsleep(which_clock, flags, &in, rmtp ? &out : NULL);

- if (err == -ERESTART_RESTARTBLOCK) {
- restart = &current->restart_block;
- restart->fn = compat_clock_nanosleep_restart;
- restart->nanosleep.compat_rmtp = rmtp;
- }
- return err;
+ return compat_nanosleep_process_return(err, &out, rmtp,
+ compat_clock_nanosleep_restart);
}

COMPAT_SYSCALL_DEFINE4(timer_settime, timer_t, timer_id, int, flags,
@@ -1324,5 +1312,4 @@ COMPAT_SYSCALL_DEFINE2(clock_getres, clockid_t, which_clock,
return -EFAULT;
return err;
}
-
#endif
diff --git a/kernel/time/posix-timers.h b/kernel/time/posix-timers.h
index b086f5ba2f5b..85a3f34593df 100644
--- a/kernel/time/posix-timers.h
+++ b/kernel/time/posix-timers.h
@@ -10,8 +10,9 @@ struct k_clock {
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 timespec64 *, struct timespec __user *);
- long (*nsleep_restart)(struct restart_block *restart_block);
+ struct timespec64 *, struct timespec64 *);
+ long (*nsleep_restart)(struct restart_block *restart_block,
+ struct timespec64 *);
int (*timer_set)(struct k_itimer *timr, int flags,
struct itimerspec64 *new_setting,
struct itimerspec64 *old_setting);
--
2.11.0