Commit 7cdf7c20 authored by Willem de Bruijn's avatar Willem de Bruijn Committed by Linus Torvalds

epoll: convert internal api to timespec64

Patch series "add epoll_pwait2 syscall", v4.

Enable nanosecond timeouts for epoll.

Analogous to pselect and ppoll, introduce an epoll_wait syscall
variant that takes a struct timespec instead of int timeout.

This patch (of 4):

Make epoll more consistent with select/poll: pass along the timeout as
timespec64 pointer.

In anticipation of additional changes affecting all three polling
mechanisms:

- add epoll_pwait2 syscall with timespec semantics,
  and share poll_select_set_timeout implementation.
- compute slack before conversion to absolute time,
  to save one ktime_get_ts64 call.

Link: https://lkml.kernel.org/r/20201121144401.3727659-1-willemdebruijn.kernel@gmail.com
Link: https://lkml.kernel.org/r/20201121144401.3727659-2-willemdebruijn.kernel@gmail.comSigned-off-by: default avatarWillem de Bruijn <willemb@google.com>
Cc: Al Viro <viro@zeniv.linux.org.uk>
Cc: Matthew Wilcox (Oracle) <willy@infradead.org>
Cc: Arnd Bergmann <arnd@arndb.de>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent e59d3c64
...@@ -1712,15 +1712,25 @@ static int ep_send_events(struct eventpoll *ep, ...@@ -1712,15 +1712,25 @@ static int ep_send_events(struct eventpoll *ep,
return res; return res;
} }
static inline struct timespec64 ep_set_mstimeout(long ms) static struct timespec64 *ep_timeout_to_timespec(struct timespec64 *to, long ms)
{ {
struct timespec64 now, ts = { struct timespec64 now;
.tv_sec = ms / MSEC_PER_SEC,
.tv_nsec = NSEC_PER_MSEC * (ms % MSEC_PER_SEC), if (ms < 0)
}; return NULL;
if (!ms) {
to->tv_sec = 0;
to->tv_nsec = 0;
return to;
}
to->tv_sec = ms / MSEC_PER_SEC;
to->tv_nsec = NSEC_PER_MSEC * (ms % MSEC_PER_SEC);
ktime_get_ts64(&now); ktime_get_ts64(&now);
return timespec64_add_safe(now, ts); *to = timespec64_add_safe(now, *to);
return to;
} }
/** /**
...@@ -1732,8 +1742,8 @@ static inline struct timespec64 ep_set_mstimeout(long ms) ...@@ -1732,8 +1742,8 @@ static inline struct timespec64 ep_set_mstimeout(long ms)
* stored. * stored.
* @maxevents: Size (in terms of number of events) of the caller event buffer. * @maxevents: Size (in terms of number of events) of the caller event buffer.
* @timeout: Maximum timeout for the ready events fetch operation, in * @timeout: Maximum timeout for the ready events fetch operation, in
* milliseconds. If the @timeout is zero, the function will not block, * timespec. If the timeout is zero, the function will not block,
* while if the @timeout is less than zero, the function will block * while if the @timeout ptr is NULL, the function will block
* until at least one event has been retrieved (or an error * until at least one event has been retrieved (or an error
* occurred). * occurred).
* *
...@@ -1741,7 +1751,7 @@ static inline struct timespec64 ep_set_mstimeout(long ms) ...@@ -1741,7 +1751,7 @@ static inline struct timespec64 ep_set_mstimeout(long ms)
* error code, in case of error. * error code, in case of error.
*/ */
static int ep_poll(struct eventpoll *ep, struct epoll_event __user *events, static int ep_poll(struct eventpoll *ep, struct epoll_event __user *events,
int maxevents, long timeout) int maxevents, struct timespec64 *timeout)
{ {
int res, eavail, timed_out = 0; int res, eavail, timed_out = 0;
u64 slack = 0; u64 slack = 0;
...@@ -1750,13 +1760,11 @@ static int ep_poll(struct eventpoll *ep, struct epoll_event __user *events, ...@@ -1750,13 +1760,11 @@ static int ep_poll(struct eventpoll *ep, struct epoll_event __user *events,
lockdep_assert_irqs_enabled(); lockdep_assert_irqs_enabled();
if (timeout > 0) { if (timeout && (timeout->tv_sec | timeout->tv_nsec)) {
struct timespec64 end_time = ep_set_mstimeout(timeout); slack = select_estimate_accuracy(timeout);
slack = select_estimate_accuracy(&end_time);
to = &expires; to = &expires;
*to = timespec64_to_ktime(end_time); *to = timespec64_to_ktime(*timeout);
} else if (timeout == 0) { } else if (timeout) {
/* /*
* Avoid the unnecessary trip to the wait queue loop, if the * Avoid the unnecessary trip to the wait queue loop, if the
* caller specified a non blocking operation. * caller specified a non blocking operation.
...@@ -2175,7 +2183,7 @@ SYSCALL_DEFINE4(epoll_ctl, int, epfd, int, op, int, fd, ...@@ -2175,7 +2183,7 @@ SYSCALL_DEFINE4(epoll_ctl, int, epfd, int, op, int, fd,
* part of the user space epoll_wait(2). * part of the user space epoll_wait(2).
*/ */
static int do_epoll_wait(int epfd, struct epoll_event __user *events, static int do_epoll_wait(int epfd, struct epoll_event __user *events,
int maxevents, int timeout) int maxevents, struct timespec64 *to)
{ {
int error; int error;
struct fd f; struct fd f;
...@@ -2209,7 +2217,7 @@ static int do_epoll_wait(int epfd, struct epoll_event __user *events, ...@@ -2209,7 +2217,7 @@ static int do_epoll_wait(int epfd, struct epoll_event __user *events,
ep = f.file->private_data; ep = f.file->private_data;
/* Time to fish for events ... */ /* Time to fish for events ... */
error = ep_poll(ep, events, maxevents, timeout); error = ep_poll(ep, events, maxevents, to);
error_fput: error_fput:
fdput(f); fdput(f);
...@@ -2219,7 +2227,10 @@ static int do_epoll_wait(int epfd, struct epoll_event __user *events, ...@@ -2219,7 +2227,10 @@ static int do_epoll_wait(int epfd, struct epoll_event __user *events,
SYSCALL_DEFINE4(epoll_wait, int, epfd, struct epoll_event __user *, events, SYSCALL_DEFINE4(epoll_wait, int, epfd, struct epoll_event __user *, events,
int, maxevents, int, timeout) int, maxevents, int, timeout)
{ {
return do_epoll_wait(epfd, events, maxevents, timeout); struct timespec64 to;
return do_epoll_wait(epfd, events, maxevents,
ep_timeout_to_timespec(&to, timeout));
} }
/* /*
...@@ -2230,6 +2241,7 @@ SYSCALL_DEFINE6(epoll_pwait, int, epfd, struct epoll_event __user *, events, ...@@ -2230,6 +2241,7 @@ SYSCALL_DEFINE6(epoll_pwait, int, epfd, struct epoll_event __user *, events,
int, maxevents, int, timeout, const sigset_t __user *, sigmask, int, maxevents, int, timeout, const sigset_t __user *, sigmask,
size_t, sigsetsize) size_t, sigsetsize)
{ {
struct timespec64 to;
int error; int error;
/* /*
...@@ -2240,7 +2252,9 @@ SYSCALL_DEFINE6(epoll_pwait, int, epfd, struct epoll_event __user *, events, ...@@ -2240,7 +2252,9 @@ SYSCALL_DEFINE6(epoll_pwait, int, epfd, struct epoll_event __user *, events,
if (error) if (error)
return error; return error;
error = do_epoll_wait(epfd, events, maxevents, timeout); error = do_epoll_wait(epfd, events, maxevents,
ep_timeout_to_timespec(&to, timeout));
restore_saved_sigmask_unless(error == -EINTR); restore_saved_sigmask_unless(error == -EINTR);
return error; return error;
...@@ -2253,6 +2267,7 @@ COMPAT_SYSCALL_DEFINE6(epoll_pwait, int, epfd, ...@@ -2253,6 +2267,7 @@ COMPAT_SYSCALL_DEFINE6(epoll_pwait, int, epfd,
const compat_sigset_t __user *, sigmask, const compat_sigset_t __user *, sigmask,
compat_size_t, sigsetsize) compat_size_t, sigsetsize)
{ {
struct timespec64 to;
long err; long err;
/* /*
...@@ -2263,7 +2278,9 @@ COMPAT_SYSCALL_DEFINE6(epoll_pwait, int, epfd, ...@@ -2263,7 +2278,9 @@ COMPAT_SYSCALL_DEFINE6(epoll_pwait, int, epfd,
if (err) if (err)
return err; return err;
err = do_epoll_wait(epfd, events, maxevents, timeout); err = do_epoll_wait(epfd, events, maxevents,
ep_timeout_to_timespec(&to, timeout));
restore_saved_sigmask_unless(err == -EINTR); restore_saved_sigmask_unless(err == -EINTR);
return err; return err;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment