kernel - Implement ppoll system call with precise microseconds timeout.
authorImre Vadasz <imre@vdsz.com>
Sat, 12 Dec 2015 20:26:09 +0000 (21:26 +0100)
committerImre Vadász <imre@vdsz.com>
Sun, 19 Jun 2016 07:49:47 +0000 (09:49 +0200)
* Implement a maximum timeout of 2000s, because systimer(9) just accepts an
  int timeout in microseconds.

* Add kern.kv_sleep_threshold sysctl variable for tuning the threshold for
  the ppoll sleep duration (in nanoseconds), below which we will
  busy-loop with DELAY instead of using tsleep for waiting.

20 files changed:
lib/libc/include/namespace.h
lib/libc/include/un-namespace.h
lib/libc/sys/Makefile.inc
lib/libc/sys/Symbol.map
lib/libc/sys/poll.2
lib/libthread_xu/pthread.map
lib/libthread_xu/thread/thr_private.h
lib/libthread_xu/thread/thr_syscalls.c
sys/emulation/linux/linux_epoll.c
sys/kern/init_sysent.c
sys/kern/kern_event.c
sys/kern/sys_generic.c
sys/kern/syscalls.c
sys/kern/syscalls.master
sys/sys/event.h
sys/sys/poll.h
sys/sys/syscall.h
sys/sys/syscall.mk
sys/sys/sysproto.h
sys/sys/sysunion.h

index e4d7c1f..680e7f2 100644 (file)
@@ -74,6 +74,7 @@
 #define                nanosleep                       _nanosleep
 #define                open                            _open
 #define                poll                            _poll
+#define                ppoll                           _ppoll
 #define                pthread_atfork                  _pthread_atfork
 #define                pthread_attr_destroy            _pthread_attr_destroy
 #define                pthread_attr_get_np             _pthread_attr_get_np
index 825e929..04f0a3c 100644 (file)
@@ -55,6 +55,7 @@
 #undef         nanosleep
 #undef         open
 #undef         poll
+#undef         ppoll
 #undef         pthread_atfork
 #undef         pthread_attr_destroy
 #undef         pthread_attr_get_np
index e960ab7..73a6aae 100644 (file)
@@ -156,6 +156,7 @@ MLINKS+=msgsnd.2 msgsnd.3
 MLINKS+=open.2 openat.2
 MLINKS+=pathconf.2 fpathconf.2 pathconf.2 lpathconf.2
 MLINKS+=pipe.2 pipe2.2
+MLINKS+=poll.2 ppoll.2
 MLINKS+=read.2 pread.2 read.2 preadv.2 read.2 readv.2
 MLINKS+=readlink.2 readlinkat.2
 MLINKS+=recv.2 recvfrom.2 recv.2 recvmsg.2
