This changes all system calls that get a select()-style timeout
from using timespec internally to using timespec64, and changes
the user-facing API to use __kernel_timespec, as a preparation
for 32-bit architectures to set CONFIG_COMPAT_TIME.

Note that all these system calls pass a time interval into the
kernel, not an absolute time, so using a 32-bit value is
sufficient for the operation, but when user space changes over
to using 64-bit time_t, its own data structures change, and
this makes the kernel match what user space will use.

There may be a small slowdown from using timespec64_sub and
timespec64_add_safe instead of the 32-bit variants, so any
suggestion for how to avoid that overhead would be welcome.

Signed-off-by: Arnd Bergmann <[email protected]>
---
 fs/compat.c              | 16 ++++++------
 fs/eventpoll.c           | 12 ++++-----
 fs/select.c              | 64 ++++++++++++++++++++++++------------------------
 include/linux/poll.h     | 10 ++++----
 include/linux/socket.h   |  4 ++-
 include/linux/syscalls.h |  6 ++---
 include/linux/time.h     |  9 -------
 include/linux/time64.h   |  8 +++++-
 net/socket.c             | 18 +++++++-------
 9 files changed, 73 insertions(+), 74 deletions(-)

diff --git a/fs/compat.c b/fs/compat.c
index c5065aa1852c..78ffecce6379 100644
--- a/fs/compat.c
+++ b/fs/compat.c
@@ -1098,10 +1098,10 @@ COMPAT_SYSCALL_DEFINE4(openat, int, dfd, const char 
__user *, filename, int, fla
 #ifdef CONFIG_COMPAT_TIME
 #define __COMPAT_NFDBITS       (8 * sizeof(compat_ulong_t))
 
-static int poll_select_copy_remaining(struct timespec *end_time, void __user 
*p,
+static int poll_select_copy_remaining(struct timespec64 *end_time, void __user 
*p,
                                      int timeval, int ret)
 {
-       struct timespec ts;
+       struct timespec64 ts;
 
        if (!p)
                return ret;
@@ -1113,8 +1113,8 @@ static int poll_select_copy_remaining(struct timespec 
*end_time, void __user *p,
        if (!end_time->tv_sec && !end_time->tv_nsec)
                return ret;
 
-       ktime_get_ts(&ts);
-       ts = timespec_sub(*end_time, ts);
+       ktime_get_ts64(&ts);
+       ts = timespec64_sub(*end_time, ts);
        if (ts.tv_sec < 0)
                ts.tv_sec = ts.tv_nsec = 0;
 
@@ -1228,7 +1228,7 @@ int compat_set_fd_set(unsigned long nr, compat_ulong_t 
__user *ufdset,
  */
 int compat_core_sys_select(int n, compat_ulong_t __user *inp,
        compat_ulong_t __user *outp, compat_ulong_t __user *exp,
-       struct timespec *end_time)
+       struct timespec64 *end_time)
 {
        fd_set_bits fds;
        void *bits;
@@ -1301,7 +1301,7 @@ COMPAT_SYSCALL_DEFINE5(select, int, n, compat_ulong_t 
__user *, inp,
        compat_ulong_t __user *, outp, compat_ulong_t __user *, exp,
        struct compat_timeval __user *, tvp)
 {
-       struct timespec end_time, *to = NULL;
+       struct timespec64 end_time, *to = NULL;
        struct compat_timeval tv;
        int ret;
 
@@ -1348,7 +1348,7 @@ static long do_compat_pselect(int n, compat_ulong_t 
__user *inp,
        compat_sigset_t ss32;
        sigset_t ksigmask, sigsaved;
        struct compat_timespec ts;
-       struct timespec end_time, *to = NULL;
+       struct timespec64 end_time, *to = NULL;
        int ret;
 
        if (tsp) {
@@ -1417,7 +1417,7 @@ COMPAT_SYSCALL_DEFINE5(ppoll, struct pollfd __user *, 
ufds,
        compat_sigset_t ss32;
        sigset_t ksigmask, sigsaved;
        struct compat_timespec ts;
-       struct timespec end_time, *to = NULL;
+       struct timespec64 end_time, *to = NULL;
        int ret;
 
        if (tsp) {
diff --git a/fs/eventpoll.c b/fs/eventpoll.c
index 1e009cad8d5c..6d1a966967a5 100644
--- a/fs/eventpoll.c
+++ b/fs/eventpoll.c
@@ -1554,15 +1554,15 @@ static int ep_send_events(struct eventpoll *ep,
        return ep_scan_ready_list(ep, ep_send_events_proc, &esed, 0, false);
 }
 
-static inline struct timespec ep_set_mstimeout(long ms)
+static inline struct timespec64 ep_set_mstimeout(long ms)
 {
-       struct timespec now, ts = {
+       struct timespec64 now, ts = {
                .tv_sec = ms / MSEC_PER_SEC,
                .tv_nsec = NSEC_PER_MSEC * (ms % MSEC_PER_SEC),
        };
 
-       ktime_get_ts(&now);
-       return timespec_add_safe(now, ts);
+       ktime_get_ts64(&now);
+       return timespec64_add_safe(now, ts);
 }
 
 /**
@@ -1592,11 +1592,11 @@ static int ep_poll(struct eventpoll *ep, struct 
epoll_event __user *events,
        ktime_t expires, *to = NULL;
 
        if (timeout > 0) {
-               struct timespec end_time = ep_set_mstimeout(timeout);
+               struct timespec64 end_time = ep_set_mstimeout(timeout);
 
                slack = select_estimate_accuracy(&end_time);
                to = &expires;
-               *to = timespec_to_ktime(end_time);
+               *to = timespec64_to_ktime(end_time);
        } else if (timeout == 0) {
                /*
                 * Avoid the unnecessary trip to the wait queue loop, if the
diff --git a/fs/select.c b/fs/select.c
index f684c750e08a..5aa82769ec8b 100644
--- a/fs/select.c
+++ b/fs/select.c
@@ -47,7 +47,7 @@
 
 #define MAX_SLACK      (100 * NSEC_PER_MSEC)
 
-static long __estimate_accuracy(struct timespec *tv)
+static long __estimate_accuracy(struct timespec64 *tv)
 {
        long slack;
        int divfactor = 1000;
@@ -70,10 +70,10 @@ static long __estimate_accuracy(struct timespec *tv)
        return slack;
 }
 
-long select_estimate_accuracy(struct timespec *tv)
+long select_estimate_accuracy(struct timespec64 *tv)
 {
        unsigned long ret;
-       struct timespec now;
+       struct timespec64 now;
 
        /*
         * Realtime tasks get a slack of 0 for obvious reasons.
@@ -82,8 +82,8 @@ long select_estimate_accuracy(struct timespec *tv)
        if (rt_task(current))
                return 0;
 
-       ktime_get_ts(&now);
-       now = timespec_sub(*tv, now);
+       ktime_get_ts64(&now);
+       now = timespec64_sub(*tv, now);
        ret = __estimate_accuracy(&now);
        if (ret < current->timer_slack_ns)
                return current->timer_slack_ns;
@@ -269,27 +269,27 @@ EXPORT_SYMBOL(poll_schedule_timeout);
  *
  * Returns -EINVAL if sec/nsec are not normalized. Otherwise 0.
  */
-int poll_select_set_timeout(struct timespec *to, long sec, long nsec)
+int poll_select_set_timeout(struct timespec64 *to, s64 sec, long nsec)
 {
-       struct timespec ts = {.tv_sec = sec, .tv_nsec = nsec};
+       struct timespec64 ts = {.tv_sec = sec, .tv_nsec = nsec};
 
-       if (!timespec_valid(&ts))
+       if (!timespec64_valid(&ts))
                return -EINVAL;
 
        /* Optimize for the zero timeout value here */
        if (!sec && !nsec) {
                to->tv_sec = to->tv_nsec = 0;
        } else {
-               ktime_get_ts(to);
-               *to = timespec_add_safe(*to, ts);
+               ktime_get_ts64(to);
+               *to = timespec64_add_safe(*to, ts);
        }
        return 0;
 }
 
-static int poll_select_copy_remaining(struct timespec *end_time, void __user 
*p,
+static int poll_select_copy_remaining(struct timespec64 *end_time, void __user 
*p,
                                      int timeval, int ret)
 {
-       struct timespec rts;
+       struct timespec64 rts;
        struct timeval rtv;
 
        if (!p)
@@ -302,8 +302,8 @@ static int poll_select_copy_remaining(struct timespec 
*end_time, void __user *p,
        if (!end_time->tv_sec && !end_time->tv_nsec)
                return ret;
 
-       ktime_get_ts(&rts);
-       rts = timespec_sub(*end_time, rts);
+       ktime_get_ts64(&rts);
+       rts = timespec64_sub(*end_time, rts);
        if (rts.tv_sec < 0)
                rts.tv_sec = rts.tv_nsec = 0;
 
@@ -316,7 +316,7 @@ static int poll_select_copy_remaining(struct timespec 
*end_time, void __user *p,
                if (!copy_to_user(p, &rtv, sizeof(rtv)))
                        return ret;
 
-       } else if (!copy_to_user(p, &rts, sizeof(rts)))
+       } else if (!put_timespec64(&rts, p))
                return ret;
 
        /*
@@ -396,7 +396,7 @@ static inline void wait_key_set(poll_table *wait, unsigned 
long in,
                wait->_key |= POLLOUT_SET;
 }
 
-int do_select(int n, fd_set_bits *fds, struct timespec *end_time)
+int do_select(int n, fd_set_bits *fds, struct timespec64 *end_time)
 {
        ktime_t expire, *to = NULL;
        struct poll_wqueues table;
@@ -522,7 +522,7 @@ int do_select(int n, fd_set_bits *fds, struct timespec 
*end_time)
                 * pointer to the expiry value.
                 */
                if (end_time && !to) {
-                       expire = timespec_to_ktime(*end_time);
+                       expire = timespec64_to_ktime(*end_time);
                        to = &expire;
                }
 
@@ -545,7 +545,7 @@ int do_select(int n, fd_set_bits *fds, struct timespec 
*end_time)
  * I'm trying ERESTARTNOHAND which restart only when you want to.
  */
 int core_sys_select(int n, fd_set __user *inp, fd_set __user *outp,
-                          fd_set __user *exp, struct timespec *end_time)
+                          fd_set __user *exp, struct timespec64 *end_time)
 {
        fd_set_bits fds;
        void *bits;
@@ -622,7 +622,7 @@ out_nofds:
 SYSCALL_DEFINE5(select, int, n, fd_set __user *, inp, fd_set __user *, outp,
                fd_set __user *, exp, struct timeval __user *, tvp)
 {
-       struct timespec end_time, *to = NULL;
+       struct timespec64 end_time, *to = NULL;
        struct timeval tv;
        int ret;
 
@@ -644,15 +644,15 @@ SYSCALL_DEFINE5(select, int, n, fd_set __user *, inp, 
fd_set __user *, outp,
 }
 
 static long do_pselect(int n, fd_set __user *inp, fd_set __user *outp,
-                      fd_set __user *exp, struct timespec __user *tsp,
+                      fd_set __user *exp, struct __kernel_timespec __user *tsp,
                       const sigset_t __user *sigmask, size_t sigsetsize)
 {
        sigset_t ksigmask, sigsaved;
-       struct timespec ts, end_time, *to = NULL;
+       struct timespec64 ts, end_time, *to = NULL;
        int ret;
 
        if (tsp) {
-               if (copy_from_user(&ts, tsp, sizeof(ts)))
+               if (get_timespec64(&ts, tsp))
                        return -EFAULT;
 
                to = &end_time;
@@ -698,7 +698,7 @@ static long do_pselect(int n, fd_set __user *inp, fd_set 
__user *outp,
  * the sigset size.
  */
 SYSCALL_DEFINE6(pselect6, int, n, fd_set __user *, inp, fd_set __user *, outp,
-               fd_set __user *, exp, struct timespec __user *, tsp,
+               fd_set __user *, exp, struct __kernel_timespec __user *, tsp,
                void __user *, sig)
 {
        size_t sigsetsize = 0;
@@ -779,7 +779,7 @@ static inline unsigned int do_pollfd(struct pollfd *pollfd, 
poll_table *pwait,
 }
 
 static int do_poll(unsigned int nfds,  struct poll_list *list,
-                  struct poll_wqueues *wait, struct timespec *end_time)
+                  struct poll_wqueues *wait, struct timespec64 *end_time)
 {
        poll_table* pt = &wait->pt;
        ktime_t expire, *to = NULL;
@@ -854,7 +854,7 @@ static int do_poll(unsigned int nfds,  struct poll_list 
*list,
                 * pointer to the expiry value.
                 */
                if (end_time && !to) {
-                       expire = timespec_to_ktime(*end_time);
+                       expire = timespec64_to_ktime(*end_time);
                        to = &expire;
                }
 
@@ -868,7 +868,7 @@ static int do_poll(unsigned int nfds,  struct poll_list 
*list,
                        sizeof(struct pollfd))
 
 int do_sys_poll(struct pollfd __user *ufds, unsigned int nfds,
-               struct timespec *end_time)
+               struct timespec64 *end_time)
 {
        struct poll_wqueues table;
        int err = -EFAULT, fdcount, len, size;
@@ -936,7 +936,7 @@ static long do_restart_poll(struct restart_block 
*restart_block)
 {
        struct pollfd __user *ufds = restart_block->poll.ufds;
        int nfds = restart_block->poll.nfds;
-       struct timespec *to = NULL, end_time;
+       struct timespec64 *to = NULL, end_time;
        int ret;
 
        if (restart_block->poll.has_timeout) {
@@ -957,7 +957,7 @@ static long do_restart_poll(struct restart_block 
*restart_block)
 SYSCALL_DEFINE3(poll, struct pollfd __user *, ufds, unsigned int, nfds,
                int, timeout_msecs)
 {
-       struct timespec end_time, *to = NULL;
+       struct timespec64 end_time, *to = NULL;
        int ret;
 
        if (timeout_msecs >= 0) {
@@ -989,15 +989,15 @@ SYSCALL_DEFINE3(poll, struct pollfd __user *, ufds, 
unsigned int, nfds,
 }
 
 SYSCALL_DEFINE5(ppoll, struct pollfd __user *, ufds, unsigned int, nfds,
-               struct timespec __user *, tsp, const sigset_t __user *, sigmask,
-               size_t, sigsetsize)
+               struct __kernel_timespec __user *, tsp,
+               const sigset_t __user *, sigmask, size_t, sigsetsize)
 {
        sigset_t ksigmask, sigsaved;
-       struct timespec ts, end_time, *to = NULL;
+       struct timespec64 ts, end_time, *to = NULL;
        int ret;
 
        if (tsp) {
-               if (copy_from_user(&ts, tsp, sizeof(ts)))
+               if (get_timespec64(&ts, tsp))
                        return -EFAULT;
 
                to = &end_time;
diff --git a/include/linux/poll.h b/include/linux/poll.h
index c08386fb3e08..92b7f5bea749 100644
--- a/include/linux/poll.h
+++ b/include/linux/poll.h
@@ -96,7 +96,7 @@ extern void poll_initwait(struct poll_wqueues *pwq);
 extern void poll_freewait(struct poll_wqueues *pwq);
 extern int poll_schedule_timeout(struct poll_wqueues *pwq, int state,
                                 ktime_t *expires, unsigned long slack);
-extern long select_estimate_accuracy(struct timespec *tv);
+extern long select_estimate_accuracy(struct timespec64 *tv);
 
 
 static inline int poll_schedule(struct poll_wqueues *pwq, int state)
@@ -153,12 +153,12 @@ void zero_fd_set(unsigned long nr, unsigned long *fdset)
 
 #define MAX_INT64_SECONDS (((s64)(~((u64)0)>>1)/HZ)-1)
 
-extern int do_select(int n, fd_set_bits *fds, struct timespec *end_time);
+extern int do_select(int n, fd_set_bits *fds, struct timespec64 *end_time);
 extern int do_sys_poll(struct pollfd __user * ufds, unsigned int nfds,
-                      struct timespec *end_time);
+                      struct timespec64 *end_time);
 extern int core_sys_select(int n, fd_set __user *inp, fd_set __user *outp,
-                          fd_set __user *exp, struct timespec *end_time);
+                          fd_set __user *exp, struct timespec64 *end_time);
 
-extern int poll_select_set_timeout(struct timespec *to, long sec, long nsec);
+extern int poll_select_set_timeout(struct timespec64 *to, long long sec, long 
nsec);
 
 #endif /* _LINUX_POLL_H */
diff --git a/include/linux/socket.h b/include/linux/socket.h
index 5bf59c8493b7..ebacba2fa111 100644
--- a/include/linux/socket.h
+++ b/include/linux/socket.h
@@ -7,6 +7,8 @@
 #include <linux/uio.h>                 /* iovec support                */
 #include <linux/types.h>               /* pid_t                        */
 #include <linux/compiler.h>            /* __user                       */
+#include <linux/math64.h>
+#include <linux/time64.h>              /* timespec64                   */
 #include <uapi/linux/socket.h>
 
 struct pid;
@@ -335,7 +337,7 @@ struct timespec;
 extern long __sys_recvmsg(int fd, struct user_msghdr __user *msg, unsigned 
flags);
 extern long __sys_sendmsg(int fd, struct user_msghdr __user *msg, unsigned 
flags);
 extern int __sys_recvmmsg(int fd, struct mmsghdr __user *mmsg, unsigned int 
vlen,
-                         unsigned int flags, struct timespec *timeout);
+                         unsigned int flags, struct timespec64 *timeout);
 extern int __sys_sendmmsg(int fd, struct mmsghdr __user *mmsg,
                          unsigned int vlen, unsigned int flags);
 #endif /* _LINUX_SOCKET_H */
diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h
index f3fdc312627b..855897ee0c6d 100644
--- a/include/linux/syscalls.h
+++ b/include/linux/syscalls.h
@@ -612,7 +612,7 @@ asmlinkage long sys_recvfrom(int, void __user *, size_t, 
unsigned,
 asmlinkage long sys_recvmsg(int fd, struct user_msghdr __user *msg, unsigned 
flags);
 asmlinkage long sys_recvmmsg(int fd, struct mmsghdr __user *msg,
                             unsigned int vlen, unsigned flags,
-                            struct timespec __user *timeout);
+                            struct __kernel_timespec __user *timeout);
 asmlinkage long sys_socket(int, int, int);
 asmlinkage long sys_socketpair(int, int, int, int __user *);
 asmlinkage long sys_socketcall(int call, unsigned long __user *args);
@@ -811,10 +811,10 @@ asmlinkage long sys_memfd_create(const char __user 
*uname_ptr, unsigned int flag
 asmlinkage long sys_fallocate(int fd, int mode, loff_t offset, loff_t len);
 asmlinkage long sys_old_readdir(unsigned int, struct old_linux_dirent __user 
*, unsigned int);
 asmlinkage long sys_pselect6(int, fd_set __user *, fd_set __user *,
-                            fd_set __user *, struct timespec __user *,
+                            fd_set __user *, struct __kernel_timespec __user *,
                             void __user *);
 asmlinkage long sys_ppoll(struct pollfd __user *, unsigned int,
-                         struct timespec __user *, const sigset_t __user *,
+                         struct __kernel_timespec __user *, const sigset_t 
__user *,
                          size_t);
 asmlinkage long sys_fanotify_init(unsigned int flags, unsigned int 
event_f_flags);
 asmlinkage long sys_fanotify_mark(int fanotify_fd, unsigned int flags,
diff --git a/include/linux/time.h b/include/linux/time.h
index beebe3a02d43..5b5a64952df7 100644
--- a/include/linux/time.h
+++ b/include/linux/time.h
@@ -56,15 +56,6 @@ static inline unsigned long mktime(const unsigned int year,
 
 extern void set_normalized_timespec(struct timespec *ts, time_t sec, s64 nsec);
 
-/*
- * timespec_add_safe assumes both values are positive and checks
- * for overflow. It will return TIME_T_MAX if the reutrn would be
- * smaller then either of the arguments.
- */
-extern struct timespec timespec_add_safe(const struct timespec lhs,
-                                        const struct timespec rhs);
-
-
 static inline struct timespec timespec_add(struct timespec lhs,
                                                struct timespec rhs)
 {
diff --git a/include/linux/time64.h b/include/linux/time64.h
index 880ebe4b4ba4..4ec589daf565 100644
--- a/include/linux/time64.h
+++ b/include/linux/time64.h
@@ -46,7 +46,6 @@ static inline struct timespec64 timespec_to_timespec64(const 
struct timespec ts)
 # define timespec64_equal              timespec_equal
 # define timespec64_compare            timespec_compare
 # define set_normalized_timespec64     set_normalized_timespec
-# define timespec64_add_safe           timespec_add_safe
 # define timespec64_add                        timespec_add
 # define timespec64_sub                        timespec_sub
 # define timespec64_valid              timespec_valid
@@ -187,6 +186,13 @@ static __always_inline void timespec64_add_ns(struct 
timespec64 *a, u64 ns)
 
 #endif
 
+/*
+ * timespec64_add_safe assumes both values are positive and checks
+ * for overflow. It will return TIME_T_MAX if the reutrn would be
+ * smaller then either of the arguments.
+ */
+extern struct timespec64 timespec64_add_safe(const struct timespec64 lhs,
+                                            const struct timespec64 rhs);
 extern int get_timespec64(struct timespec64 *ts,
                          const struct __kernel_timespec __user *uts);
 extern int put_timespec64(const struct timespec64 *ts,
diff --git a/net/socket.c b/net/socket.c
index 884e32997698..bfe50f60688b 100644
--- a/net/socket.c
+++ b/net/socket.c
@@ -2170,14 +2170,14 @@ SYSCALL_DEFINE3(recvmsg, int, fd, struct user_msghdr 
__user *, msg,
  */
 
 int __sys_recvmmsg(int fd, struct mmsghdr __user *mmsg, unsigned int vlen,
-                  unsigned int flags, struct timespec *timeout)
+                  unsigned int flags, struct timespec64 *timeout)
 {
        int fput_needed, err, datagrams;
        struct socket *sock;
        struct mmsghdr __user *entry;
        struct compat_mmsghdr __user *compat_entry;
        struct msghdr msg_sys;
-       struct timespec end_time;
+       struct timespec64 end_time;
 
        if (timeout &&
            poll_select_set_timeout(&end_time, timeout->tv_sec,
@@ -2229,8 +2229,8 @@ int __sys_recvmmsg(int fd, struct mmsghdr __user *mmsg, 
unsigned int vlen,
                        flags |= MSG_DONTWAIT;
 
                if (timeout) {
-                       ktime_get_ts(timeout);
-                       *timeout = timespec_sub(end_time, *timeout);
+                       ktime_get_ts64(timeout);
+                       *timeout = timespec64_sub(end_time, *timeout);
                        if (timeout->tv_sec < 0) {
                                timeout->tv_sec = timeout->tv_nsec = 0;
                                break;
@@ -2275,10 +2275,10 @@ out_put:
 
 SYSCALL_DEFINE5(recvmmsg, int, fd, struct mmsghdr __user *, mmsg,
                unsigned int, vlen, unsigned int, flags,
-               struct timespec __user *, timeout)
+               struct __kernel_timespec __user *, timeout)
 {
        int datagrams;
-       struct timespec timeout_sys;
+       struct timespec64 timeout_sys;
 
        if (flags & MSG_CMSG_COMPAT)
                return -EINVAL;
@@ -2286,13 +2286,13 @@ SYSCALL_DEFINE5(recvmmsg, int, fd, struct mmsghdr 
__user *, mmsg,
        if (!timeout)
                return __sys_recvmmsg(fd, mmsg, vlen, flags, NULL);
 
-       if (copy_from_user(&timeout_sys, timeout, sizeof(timeout_sys)))
+       if (get_timespec64(&timeout_sys, timeout))
                return -EFAULT;
 
        datagrams = __sys_recvmmsg(fd, mmsg, vlen, flags, &timeout_sys);
 
        if (datagrams > 0 &&
-           copy_to_user(timeout, &timeout_sys, sizeof(timeout_sys)))
+           put_timespec64(&timeout_sys, timeout))
                datagrams = -EFAULT;
 
        return datagrams;
@@ -2410,7 +2410,7 @@ SYSCALL_DEFINE2(socketcall, int, call, unsigned long 
__user *, args)
                break;
        case SYS_RECVMMSG:
                err = sys_recvmmsg(a0, (struct mmsghdr __user *)a1, a[2], a[3],
-                                  (struct timespec __user *)a[4]);
+                                  (struct __kernel_timespec __user *)a[4]);
                break;
        case SYS_ACCEPT4:
                err = sys_accept4(a0, (struct sockaddr __user *)a1,
-- 
2.1.0.rc2

_______________________________________________
Y2038 mailing list
[email protected]
https://lists.linaro.org/mailman/listinfo/y2038

Reply via email to