epoll: add syscall epoll_pwait2
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>
This commit is contained in:
parent
7cdf7c20e9
commit
58169a52eb
|
@ -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
|
* Implement the event wait interface for the eventpoll file. It is the kernel
|
||||||
* part of the user space epoll_pwait(2).
|
* part of the user space epoll_pwait(2).
|
||||||
*/
|
*/
|
||||||
SYSCALL_DEFINE6(epoll_pwait, int, epfd, struct epoll_event __user *, events,
|
static int do_epoll_pwait(int epfd, struct epoll_event __user *events,
|
||||||
int, maxevents, int, timeout, const sigset_t __user *, sigmask,
|
int maxevents, struct timespec64 *to,
|
||||||
size_t, sigsetsize)
|
const sigset_t __user *sigmask, size_t sigsetsize)
|
||||||
{
|
{
|
||||||
struct timespec64 to;
|
|
||||||
int error;
|
int error;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -2252,22 +2251,48 @@ 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,
|
error = do_epoll_wait(epfd, events, maxevents, to);
|
||||||
ep_timeout_to_timespec(&to, timeout));
|
|
||||||
|
|
||||||
restore_saved_sigmask_unless(error == -EINTR);
|
restore_saved_sigmask_unless(error == -EINTR);
|
||||||
|
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_COMPAT
|
SYSCALL_DEFINE6(epoll_pwait, int, epfd, struct epoll_event __user *, events,
|
||||||
COMPAT_SYSCALL_DEFINE6(epoll_pwait, int, epfd,
|
int, maxevents, int, timeout, const sigset_t __user *, sigmask,
|
||||||
struct epoll_event __user *, events,
|
size_t, sigsetsize)
|
||||||
int, maxevents, int, timeout,
|
|
||||||
const compat_sigset_t __user *, sigmask,
|
|
||||||
compat_size_t, sigsetsize)
|
|
||||||
{
|
{
|
||||||
struct timespec64 to;
|
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;
|
long err;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -2278,13 +2303,47 @@ COMPAT_SYSCALL_DEFINE6(epoll_pwait, int, epfd,
|
||||||
if (err)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
err = do_epoll_wait(epfd, events, maxevents,
|
err = do_epoll_wait(epfd, events, maxevents, timeout);
|
||||||
ep_timeout_to_timespec(&to, timeout));
|
|
||||||
|
|
||||||
restore_saved_sigmask_unless(err == -EINTR);
|
restore_saved_sigmask_unless(err == -EINTR);
|
||||||
|
|
||||||
return err;
|
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
|
#endif
|
||||||
|
|
||||||
static int __init eventpoll_init(void)
|
static int __init eventpoll_init(void)
|
||||||
|
|
Loading…
Reference in New Issue