index d17758a..a3906b8 100644 (file)
@@ -305,6 +305,7 @@ DF404.0 {
 
 DF406.0 {
     kenv;
+    ppoll;
 };
 
 DFprivate_1.0 {
@@ -497,6 +498,7 @@ DFprivate_1.0 {
     __sys_pipe;
     __sys_pipe2;
     __sys_poll;
+    __sys_ppoll;
     __sys_procctl;
     __sys_profil;
     __sys_pselect;
@@ -791,6 +793,7 @@ DFprivate_1.0 {
     _pipe;
     _pipe2;
     _poll;
+    _ppoll;
     _procctl;
     _profil;
     _pselect;
index 1ea1fc3..ec38dcd 100644 (file)
 .In sys/types.h
 .In poll.h
 .Ft int
-.Fn poll "struct pollfd *fds" "nfds_t nfds" "int timeout"
+.Fo poll
+.Fa "struct pollfd *fds"
+.Fa "nfds_t nfds"
+.Fa "int timeout"
+.Fc
+.Ft int
+.Fo ppoll
+.Fa "struct pollfd *fds"
+.Fa "nfds_t nfds"
+.Fa "const struct timespec *timeout"
+.Fa "const sigset_t *newsigmask"
+.Fc
 .Sh DESCRIPTION
 .Fn Poll
-examines a set of file descriptors to see if some of them are ready for
+and
+.Fn ppoll
+examine a set of file descriptors to see if some of them are ready for
 I/O.
 The
 .Fa fds
index b89954f..ae622cf 100644 (file)
@@ -12,6 +12,7 @@ global:
        __nanosleep;
        __open;
        __poll;
+       __ppoll;
        __pthread_cond_timedwait;
        __pthread_cond_wait;
        __pthread_cxa_finalize;
@@ -377,3 +378,8 @@ global:
 local:
        *;
 };
+
+LIBTHREAD_1_1 {
+global:
+       ppoll;
+};
index f5c6b66..1094a06 100644 (file)
@@ -770,6 +770,8 @@ int __sys_sigwaitinfo(const sigset_t *set, siginfo_t *info);
 /* #include <poll.h> */
 #ifdef _SYS_POLL_H_
 int    __sys_poll(struct pollfd *, unsigned, int);
+int    __sys_ppoll(struct pollfd *, unsigned, const struct timespec *,
+               const sigset_t *);
 #endif
 
 /* #include <sys/mman.h> */
index 66f286e..d537ddf 100644 (file)
@@ -110,6 +110,8 @@ extern int  __sys_connect(int, const struct sockaddr *, socklen_t);
 extern int     __sys_fsync(int);
 extern int     __sys_msync(void *, size_t, int);
 extern int     __sys_poll(struct pollfd *, unsigned, int);
+extern int     __sys_ppoll(struct pollfd *, unsigned, const struct timespec *,
+                       const sigset_t *);
 extern ssize_t __sys_recv(int, void *, size_t, int);
 extern ssize_t __sys_recvfrom(int, void *, size_t, int, struct sockaddr *, socklen_t *);
 extern ssize_t __sys_recvmsg(int, struct msghdr *, int);
@@ -134,6 +136,8 @@ int __nanosleep(const struct timespec *, struct timespec *);
 int    __open(const char *, int,...);
 int    __openat(int fd, const char *, int,...);
 int    __poll(struct pollfd *, unsigned int, int);
+int    __ppoll(struct pollfd *, unsigned int, const struct timespec *,
+               const sigset_t *);
 ssize_t        __read(int, void *buf, size_t);
 ssize_t        __readv(int, const struct iovec *, int);
 ssize_t        __recvfrom(int, void *, size_t, int f, struct sockaddr *, socklen_t *);
@@ -412,6 +416,23 @@ __poll(struct pollfd *fds, unsigned int nfds, int timeout)
 
 __strong_reference(__poll, poll);
 
+int
+__ppoll(struct pollfd *fds, unsigned int nfds, const struct timespec *ts,
+       const sigset_t *mask)
+{
+       struct pthread *curthread = tls_get_curthread();
+       int oldcancel;
+       int ret;
+
+       oldcancel = _thr_cancel_enter(curthread);
+       ret = __sys_ppoll(fds, nfds, ts, mask);
+       _thr_cancel_leave(curthread, oldcancel);
+
+       return ret;
+}
+
+__strong_reference(__ppoll, ppoll);
+
 int 
 __pselect(int count, fd_set *rfds, fd_set *wfds, fd_set *efds,
        const struct timespec *timo, const sigset_t *mask)
index 9fa47c1..767a878 100644 (file)
@@ -223,7 +223,7 @@ sys_linux_epoll_ctl(struct linux_epoll_ctl_args *args)
        kq = (struct kqueue *)fp->f_data;
 
        error = kern_kevent(kq, 0, &k_args.sysmsg_result, &k_args,
-           linux_kev_copyin, linux_kev_copyout, NULL);
+           linux_kev_copyin, linux_kev_copyout, NULL, 0);
         /* Check if there was an error during registration. */
         if (error == 0 && k_args.sysmsg_result != 0) {
                 /* The copyout callback stored the error there. */
@@ -274,7 +274,7 @@ sys_linux_epoll_wait(struct linux_epoll_wait_args *args)
        kq = (struct kqueue *)fp->f_data;
 
        error = kern_kevent(kq, args->maxevents, &args->sysmsg_result,
-           &k_args, linux_kev_copyin, linux_kev_copyout, &ts);
+           &k_args, linux_kev_copyin, linux_kev_copyout, &ts, 0);
 
        fdrop(fp);
         /* translation? */
index d1fb862..4519a12 100644 (file)
@@ -568,4 +568,5 @@ struct sysent sysent[] = {
        { AS(futimens_args), (sy_call_t *)sys_futimens },       /* 540 = futimens */
        { AS(accept4_args), (sy_call_t *)sys_accept4 }, /* 541 = accept4 */
        { AS(lwp_setname_args), (sy_call_t *)sys_lwp_setname }, /* 542 = lwp_setname */
+       { AS(ppoll_args), (sy_call_t *)sys_ppoll },     /* 543 = ppoll */
 };
index cf9f759..33e34c6 100644 (file)
@@ -108,6 +108,11 @@ static void        knote_dequeue(struct knote *kn);
 static struct  knote *knote_alloc(void);
 static void    knote_free(struct knote *kn);
 
+static void    precise_sleep_intr(systimer_t info, int in_ipi,
+                                  struct intrframe *frame);
+static int     precise_sleep(void *ident, int flags, const char *wmesg,
+                             int us);
+
 static void    filt_kqdetach(struct knote *kn);
 static int     filt_kqueue(struct knote *kn, long hint);
 static int     filt_procattach(struct knote *kn);
@@ -147,6 +152,9 @@ SYSCTL_INT(_kern, OID_AUTO, kq_calloutmax, CTLFLAG_RW,
 static int             kq_checkloop = 1000000;
 SYSCTL_INT(_kern, OID_AUTO, kq_checkloop, CTLFLAG_RW,
     &kq_checkloop, 0, "Maximum number of loops for kqueue scan");
+static int             kq_sleep_threshold = 20000;
+SYSCTL_INT(_kern, OID_AUTO, kq_sleep_threshold, CTLFLAG_RW,
+    &kq_sleep_threshold, 0, "Minimum sleep duration without busy-looping");
 
 #define KNOTE_ACTIVATE(kn) do {                                        \
        kn->kn_status |= KN_ACTIVE;                                     \
@@ -748,7 +756,7 @@ kevent_copyin(void *arg, struct kevent *kevp, int max, int *events)
 int
 kern_kevent(struct kqueue *kq, int nevents, int *res, void *uap,
            k_copyin_fn kevent_copyinfn, k_copyout_fn kevent_copyoutfn,
-           struct timespec *tsp_in)
+           struct timespec *tsp_in, int flags)
 {
        struct kevent *kevp;
        struct timespec *tsp, ats;
@@ -851,7 +859,7 @@ kern_kevent(struct kqueue *kq, int nevents, int *res, void *uap,
                 * to our scan.
                 */
                if (kq->kq_count == 0 && *res == 0) {
-                       int timeout;
+                       int timeout, ustimeout = 0;
 
                        if (tsp == NULL) {
                                timeout = 0;
@@ -871,6 +879,20 @@ kern_kevent(struct kqueue *kq, int nevents, int *res, void *uap,
                                            24 * 60 * 60 * hz :
                                            tstohz_high(&atx);
                                }
+                               if (flags & KEVENT_TIMEOUT_PRECISE &&
+                                   timeout != 0) {
+                                       if (atx.tv_sec == 0 &&
+                                           atx.tv_nsec < kq_sleep_threshold) {
+                                               DELAY(atx.tv_nsec / 1000);
+                                               error = EWOULDBLOCK;
+                                               break;
+                                       } else if (atx.tv_sec < 2000) {
+                                               ustimeout = atx.tv_sec *
+                                                   1000000 + atx.tv_nsec/1000;
+                                       } else {
+                                               ustimeout = 2000000000;
+                                       }
+                               }
                        }
 
                        lwkt_gettoken(tok);
@@ -884,7 +906,14 @@ kern_kevent(struct kqueue *kq, int nevents, int *res, void *uap,
                                         */
                                        kq->kq_sleep_cnt = 2;
                                }
-                               error = tsleep(kq, PCATCH, "kqread", timeout);
+                               if ((flags & KEVENT_TIMEOUT_PRECISE) &&
+                                   timeout != 0) {
+                                       error = precise_sleep(kq, PCATCH,
+                                           "kqread", ustimeout);
+                               } else {
+                                       error = tsleep(kq, PCATCH, "kqread",
+                                           timeout);
+                               }
 
                                /* don't restart after signals... */
                                if (error == ERESTART)
@@ -992,7 +1021,7 @@ sys_kevent(struct kevent_args *uap)
        kap->pchanges = 0;
 
        error = kern_kevent(kq, uap->nevents, &uap->sysmsg_result, kap,
-                           kevent_copyin, kevent_copyout, tsp);
+                           kevent_copyin, kevent_copyout, tsp, 0);
 
        fdrop(fp);
 
@@ -1845,3 +1874,39 @@ knote_free(struct knote *kn)
        }
        kfree(kn, M_KQUEUE);
 }
+
+struct sleepinfo {
+       void *ident;
+       int timedout;
+};
+
+static void
+precise_sleep_intr(systimer_t info, int in_ipi, struct intrframe *frame)
+{
+       struct sleepinfo *si;
+
+       si = info->data;
+       si->timedout = 1;
+       wakeup(si->ident);
+}
+
+static int
+precise_sleep(void *ident, int flags, const char *wmesg, int us)
+{
+       struct systimer info;
+       struct sleepinfo si = {
+               .ident = ident,
+               .timedout = 0,
+       };
+       int r;
+
+       tsleep_interlock(ident, flags);
+       systimer_init_oneshot(&info, precise_sleep_intr, &si,
+           us == 0 ? 1 : us);
+       r = tsleep(ident, flags | PINTERLOCKED, wmesg, 0);
+       systimer_del(&info);
+       if (si.timedout)
+               r = EWOULDBLOCK;
+
+       return r;
+}
index 65097f0..f71229a 100644 (file)
@@ -108,7 +108,7 @@ static struct lwkt_token mioctl_token = LWKT_TOKEN_INITIALIZER(mioctl_token);
 static int     doselect(int nd, fd_set *in, fd_set *ou, fd_set *ex,
                         struct timespec *ts, int *res);
 static int     dopoll(int nfds, struct pollfd *fds, struct timespec *ts,
-                      int *res);
+                      int *res, int flags);
 static int     dofileread(int, struct file *, struct uio *, int, size_t *);
 static int     dofilewrite(int, struct file *, struct uio *, int, size_t *);
 
@@ -1180,7 +1180,7 @@ doselect(int nd, fd_set *read, fd_set *write, fd_set *except,
         *       loaded in.
         */
        error = kern_kevent(&kap->lwp->lwp_kqueue, 0x7FFFFFFF, res, kap,
-                           select_copyin, select_copyout, ts);
+                           select_copyin, select_copyout, ts, 0);
        if (error == 0)
                error = putbits(bytes, kap->read_set, read);
        if (error == 0)
@@ -1232,7 +1232,74 @@ sys_poll(struct poll_args *uap)
                tsp = NULL;
        }
 
