aboutsummaryrefslogtreecommitdiff
path: root/sys/compat/cloudabi64/cloudabi64_poll.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/compat/cloudabi64/cloudabi64_poll.c')
-rw-r--r--sys/compat/cloudabi64/cloudabi64_poll.c340
1 files changed, 340 insertions, 0 deletions
diff --git a/sys/compat/cloudabi64/cloudabi64_poll.c b/sys/compat/cloudabi64/cloudabi64_poll.c
new file mode 100644
index 000000000000..2d3d87f3ac7f
--- /dev/null
+++ b/sys/compat/cloudabi64/cloudabi64_poll.c
@@ -0,0 +1,340 @@
+/*-
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/proc.h>
+#include <sys/syscallsubr.h>
+
+#include <contrib/cloudabi/cloudabi64_types.h>
+
+#include <compat/cloudabi/cloudabi_util.h>
+
+#include <compat/cloudabi64/cloudabi64_proto.h>
+#include <compat/cloudabi64/cloudabi64_util.h>
+
+/* 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;
+ cloudabi_event_t *out;
+};
+
+/* 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 = TO_PTR(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;
+ 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;
+ }
+ kevp->flags = EV_ADD | 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)
+{
+ cloudabi_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;
+ break;
+ case EVFILT_READ:
+ ev.type = CLOUDABI_EVENTTYPE_FD_READ;
+ break;
+ case EVFILT_WRITE:
+ ev.type = CLOUDABI_EVENTTYPE_FD_WRITE;
+ break;
+ case EVFILT_PROCDESC:
+ ev.type = CLOUDABI_EVENTTYPE_PROC_TERMINATE;
+ 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,
+ };
+ 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->nsubscriptions == 1) {
+ cloudabi64_subscription_t sub;
+ cloudabi_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.error = cloudabi_convert_errno(
+ cloudabi_futex_condvar_wait(
+ td, TO_PTR(sub.condvar.condvar),
+ sub.condvar.condvar_scope,
+ TO_PTR(sub.condvar.lock),
+ sub.condvar.lock_scope,
+ CLOUDABI_CLOCK_MONOTONIC, UINT64_MAX, 0, true));
+ 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.error = cloudabi_convert_errno(
+ cloudabi_futex_lock_rdlock(
+ td, TO_PTR(sub.lock.lock),
+ sub.lock.lock_scope, CLOUDABI_CLOCK_MONOTONIC,
+ UINT64_MAX, 0, true));
+ 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.error = cloudabi_convert_errno(
+ cloudabi_futex_lock_wrlock(
+ td, TO_PTR(sub.lock.lock),
+ sub.lock.lock_scope, CLOUDABI_CLOCK_MONOTONIC,
+ UINT64_MAX, 0, true));
+ td->td_retval[0] = 1;
+ return (copyout(&ev, uap->out, sizeof(ev)));
+ }
+ } else if (uap->nsubscriptions == 2) {
+ cloudabi64_subscription_t sub[2];
+ cloudabi_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) {
+ /* Wait for a condition variable with timeout. */
+ error = cloudabi_futex_condvar_wait(
+ td, TO_PTR(sub[0].condvar.condvar),
+ sub[0].condvar.condvar_scope,
+ TO_PTR(sub[0].condvar.lock),
+ sub[0].condvar.lock_scope, sub[1].clock.clock_id,
+ sub[1].clock.timeout, sub[1].clock.precision,
+ (sub[1].clock.flags &
+ CLOUDABI_SUBSCRIPTION_CLOCK_ABSTIME) != 0);
+ 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) {
+ /* Acquire a read lock with a timeout. */
+ error = cloudabi_futex_lock_rdlock(
+ td, TO_PTR(sub[0].lock.lock),
+ sub[0].lock.lock_scope, sub[1].clock.clock_id,
+ sub[1].clock.timeout, sub[1].clock.precision,
+ (sub[1].clock.flags &
+ CLOUDABI_SUBSCRIPTION_CLOCK_ABSTIME) != 0);
+ 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) {
+ /* Acquire a write lock with a timeout. */
+ error = cloudabi_futex_lock_wrlock(
+ td, TO_PTR(sub[0].lock.lock),
+ sub[0].lock.lock_scope, sub[1].clock.clock_id,
+ sub[1].clock.timeout, sub[1].clock.precision,
+ (sub[1].clock.flags &
+ CLOUDABI_SUBSCRIPTION_CLOCK_ABSTIME) != 0);
+ 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->nsubscriptions, &copyops));
+}