epoll: add syscall epoll_pwait2
authorWillem de Bruijn <willemb@google.com>
Fri, 18 Dec 2020 22:05:38 +0000 (14:05 -0800)
committerLinus Torvalds <torvalds@linux-foundation.org>
Sat, 19 Dec 2020 19:18:38 +0000 (11:18 -0800)
Add syscall epoll_pwait2, an epoll_wait variant with nsec resolution that
replaces int timeout with struct timespec.  It is equivalent otherwise.

    int epoll_pwait2(int fd, struct epoll_event *events,
                     int maxevents,
                     const struct timespec *timeout,
                     const sigset_t *sigset);

The underlying hrtimer is already programmed with nsec resolution.
pselect and ppoll also set nsec resolution timeout with timespec.

The sigset_t in epoll_pwait has a compat variant. epoll_pwait2 needs
the same.

For timespec, only support this new interface on 2038 aware platforms
that define __kernel_timespec_t. So no CONFIG_COMPAT_32BIT_TIME.

Link: https://lkml.kernel.org/r/20201121144401.3727659-3-willemdebruijn.kernel@gmail.com
Signed-off-by: Willem de Bruijn <willemb@google.com>
Cc: Al Viro <viro@zeniv.linux.org.uk>
Cc: Arnd Bergmann <arnd@arndb.de>
Cc: Matthew Wilcox (Oracle) <willy@infradead.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
fs/eventpoll.c

index 1ba4066..a829af0 100644 (file)
@@ -2237,11 +2237,10 @@ SYSCALL_DEFINE4(epoll_wait, int, epfd, struct epoll_event __user *, events,
  * Implement the event wait interface for the eventpoll file. It is the kernel
  * part of the user space epoll_pwait(2).
  */
-SYSCALL_DEFINE6(epoll_pwait, int, epfd, struct epoll_event __user *, events,
-               int, maxevents, int, timeout, const sigset_t __user *, sigmask,
-               size_t, sigsetsize)
+static int do_epoll_pwait(int epfd, struct epoll_event __user *events,
+                         int maxevents, struct timespec64 *to,
+                         const sigset_t __user *sigmask, size_t sigsetsize)
 {
-       struct timespec64 to;
        int error;
 
        /*
@@ -2252,22 +2251,48 @@ SYSCALL_DEFINE6(epoll_pwait, int, epfd, struct epoll_event __user *, events,
        if (error)
                return error;
 
-       error = do_epoll_wait(epfd, events, maxevents,
-                             ep_timeout_to_timespec(&to, timeout));
+       error = do_epoll_wait(epfd, events, maxevents, to);
 
        restore_saved_sigmask_unless(error == -EINTR);
 
        return error;
 }
 
-#ifdef CONFIG_COMPAT
-COMPAT_SYSCALL_DEFINE6(epoll_pwait, int, epfd,
-                       struct epoll_event __user *, events,
-                       int, maxevents, int, timeout,
-                       const compat_sigset_t __user *, sigmask,
-                       compat_size_t, sigsetsize)
+SYSCALL_DEFINE6(epoll_pwait, int, epfd, struct epoll_event __user *, events,
+               int, maxevents, int, timeout, const sigset_t __user *, sigmask,
+               size_t, sigsetsize)
 {
        struct timespec64 to;
+
+       return do_epoll_pwait(epfd, events, maxevents,
+                             ep_timeout_to_timespec(&to, timeout),
+                             sigmask, sigsetsize);
+}
+
+SYSCALL_DEFINE6(epoll_pwait2, int, epfd, struct epoll_event __user *, events,
+               int, maxevents, const struct __kernel_timespec __user *, timeout,
+               const sigset_t __user *, sigmask, size_t, sigsetsize)
+{
+       struct timespec64 ts, *to = NULL;
+
+       if (timeout) {
+               if (get_timespec64(&ts, timeout))
+                       return -EFAULT;
+               to = &ts;
+               if (poll_select_set_timeout(to, ts.tv_sec, ts.tv_nsec))
+                       return -EINVAL;
+       }
+
+       return do_epoll_pwait(epfd, events, maxevents, to,
+                             sigmask, sigsetsize);
+}
+
+#ifdef CONFIG_COMPAT
+static int do_compat_epoll_pwait(int epfd, struct epoll_event __user *events,
+                                int maxevents, struct timespec64 *timeout,
+                                const compat_sigset_t __user *sigmask,
+                                compat_size_t sigsetsize)
+{
        long err;
 
        /*
@@ -2278,13 +2303,47 @@ COMPAT_SYSCALL_DEFINE6(epoll_pwait, int, epfd,
        if (err)
                return err;
 
-       err = do_epoll_wait(epfd, events, maxevents,
-                           ep_timeout_to_timespec(&to, timeout));
+       err = do_epoll_wait(epfd, events, maxevents, timeout);
 
        restore_saved_sigmask_unless(err == -EINTR);
 
        return err;
 }
+
+COMPAT_SYSCALL_DEFINE6(epoll_pwait, int, epfd,
+                      struct epoll_event __user *, events,
+                      int, maxevents, int, timeout,
+                      const compat_sigset_t __user *, sigmask,
+                      compat_size_t, sigsetsize)
+{
+       struct timespec64 to;
+
+       return do_compat_epoll_pwait(epfd, events, maxevents,
+                                    ep_timeout_to_timespec(&to, timeout),
+                                    sigmask, sigsetsize);
+}
+
+COMPAT_SYSCALL_DEFINE6(epoll_pwait2, int, epfd,
+                      struct epoll_event __user *, events,
+                      int, maxevents,
+                      const struct __kernel_timespec __user *, timeout,
+                      const compat_sigset_t __user *, sigmask,
+                      compat_size_t, sigsetsize)
+{
+       struct timespec64 ts, *to = NULL;
+
+       if (timeout) {
+               if (get_timespec64(&ts, timeout))
+                       return -EFAULT;
+               to = &ts;
+               if (poll_select_set_timeout(to, ts.tv_sec, ts.tv_nsec))
+                       return -EINVAL;
+       }
+
+       return do_compat_epoll_pwait(epfd, events, maxevents, to,
+                                    sigmask, sigsetsize);
+}
+
 #endif
 
 static int __init eventpoll_init(void)