-       error = dopoll(uap->nfds, uap->fds, tsp, &uap->sysmsg_result);
+       error = dopoll(uap->nfds, uap->fds, tsp, &uap->sysmsg_result, 0);
+
+       return (error);
+}
+
+/*
+ * Ppoll system call.
+ *
+ * MPSAFE
+ */
+int
+sys_ppoll(struct ppoll_args *uap)
+{
+       struct thread *td = curthread;
+       struct lwp *lp = td->td_lwp;
+       struct timespec *ktsp, kts;
+       sigset_t sigmask;
+       int error;
+
+       /*
+        * Get timeout if any.
+        */
+       if (uap->ts != NULL) {
+               error = copyin(uap->ts, &kts, sizeof (kts));
+               if (error)
+                       return (error);
+               ktsp = &kts;
+       } else {
+               ktsp = NULL;
+       }
+
+       /*
+        * Install temporary signal mask if any provided.
+        */
+       if (uap->sigmask != NULL) {
+               error = copyin(uap->sigmask, &sigmask, sizeof(sigmask));
+               if (error)
+                       return (error);
+               lwkt_gettoken(&lp->lwp_proc->p_token);
+               lp->lwp_oldsigmask = lp->lwp_sigmask;
+               SIG_CANTMASK(sigmask);
+               lp->lwp_sigmask = sigmask;
+               lwkt_reltoken(&lp->lwp_proc->p_token);
+       }
+
+       error = dopoll(uap->nfds, uap->fds, ktsp, &uap->sysmsg_result,
+           ktsp != NULL ? KEVENT_TIMEOUT_PRECISE : 0);
+
+       if (uap->sigmask != NULL) {
+               lwkt_gettoken(&lp->lwp_proc->p_token);
+               /* dopoll() responsible for turning ERESTART into EINTR */
+               KKASSERT(error != ERESTART);
+               if (error == EINTR) {
+                       /*
+                        * We can't restore the previous signal mask now
+                        * because it could block the signal that interrupted
+                        * us.  So make a note to restore it after executing
+                        * the handler.
+                        */
+                       lp->lwp_flags |= LWP_OLDMASK;
+               } else {
+                       /*
+                        * No handler to run. Restore previous mask immediately.
+                        */
+                       lp->lwp_sigmask = lp->lwp_oldsigmask;
+               }
+               lwkt_reltoken(&lp->lwp_proc->p_token);
+       }
 
        return (error);
 }
