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 arnd@arndb.de --- 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,