diff mbox

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

Message ID 20170619064515.922-5-deepa.kernel@gmail.com (mailing list archive)
State New, archived
Headers show

Commit Message

Deepa Dinamani June 19, 2017, 6:45 a.m. UTC
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@gmail.com>
---
 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 mbox

Patch

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);