@@ -1461,7 +1528,7 @@ poll_copyout(void *arg, struct kevent *kevp, int count, int *res)
 }
 
 static int
-dopoll(int nfds, struct pollfd *fds, struct timespec *ts, int *res)
+dopoll(int nfds, struct pollfd *fds, struct timespec *ts, int *res, int flags)
 {
        struct poll_kevent_copyin_args ka;
        struct pollfd sfds[64];
@@ -1495,7 +1562,7 @@ dopoll(int nfds, struct pollfd *fds, struct timespec *ts, int *res)
        error = copyin(fds, ka.fds, bytes);
        if (error == 0)
                error = kern_kevent(&ka.lwp->lwp_kqueue, 0x7FFFFFFF, res, &ka,
-                                   poll_copyin, poll_copyout, ts);
+                                   poll_copyin, poll_copyout, ts, flags);
 
        if (error == 0)
                error = copyout(ka.fds, fds, bytes);
@@ -1553,7 +1620,7 @@ socket_wait(struct socket *so, struct timespec *ts, int *res)
        }
 
        error = kern_kevent(&kq, 1, res, NULL, socket_wait_copyin,
-                           socket_wait_copyout, ts);
+                           socket_wait_copyout, ts, 0);
 
        EV_SET(&kev, fd, EVFILT_READ, EV_DELETE, 0, 0, NULL);
        kqueue_register(&kq, &kev);
