Index: head/sys/compat/cloudabi64/cloudabi64_poll.c =================================================================== --- head/sys/compat/cloudabi64/cloudabi64_poll.c (revision 286655) +++ head/sys/compat/cloudabi64/cloudabi64_poll.c (revision 286656) @@ -1,285 +1,406 @@ /*- * Copyright (c) 2015 Nuxi, https://nuxi.nl/ * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include /* Converts a FreeBSD signal number to a CloudABI signal number. */ static cloudabi_signal_t convert_signal(int sig) { static const cloudabi_signal_t signals[] = { [SIGABRT] = CLOUDABI_SIGABRT, [SIGALRM] = CLOUDABI_SIGALRM, [SIGBUS] = CLOUDABI_SIGBUS, [SIGCHLD] = CLOUDABI_SIGCHLD, [SIGCONT] = CLOUDABI_SIGCONT, [SIGFPE] = CLOUDABI_SIGFPE, [SIGHUP] = CLOUDABI_SIGHUP, [SIGILL] = CLOUDABI_SIGILL, [SIGINT] = CLOUDABI_SIGINT, [SIGKILL] = CLOUDABI_SIGKILL, [SIGPIPE] = CLOUDABI_SIGPIPE, [SIGQUIT] = CLOUDABI_SIGQUIT, [SIGSEGV] = CLOUDABI_SIGSEGV, [SIGSTOP] = CLOUDABI_SIGSTOP, [SIGSYS] = CLOUDABI_SIGSYS, [SIGTERM] = CLOUDABI_SIGTERM, [SIGTRAP] = CLOUDABI_SIGTRAP, [SIGTSTP] = CLOUDABI_SIGTSTP, [SIGTTIN] = CLOUDABI_SIGTTIN, [SIGTTOU] = CLOUDABI_SIGTTOU, [SIGURG] = CLOUDABI_SIGURG, [SIGUSR1] = CLOUDABI_SIGUSR1, [SIGUSR2] = CLOUDABI_SIGUSR2, [SIGVTALRM] = CLOUDABI_SIGVTALRM, [SIGXCPU] = CLOUDABI_SIGXCPU, [SIGXFSZ] = CLOUDABI_SIGXFSZ, }; /* Convert unknown signals to SIGABRT. */ if (sig < 0 || sig >= nitems(signals) || signals[sig] == 0) return (SIGABRT); return (signals[sig]); } struct cloudabi64_kevent_args { const cloudabi64_subscription_t *in; cloudabi64_event_t *out; bool once; }; /* Converts CloudABI's subscription objects to FreeBSD's struct kevent. */ static int cloudabi64_kevent_copyin(void *arg, struct kevent *kevp, int count) { cloudabi64_subscription_t sub; struct cloudabi64_kevent_args *args; cloudabi_timestamp_t ts; int error; args = arg; while (count-- > 0) { /* TODO(ed): Copy in multiple entries at once. */ error = copyin(args->in++, &sub, sizeof(sub)); if (error != 0) return (error); memset(kevp, 0, sizeof(*kevp)); kevp->udata = (void *)sub.userdata; switch (sub.type) { case CLOUDABI_EVENTTYPE_CLOCK: kevp->filter = EVFILT_TIMER; kevp->ident = sub.clock.identifier; kevp->fflags = NOTE_NSECONDS; if ((sub.clock.flags & CLOUDABI_SUBSCRIPTION_CLOCK_ABSTIME) != 0 && sub.clock.timeout > 0) { /* Convert absolute timestamp to a relative. */ error = cloudabi_clock_time_get(curthread, sub.clock.clock_id, &ts); if (error != 0) return (error); ts = ts > sub.clock.timeout ? 0 : sub.clock.timeout - ts; } else { /* Relative timestamp. */ ts = sub.clock.timeout; } kevp->data = ts > INTPTR_MAX ? INTPTR_MAX : ts; break; case CLOUDABI_EVENTTYPE_FD_READ: kevp->filter = EVFILT_READ; kevp->ident = sub.fd_readwrite.fd; if ((sub.fd_readwrite.flags & CLOUDABI_SUBSCRIPTION_FD_READWRITE_POLL) != 0) kevp->fflags = NOTE_FILE_POLL; break; case CLOUDABI_EVENTTYPE_FD_WRITE: kevp->filter = EVFILT_WRITE; kevp->ident = sub.fd_readwrite.fd; break; case CLOUDABI_EVENTTYPE_PROC_TERMINATE: kevp->filter = EVFILT_PROCDESC; kevp->ident = sub.proc_terminate.fd; kevp->fflags = NOTE_EXIT; break; } if (args->once) { /* Ignore flags. Simply use oneshot mode. */ kevp->flags = EV_ADD | EV_ONESHOT; } else { /* Translate flags. */ if ((sub.flags & CLOUDABI_SUBSCRIPTION_ADD) != 0) kevp->flags |= EV_ADD; if ((sub.flags & CLOUDABI_SUBSCRIPTION_CLEAR) != 0) kevp->flags |= EV_CLEAR; if ((sub.flags & CLOUDABI_SUBSCRIPTION_DELETE) != 0) kevp->flags |= EV_DELETE; if ((sub.flags & CLOUDABI_SUBSCRIPTION_DISABLE) != 0) kevp->flags |= EV_DISABLE; if ((sub.flags & CLOUDABI_SUBSCRIPTION_ENABLE) != 0) kevp->flags |= EV_ENABLE; if ((sub.flags & CLOUDABI_SUBSCRIPTION_ONESHOT) != 0) kevp->flags |= EV_ONESHOT; } ++kevp; } return (0); } /* Converts FreeBSD's struct kevent to CloudABI's event objects. */ static int cloudabi64_kevent_copyout(void *arg, struct kevent *kevp, int count) { cloudabi64_event_t ev; struct cloudabi64_kevent_args *args; int error; args = arg; while (count-- > 0) { /* Convert fields that should always be present. */ memset(&ev, 0, sizeof(ev)); ev.userdata = (uintptr_t)kevp->udata; switch (kevp->filter) { case EVFILT_TIMER: ev.type = CLOUDABI_EVENTTYPE_CLOCK; ev.clock.identifier = kevp->ident; break; case EVFILT_READ: ev.type = CLOUDABI_EVENTTYPE_FD_READ; ev.fd_readwrite.fd = kevp->ident; break; case EVFILT_WRITE: ev.type = CLOUDABI_EVENTTYPE_FD_WRITE; ev.fd_readwrite.fd = kevp->ident; break; case EVFILT_PROCDESC: ev.type = CLOUDABI_EVENTTYPE_PROC_TERMINATE; ev.proc_terminate.fd = kevp->ident; break; } if ((kevp->flags & EV_ERROR) == 0) { /* Success. */ switch (kevp->filter) { case EVFILT_READ: case EVFILT_WRITE: ev.fd_readwrite.nbytes = kevp->data; if ((kevp->flags & EV_EOF) != 0) { ev.fd_readwrite.flags |= CLOUDABI_EVENT_FD_READWRITE_HANGUP; } break; case EVFILT_PROCDESC: if (WIFSIGNALED(kevp->data)) { /* Process got signalled. */ ev.proc_terminate.signal = convert_signal(WTERMSIG(kevp->data)); ev.proc_terminate.exitcode = 0; } else { /* Process exited. */ ev.proc_terminate.signal = 0; ev.proc_terminate.exitcode = WEXITSTATUS(kevp->data); } break; } } else { /* Error. */ ev.error = cloudabi_convert_errno(kevp->data); } ++kevp; /* TODO(ed): Copy out multiple entries at once. */ error = copyout(&ev, args->out++, sizeof(ev)); if (error != 0) return (error); } return (0); } int cloudabi64_sys_poll(struct thread *td, struct cloudabi64_sys_poll_args *uap) { struct cloudabi64_kevent_args args = { .in = uap->in, .out = uap->out, .once = true, }; struct kevent_copyops copyops = { .k_copyin = cloudabi64_kevent_copyin, .k_copyout = cloudabi64_kevent_copyout, .arg = &args, }; + /* + * Bandaid to support CloudABI futex constructs that are not + * implemented through FreeBSD's kqueue(). + */ + if (uap->nevents == 1) { + cloudabi64_subscription_t sub; + cloudabi64_event_t ev = {}; + int error; + + error = copyin(uap->in, &sub, sizeof(sub)); + if (error != 0) + return (error); + ev.userdata = sub.userdata; + ev.type = sub.type; + if (sub.type == CLOUDABI_EVENTTYPE_CONDVAR) { + /* Wait on a condition variable. */ + ev.condvar.condvar = sub.condvar.condvar; + ev.error = cloudabi_convert_errno( + cloudabi_futex_condvar_wait( + td, (cloudabi_condvar_t *)sub.condvar.condvar, + sub.condvar.condvar_scope, + (cloudabi_lock_t *)sub.condvar.lock, + sub.condvar.lock_scope, + CLOUDABI_CLOCK_MONOTONIC, UINT64_MAX, 0)); + td->td_retval[0] = 1; + return (copyout(&ev, uap->out, sizeof(ev))); + } else if (sub.type == CLOUDABI_EVENTTYPE_LOCK_RDLOCK) { + /* Acquire a read lock. */ + ev.lock.lock = sub.lock.lock; + ev.error = cloudabi_convert_errno( + cloudabi_futex_lock_rdlock( + td, (cloudabi_lock_t *)sub.lock.lock, + sub.lock.lock_scope, CLOUDABI_CLOCK_MONOTONIC, + UINT64_MAX, 0)); + td->td_retval[0] = 1; + return (copyout(&ev, uap->out, sizeof(ev))); + } else if (sub.type == CLOUDABI_EVENTTYPE_LOCK_WRLOCK) { + /* Acquire a write lock. */ + ev.lock.lock = sub.lock.lock; + ev.error = cloudabi_convert_errno( + cloudabi_futex_lock_wrlock( + td, (cloudabi_lock_t *)sub.lock.lock, + sub.lock.lock_scope, CLOUDABI_CLOCK_MONOTONIC, + UINT64_MAX, 0)); + td->td_retval[0] = 1; + return (copyout(&ev, uap->out, sizeof(ev))); + } + } else if (uap->nevents == 2) { + cloudabi64_subscription_t sub[2]; + cloudabi64_event_t ev[2] = {}; + int error; + + error = copyin(uap->in, &sub, sizeof(sub)); + if (error != 0) + return (error); + ev[0].userdata = sub[0].userdata; + ev[0].type = sub[0].type; + ev[1].userdata = sub[1].userdata; + ev[1].type = sub[1].type; + if (sub[0].type == CLOUDABI_EVENTTYPE_CONDVAR && + sub[1].type == CLOUDABI_EVENTTYPE_CLOCK && + sub[1].clock.flags == CLOUDABI_SUBSCRIPTION_CLOCK_ABSTIME) { + /* Wait for a condition variable with timeout. */ + ev[0].condvar.condvar = sub[0].condvar.condvar; + ev[1].clock.identifier = sub[1].clock.identifier; + error = cloudabi_futex_condvar_wait( + td, (cloudabi_condvar_t *)sub[0].condvar.condvar, + sub[0].condvar.condvar_scope, + (cloudabi_lock_t *)sub[0].condvar.lock, + sub[0].condvar.lock_scope, sub[1].clock.clock_id, + sub[1].clock.timeout, sub[1].clock.precision); + if (error == ETIMEDOUT) { + td->td_retval[0] = 1; + return (copyout(&ev[1], uap->out, + sizeof(ev[1]))); + } + + ev[0].error = cloudabi_convert_errno(error); + td->td_retval[0] = 1; + return (copyout(&ev[0], uap->out, sizeof(ev[0]))); + } else if (sub[0].type == CLOUDABI_EVENTTYPE_LOCK_RDLOCK && + sub[1].type == CLOUDABI_EVENTTYPE_CLOCK && + sub[1].clock.flags == CLOUDABI_SUBSCRIPTION_CLOCK_ABSTIME) { + /* Acquire a read lock with a timeout. */ + ev[0].lock.lock = sub[0].lock.lock; + ev[1].clock.identifier = sub[1].clock.identifier; + error = cloudabi_futex_lock_rdlock( + td, (cloudabi_lock_t *)sub[0].lock.lock, + sub[0].lock.lock_scope, sub[1].clock.clock_id, + sub[1].clock.timeout, sub[1].clock.precision); + if (error == ETIMEDOUT) { + td->td_retval[0] = 1; + return (copyout(&ev[1], uap->out, + sizeof(ev[1]))); + } + + ev[0].error = cloudabi_convert_errno(error); + td->td_retval[0] = 1; + return (copyout(&ev[0], uap->out, sizeof(ev[0]))); + } else if (sub[0].type == CLOUDABI_EVENTTYPE_LOCK_WRLOCK && + sub[1].type == CLOUDABI_EVENTTYPE_CLOCK && + sub[1].clock.flags == CLOUDABI_SUBSCRIPTION_CLOCK_ABSTIME) { + /* Acquire a write lock with a timeout. */ + ev[0].lock.lock = sub[0].lock.lock; + ev[1].clock.identifier = sub[1].clock.identifier; + error = cloudabi_futex_lock_wrlock( + td, (cloudabi_lock_t *)sub[0].lock.lock, + sub[0].lock.lock_scope, sub[1].clock.clock_id, + sub[1].clock.timeout, sub[1].clock.precision); + if (error == ETIMEDOUT) { + td->td_retval[0] = 1; + return (copyout(&ev[1], uap->out, + sizeof(ev[1]))); + } + + ev[0].error = cloudabi_convert_errno(error); + td->td_retval[0] = 1; + return (copyout(&ev[0], uap->out, sizeof(ev[0]))); + } + } + return (kern_kevent_anonymous(td, uap->nevents, ©ops)); } int cloudabi64_sys_poll_fd(struct thread *td, struct cloudabi64_sys_poll_fd_args *uap) { struct cloudabi64_kevent_args args = { .in = uap->in, .out = uap->out, .once = false, }; struct kevent_copyops copyops = { .k_copyin = cloudabi64_kevent_copyin, .k_copyout = cloudabi64_kevent_copyout, .arg = &args, }; cloudabi64_subscription_t subtimo; struct timespec timeout; int error; if (uap->timeout != NULL) { /* Poll with a timeout. */ error = copyin(uap->timeout, &subtimo, sizeof(subtimo)); if (error != 0) return (error); if (subtimo.type != CLOUDABI_EVENTTYPE_CLOCK || subtimo.clock.flags != 0) return (EINVAL); timeout.tv_sec = subtimo.clock.timeout / 1000000000; timeout.tv_nsec = subtimo.clock.timeout % 1000000000; return (kern_kevent(td, uap->fd, uap->nin, uap->nout, ©ops, &timeout)); } else { /* Poll without a timeout. */ return (kern_kevent(td, uap->fd, uap->nin, uap->nout, ©ops, NULL)); } }