In preparation of epoll_pwait1, this allows sharing code with coming new syscall. The new functions use timespec for timeout. Signed-off-by: Fam Zheng <famz@xxxxxxxxxx> --- fs/eventpoll.c | 136 +++++++++++++++++++++++++++++---------------------------- 1 file changed, 70 insertions(+), 66 deletions(-) diff --git a/fs/eventpoll.c b/fs/eventpoll.c index d77f944..117ba72 100644 --- a/fs/eventpoll.c +++ b/fs/eventpoll.c @@ -1554,15 +1554,12 @@ 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 timespec ep_set_mstimeout(const struct timespec *ts) { - struct timespec now, ts = { - .tv_sec = ms / MSEC_PER_SEC, - .tv_nsec = NSEC_PER_MSEC * (ms % MSEC_PER_SEC), - }; + struct timespec now; ktime_get_ts(&now); - return timespec_add_safe(now, ts); + return timespec_add_safe(now, *ts); } /** @@ -1573,17 +1570,16 @@ static inline struct timespec ep_set_mstimeout(long ms) * @events: Pointer to the userspace buffer where the ready events should be * stored. * @maxevents: Size (in terms of number of events) of the caller event buffer. - * @timeout: Maximum timeout for the ready events fetch operation, in - * milliseconds. If the @timeout is zero, the function will not block, - * while if the @timeout is less than zero, the function will block - * until at least one event has been retrieved (or an error - * occurred). + * @timeout: Maximum timeout for the ready events fetch operation. If NULL, or + * if both tv_sec and tv_nsec are zero, the function will not block. + * If either one is less than zero, the function will block until at + * least one event has been retrieved (or an error occurred). * * Returns: Returns the number of ready events which have been fetched, or an * error code, in case of error. */ static int ep_poll(struct eventpoll *ep, struct epoll_event __user *events, - int maxevents, long timeout) + int maxevents, const struct timespec *timeout) { int res = 0, eavail, timed_out = 0; unsigned long flags; @@ -1591,13 +1587,7 @@ static int ep_poll(struct eventpoll *ep, struct epoll_event __user *events, wait_queue_t wait; ktime_t expires, *to = NULL; - if (timeout > 0) { - struct timespec end_time = ep_set_mstimeout(timeout); - - slack = select_estimate_accuracy(&end_time); - to = &expires; - *to = timespec_to_ktime(end_time); - } else if (timeout == 0) { + if (!timeout || (timeout->tv_nsec == 0 && timeout->tv_sec == 0)) { /* * Avoid the unnecessary trip to the wait queue loop, if the * caller specified a non blocking operation. @@ -1605,6 +1595,12 @@ static int ep_poll(struct eventpoll *ep, struct epoll_event __user *events, timed_out = 1; spin_lock_irqsave(&ep->lock, flags); goto check_events; + } else if (timeout->tv_nsec >= 0 && timeout->tv_sec >= 0) { + struct timespec end_time = ep_set_mstimeout(timeout); + + slack = select_estimate_accuracy(&end_time); + to = &expires; + *to = timespec_to_ktime(end_time); } fetch_events: @@ -1954,12 +1950,8 @@ error_return: return error; } -/* - * Implement the event wait interface for the eventpoll file. It is the kernel - * part of the user space epoll_wait(2). - */ -SYSCALL_DEFINE4(epoll_wait, int, epfd, struct epoll_event __user *, events, - int, maxevents, int, timeout) +static inline int epoll_wait_do(int epfd, struct epoll_event __user *events, + int maxevents, const struct timespec *timeout) { int error; struct fd f; @@ -2002,29 +1994,35 @@ error_fput: /* * 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_wait(2). */ -SYSCALL_DEFINE6(epoll_pwait, int, epfd, struct epoll_event __user *, events, - int, maxevents, int, timeout, const sigset_t __user *, sigmask, - size_t, sigsetsize) +SYSCALL_DEFINE4(epoll_wait, int, epfd, struct epoll_event __user *, events, + int, maxevents, int, timeout) +{ + struct timespec ts = (struct timespec) { + .tv_sec = timeout / MSEC_PER_SEC, + .tv_nsec = (timeout % MSEC_PER_SEC) * NSEC_PER_MSEC, + }; + return epoll_wait_do(epfd, events, maxevents, &ts); +} + +static inline int epoll_pwait_do(int epfd, struct epoll_event __user *events, + int maxevents, struct timespec *timeout, + sigset_t *sigmask, size_t sigsetsize) { int error; - sigset_t ksigmask, sigsaved; + sigset_t sigsaved; /* * If the caller wants a certain signal mask to be set during the wait, * we apply it here. */ if (sigmask) { - if (sigsetsize != sizeof(sigset_t)) - return -EINVAL; - if (copy_from_user(&ksigmask, sigmask, sizeof(ksigmask))) - return -EFAULT; sigsaved = current->blocked; - set_current_blocked(&ksigmask); + set_current_blocked(sigmask); } - error = sys_epoll_wait(epfd, events, maxevents, timeout); + error = epoll_wait_do(epfd, events, maxevents, timeout); /* * If we changed the signal mask, we need to restore the original one. @@ -2044,49 +2042,55 @@ SYSCALL_DEFINE6(epoll_pwait, int, epfd, struct epoll_event __user *, events, return error; } +/* + * 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) +{ + struct timespec ts = (struct timespec) { + .tv_sec = timeout / MSEC_PER_SEC, + .tv_nsec = (timeout % MSEC_PER_SEC) * NSEC_PER_MSEC, + }; + sigset_t ksigmask; + + if (sigmask) { + if (sigsetsize != sizeof(sigset_t)) + return -EINVAL; + if (copy_from_user(&ksigmask, sigmask, sizeof(ksigmask))) + return -EFAULT; + } + return epoll_pwait_do(epfd, events, maxevents, &ts, + sigmask ? &ksigmask : NULL, sigsetsize); +} + #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) + struct epoll_event __user *, events, + int, maxevents, int, timeout, + const compat_sigset_t __user *, sigmask, + compat_size_t, sigsetsize) { - long err; compat_sigset_t csigmask; - sigset_t ksigmask, sigsaved; + sigset_t ksigmask; + + struct timespec ts = (struct timespec) { + .tv_sec = timeout / MSEC_PER_SEC, + .tv_nsec = (timeout % MSEC_PER_SEC) * NSEC_PER_MSEC, + }; - /* - * If the caller wants a certain signal mask to be set during the wait, - * we apply it here. - */ if (sigmask) { if (sigsetsize != sizeof(compat_sigset_t)) return -EINVAL; if (copy_from_user(&csigmask, sigmask, sizeof(csigmask))) return -EFAULT; sigset_from_compat(&ksigmask, &csigmask); - sigsaved = current->blocked; - set_current_blocked(&ksigmask); - } - - err = sys_epoll_wait(epfd, events, maxevents, timeout); - - /* - * If we changed the signal mask, we need to restore the original one. - * In case we've got a signal while waiting, we do not restore the - * signal mask yet, and we allow do_signal() to deliver the signal on - * the way back to userspace, before the signal mask is restored. - */ - if (sigmask) { - if (err == -EINTR) { - memcpy(¤t->saved_sigmask, &sigsaved, - sizeof(sigsaved)); - set_restore_sigmask(); - } else - set_current_blocked(&sigsaved); } - return err; + return epoll_pwait_do(epfd, events, maxevents, &ts, + sigmask ? &ksigmask : NULL, sigsetsize); } #endif -- 1.9.3 -- To unsubscribe from this list: send the line "unsubscribe linux-fsdevel" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html