index fa73da9..6837ea7 100644 (file)
@@ -551,4 +551,5 @@ const char *syscallnames[] = {
        "futimens",                     /* 540 = futimens */
        "accept4",                      /* 541 = accept4 */
        "lwp_setname",                  /* 542 = lwp_setname */
+       "ppoll",                        /* 543 = ppoll */
 };
index f5008c1..b01fe60 100644 (file)
 540    STD     { int futimens(int fd, const struct timespec *ts); }
 541    STD     { int accept4(int s, caddr_t name, int *anamelen, int flags); }
 542    STD     { int lwp_setname(lwpid_t tid, const char *name); }
+543    STD     { int poll(struct pollfd *fds, u_int nfds, \
+                          const struct timespec *ts, const sigset_t *sigmask); }
index 331a8b5..501eb6f 100644 (file)
@@ -235,13 +235,15 @@ struct thread;
 struct filedesc;
 struct kevent_args;
 
+#define KEVENT_TIMEOUT_PRECISE 0x01
+
 typedef int    (*k_copyout_fn)(void *arg, struct kevent *kevp, int count,
     int *res);
 typedef int    (*k_copyin_fn)(void *arg, struct kevent *kevp, int max,
     int *events);
 int kern_kevent(struct kqueue *kq, int nevents, int *res, void *uap,
     k_copyin_fn kevent_copyin, k_copyout_fn kevent_copyout,
-    struct timespec *tsp);
+    struct timespec *tsp, int flags);
 
 extern void    knote(struct klist *list, long hint);
 extern void    knote_insert(struct klist *klist, struct knote *kn);
index a651823..fc68d02 100644 (file)
@@ -86,6 +86,13 @@ struct pollfd {
 
 #include <sys/cdefs.h>
 
+#ifndef _SYS_SIGNAL_H_
+#include <sys/signal.h>
+#endif
+#ifndef _SYS_TIME_H_
+#include <sys/time.h>
+#endif
+
 __BEGIN_DECLS
 /*
  * XXX logically, poll() should be declared in <poll.h>, but SVR4 at
@@ -94,6 +101,10 @@ __BEGIN_DECLS
 #if __POSIX_VISIBLE >= 200809 || __XSI_VISIBLE
 int    poll (struct pollfd *, nfds_t, int);
 #endif
+#if __BSD_VISIBLE
+int    ppoll (struct pollfd *, nfds_t, const struct timespec *,
+              const sigset_t *);
+#endif
 __END_DECLS
 
 #endif /* !_KERNEL */
index 0b445ed..6260b76 100644 (file)
 #define        SYS_futimens    540
 #define        SYS_accept4     541
 #define        SYS_lwp_setname 542
-#define        SYS_MAXSYSCALL  543
+#define        SYS_ppoll       543
+#define        SYS_MAXSYSCALL  544
index ffd7aba..80c28f6 100644 (file)
@@ -306,4 +306,5 @@ MIASM =  \
        utimensat.o \
        futimens.o \
        accept4.o \
-       lwp_setname.o
+       lwp_setname.o \
+       ppoll.o
index 9df6abe..bda2af3 100644 (file)
@@ -2324,6 +2324,15 @@ struct   lwp_setname_args {
        lwpid_t tid;    char tid_[PAD_(lwpid_t)];
        const char *    name;   char name_[PAD_(const char *)];
 };
+struct ppoll_args {
+#ifdef _KERNEL
+       struct sysmsg sysmsg;
+#endif
+       struct pollfd * fds;    char fds_[PAD_(struct pollfd *)];
+       u_int   nfds;   char nfds_[PAD_(u_int)];
+       const struct timespec * ts;     char ts_[PAD_(const struct timespec *)];
+       const sigset_t *        sigmask;        char sigmask_[PAD_(const sigset_t *)];
+};
 
 #ifdef COMPAT_43
 
@@ -2873,6 +2882,7 @@ int       sys_utimensat (struct utimensat_args *);
 int    sys_futimens (struct futimens_args *);
 int    sys_accept4 (struct accept4_args *);
 int    sys_lwp_setname (struct lwp_setname_args *);
+int    sys_ppoll (struct ppoll_args *);
 
 #endif /* !_SYS_SYSPROTO_H_ */
 #undef PAD_
index 5d14327..56035a6 100644 (file)
@@ -396,4 +396,5 @@ union sysunion {
        struct  futimens_args futimens;
        struct  accept4_args accept4;
        struct  lwp_setname_args lwp_setname;
+       struct  ppoll_args ppoll;
 };