]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/compat/cloudabi64/cloudabi64_poll.c
Make blocking CloudABI futex operations work.
[FreeBSD/FreeBSD.git] / sys / compat / cloudabi64 / cloudabi64_poll.c
1 /*-
2  * Copyright (c) 2015 Nuxi, https://nuxi.nl/
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
17  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23  * SUCH DAMAGE.
24  */
25
26 #include <sys/cdefs.h>
27 __FBSDID("$FreeBSD$");
28
29 #include <sys/param.h>
30 #include <sys/proc.h>
31 #include <sys/syscallsubr.h>
32
33 #include <compat/cloudabi/cloudabi_util.h>
34
35 #include <compat/cloudabi64/cloudabi64_syscalldefs.h>
36 #include <compat/cloudabi64/cloudabi64_proto.h>
37
38 /* Converts a FreeBSD signal number to a CloudABI signal number. */
39 static cloudabi_signal_t
40 convert_signal(int sig)
41 {
42         static const cloudabi_signal_t signals[] = {
43                 [SIGABRT]       = CLOUDABI_SIGABRT,
44                 [SIGALRM]       = CLOUDABI_SIGALRM,
45                 [SIGBUS]        = CLOUDABI_SIGBUS,
46                 [SIGCHLD]       = CLOUDABI_SIGCHLD,
47                 [SIGCONT]       = CLOUDABI_SIGCONT,
48                 [SIGFPE]        = CLOUDABI_SIGFPE,
49                 [SIGHUP]        = CLOUDABI_SIGHUP,
50                 [SIGILL]        = CLOUDABI_SIGILL,
51                 [SIGINT]        = CLOUDABI_SIGINT,
52                 [SIGKILL]       = CLOUDABI_SIGKILL,
53                 [SIGPIPE]       = CLOUDABI_SIGPIPE,
54                 [SIGQUIT]       = CLOUDABI_SIGQUIT,
55                 [SIGSEGV]       = CLOUDABI_SIGSEGV,
56                 [SIGSTOP]       = CLOUDABI_SIGSTOP,
57                 [SIGSYS]        = CLOUDABI_SIGSYS,
58                 [SIGTERM]       = CLOUDABI_SIGTERM,
59                 [SIGTRAP]       = CLOUDABI_SIGTRAP,
60                 [SIGTSTP]       = CLOUDABI_SIGTSTP,
61                 [SIGTTIN]       = CLOUDABI_SIGTTIN,
62                 [SIGTTOU]       = CLOUDABI_SIGTTOU,
63                 [SIGURG]        = CLOUDABI_SIGURG,
64                 [SIGUSR1]       = CLOUDABI_SIGUSR1,
65                 [SIGUSR2]       = CLOUDABI_SIGUSR2,
66                 [SIGVTALRM]     = CLOUDABI_SIGVTALRM,
67                 [SIGXCPU]       = CLOUDABI_SIGXCPU,
68                 [SIGXFSZ]       = CLOUDABI_SIGXFSZ,
69         };
70
71         /* Convert unknown signals to SIGABRT. */
72         if (sig < 0 || sig >= nitems(signals) || signals[sig] == 0)
73                 return (SIGABRT);
74         return (signals[sig]);
75 }
76
77 struct cloudabi64_kevent_args {
78         const cloudabi64_subscription_t *in;
79         cloudabi64_event_t *out;
80         bool once;
81 };
82
83 /* Converts CloudABI's subscription objects to FreeBSD's struct kevent. */
84 static int
85 cloudabi64_kevent_copyin(void *arg, struct kevent *kevp, int count)
86 {
87         cloudabi64_subscription_t sub;
88         struct cloudabi64_kevent_args *args;
89         cloudabi_timestamp_t ts;
90         int error;
91
92         args = arg;
93         while (count-- > 0) {
94                 /* TODO(ed): Copy in multiple entries at once. */
95                 error = copyin(args->in++, &sub, sizeof(sub));
96                 if (error != 0)
97                         return (error);
98
99                 memset(kevp, 0, sizeof(*kevp));
100                 kevp->udata = (void *)sub.userdata;
101                 switch (sub.type) {
102                 case CLOUDABI_EVENTTYPE_CLOCK:
103                         kevp->filter = EVFILT_TIMER;
104                         kevp->ident = sub.clock.identifier;
105                         kevp->fflags = NOTE_NSECONDS;
106                         if ((sub.clock.flags &
107                             CLOUDABI_SUBSCRIPTION_CLOCK_ABSTIME) != 0 &&
108                             sub.clock.timeout > 0) {
109                                 /* Convert absolute timestamp to a relative. */
110                                 error = cloudabi_clock_time_get(curthread,
111                                     sub.clock.clock_id, &ts);
112                                 if (error != 0)
113                                         return (error);
114                                 ts = ts > sub.clock.timeout ? 0 :
115                                     sub.clock.timeout - ts;
116                         } else {
117                                 /* Relative timestamp. */
118                                 ts = sub.clock.timeout;
119                         }
120                         kevp->data = ts > INTPTR_MAX ? INTPTR_MAX : ts;
121                         break;
122                 case CLOUDABI_EVENTTYPE_FD_READ:
123                         kevp->filter = EVFILT_READ;
124                         kevp->ident = sub.fd_readwrite.fd;
125                         if ((sub.fd_readwrite.flags &
126                             CLOUDABI_SUBSCRIPTION_FD_READWRITE_POLL) != 0)
127                                 kevp->fflags = NOTE_FILE_POLL;
128                         break;
129                 case CLOUDABI_EVENTTYPE_FD_WRITE:
130                         kevp->filter = EVFILT_WRITE;
131                         kevp->ident = sub.fd_readwrite.fd;
132                         break;
133                 case CLOUDABI_EVENTTYPE_PROC_TERMINATE:
134                         kevp->filter = EVFILT_PROCDESC;
135                         kevp->ident = sub.proc_terminate.fd;
136                         kevp->fflags = NOTE_EXIT;
137                         break;
138                 }
139                 if (args->once) {
140                         /* Ignore flags. Simply use oneshot mode. */
141                         kevp->flags = EV_ADD | EV_ONESHOT;
142                 } else {
143                         /* Translate flags. */
144                         if ((sub.flags & CLOUDABI_SUBSCRIPTION_ADD) != 0)
145                                 kevp->flags |= EV_ADD;
146                         if ((sub.flags & CLOUDABI_SUBSCRIPTION_CLEAR) != 0)
147                                 kevp->flags |= EV_CLEAR;
148                         if ((sub.flags & CLOUDABI_SUBSCRIPTION_DELETE) != 0)
149                                 kevp->flags |= EV_DELETE;
150                         if ((sub.flags & CLOUDABI_SUBSCRIPTION_DISABLE) != 0)
151                                 kevp->flags |= EV_DISABLE;
152                         if ((sub.flags & CLOUDABI_SUBSCRIPTION_ENABLE) != 0)
153                                 kevp->flags |= EV_ENABLE;
154                         if ((sub.flags & CLOUDABI_SUBSCRIPTION_ONESHOT) != 0)
155                                 kevp->flags |= EV_ONESHOT;
156                 }
157                 ++kevp;
158         }
159         return (0);
160 }
161
162 /* Converts FreeBSD's struct kevent to CloudABI's event objects. */
163 static int
164 cloudabi64_kevent_copyout(void *arg, struct kevent *kevp, int count)
165 {
166         cloudabi64_event_t ev;
167         struct cloudabi64_kevent_args *args;
168         int error;
169
170         args = arg;
171         while (count-- > 0) {
172                 /* Convert fields that should always be present. */
173                 memset(&ev, 0, sizeof(ev));
174                 ev.userdata = (uintptr_t)kevp->udata;
175                 switch (kevp->filter) {
176                 case EVFILT_TIMER:
177                         ev.type = CLOUDABI_EVENTTYPE_CLOCK;
178                         ev.clock.identifier = kevp->ident;
179                         break;
180                 case EVFILT_READ:
181                         ev.type = CLOUDABI_EVENTTYPE_FD_READ;
182                         ev.fd_readwrite.fd = kevp->ident;
183                         break;
184                 case EVFILT_WRITE:
185                         ev.type = CLOUDABI_EVENTTYPE_FD_WRITE;
186                         ev.fd_readwrite.fd = kevp->ident;
187                         break;
188                 case EVFILT_PROCDESC:
189                         ev.type = CLOUDABI_EVENTTYPE_PROC_TERMINATE;
190                         ev.proc_terminate.fd = kevp->ident;
191                         break;
192                 }
193
194                 if ((kevp->flags & EV_ERROR) == 0) {
195                         /* Success. */
196                         switch (kevp->filter) {
197                         case EVFILT_READ:
198                         case EVFILT_WRITE:
199                                 ev.fd_readwrite.nbytes = kevp->data;
200                                 if ((kevp->flags & EV_EOF) != 0) {
201                                         ev.fd_readwrite.flags |=
202                                             CLOUDABI_EVENT_FD_READWRITE_HANGUP;
203                                 }
204                                 break;
205                         case EVFILT_PROCDESC:
206                                 if (WIFSIGNALED(kevp->data)) {
207                                         /* Process got signalled. */
208                                         ev.proc_terminate.signal =
209                                            convert_signal(WTERMSIG(kevp->data));
210                                         ev.proc_terminate.exitcode = 0;
211                                 } else {
212                                         /* Process exited. */
213                                         ev.proc_terminate.signal = 0;
214                                         ev.proc_terminate.exitcode =
215                                             WEXITSTATUS(kevp->data);
216                                 }
217                                 break;
218                         }
219                 } else {
220                         /* Error. */
221                         ev.error = cloudabi_convert_errno(kevp->data);
222                 }
223                 ++kevp;
224
225                 /* TODO(ed): Copy out multiple entries at once. */
226                 error = copyout(&ev, args->out++, sizeof(ev));
227                 if (error != 0)
228                         return (error);
229         }
230         return (0);
231 }
232
233 int
234 cloudabi64_sys_poll(struct thread *td, struct cloudabi64_sys_poll_args *uap)
235 {
236         struct cloudabi64_kevent_args args = {
237                 .in     = uap->in,
238                 .out    = uap->out,
239                 .once   = true,
240         };
241         struct kevent_copyops copyops = {
242                 .k_copyin       = cloudabi64_kevent_copyin,
243                 .k_copyout      = cloudabi64_kevent_copyout,
244                 .arg            = &args,
245         };
246
247         /*
248          * Bandaid to support CloudABI futex constructs that are not
249          * implemented through FreeBSD's kqueue().
250          */
251         if (uap->nevents == 1) {
252                 cloudabi64_subscription_t sub;
253                 cloudabi64_event_t ev = {};
254                 int error;
255
256                 error = copyin(uap->in, &sub, sizeof(sub));
257                 if (error != 0)
258                         return (error);
259                 ev.userdata = sub.userdata;
260                 ev.type = sub.type;
261                 if (sub.type == CLOUDABI_EVENTTYPE_CONDVAR) {
262                         /* Wait on a condition variable. */
263                         ev.condvar.condvar = sub.condvar.condvar;
264                         ev.error = cloudabi_convert_errno(
265                             cloudabi_futex_condvar_wait(
266                                 td, (cloudabi_condvar_t *)sub.condvar.condvar,
267                                 sub.condvar.condvar_scope,
268                                 (cloudabi_lock_t *)sub.condvar.lock,
269                                 sub.condvar.lock_scope,
270                                 CLOUDABI_CLOCK_MONOTONIC, UINT64_MAX, 0));
271                         td->td_retval[0] = 1;
272                         return (copyout(&ev, uap->out, sizeof(ev)));
273                 } else if (sub.type == CLOUDABI_EVENTTYPE_LOCK_RDLOCK) {
274                         /* Acquire a read lock. */
275                         ev.lock.lock = sub.lock.lock;
276                         ev.error = cloudabi_convert_errno(
277                             cloudabi_futex_lock_rdlock(
278                                 td, (cloudabi_lock_t *)sub.lock.lock,
279                                 sub.lock.lock_scope, CLOUDABI_CLOCK_MONOTONIC,
280                                 UINT64_MAX, 0));
281                         td->td_retval[0] = 1;
282                         return (copyout(&ev, uap->out, sizeof(ev)));
283                 } else if (sub.type == CLOUDABI_EVENTTYPE_LOCK_WRLOCK) {
284                         /* Acquire a write lock. */
285                         ev.lock.lock = sub.lock.lock;
286                         ev.error = cloudabi_convert_errno(
287                             cloudabi_futex_lock_wrlock(
288                                 td, (cloudabi_lock_t *)sub.lock.lock,
289                                 sub.lock.lock_scope, CLOUDABI_CLOCK_MONOTONIC,
290                                 UINT64_MAX, 0));
291                         td->td_retval[0] = 1;
292                         return (copyout(&ev, uap->out, sizeof(ev)));
293                 }
294         } else if (uap->nevents == 2) {
295                 cloudabi64_subscription_t sub[2];
296                 cloudabi64_event_t ev[2] = {};
297                 int error;
298
299                 error = copyin(uap->in, &sub, sizeof(sub));
300                 if (error != 0)
301                         return (error);
302                 ev[0].userdata = sub[0].userdata;
303                 ev[0].type = sub[0].type;
304                 ev[1].userdata = sub[1].userdata;
305                 ev[1].type = sub[1].type;
306                 if (sub[0].type == CLOUDABI_EVENTTYPE_CONDVAR &&
307                     sub[1].type == CLOUDABI_EVENTTYPE_CLOCK &&
308                     sub[1].clock.flags == CLOUDABI_SUBSCRIPTION_CLOCK_ABSTIME) {
309                         /* Wait for a condition variable with timeout. */
310                         ev[0].condvar.condvar = sub[0].condvar.condvar;
311                         ev[1].clock.identifier = sub[1].clock.identifier;
312                         error = cloudabi_futex_condvar_wait(
313                             td, (cloudabi_condvar_t *)sub[0].condvar.condvar,
314                             sub[0].condvar.condvar_scope,
315                             (cloudabi_lock_t *)sub[0].condvar.lock,
316                             sub[0].condvar.lock_scope, sub[1].clock.clock_id,
317                             sub[1].clock.timeout, sub[1].clock.precision);
318                         if (error == ETIMEDOUT) {
319                                 td->td_retval[0] = 1;
320                                 return (copyout(&ev[1], uap->out,
321                                     sizeof(ev[1])));
322                         }
323
324                         ev[0].error = cloudabi_convert_errno(error);
325                         td->td_retval[0] = 1;
326                         return (copyout(&ev[0], uap->out, sizeof(ev[0])));
327                 } else if (sub[0].type == CLOUDABI_EVENTTYPE_LOCK_RDLOCK &&
328                     sub[1].type == CLOUDABI_EVENTTYPE_CLOCK &&
329                     sub[1].clock.flags == CLOUDABI_SUBSCRIPTION_CLOCK_ABSTIME) {
330                         /* Acquire a read lock with a timeout. */
331                         ev[0].lock.lock = sub[0].lock.lock;
332                         ev[1].clock.identifier = sub[1].clock.identifier;
333                         error = cloudabi_futex_lock_rdlock(
334                             td, (cloudabi_lock_t *)sub[0].lock.lock,
335                             sub[0].lock.lock_scope, sub[1].clock.clock_id,
336                             sub[1].clock.timeout, sub[1].clock.precision);
337                         if (error == ETIMEDOUT) {
338                                 td->td_retval[0] = 1;
339                                 return (copyout(&ev[1], uap->out,
340                                     sizeof(ev[1])));
341                         }
342
343                         ev[0].error = cloudabi_convert_errno(error);
344                         td->td_retval[0] = 1;
345                         return (copyout(&ev[0], uap->out, sizeof(ev[0])));
346                 } else if (sub[0].type == CLOUDABI_EVENTTYPE_LOCK_WRLOCK &&
347                     sub[1].type == CLOUDABI_EVENTTYPE_CLOCK &&
348                     sub[1].clock.flags == CLOUDABI_SUBSCRIPTION_CLOCK_ABSTIME) {
349                         /* Acquire a write lock with a timeout. */
350                         ev[0].lock.lock = sub[0].lock.lock;
351                         ev[1].clock.identifier = sub[1].clock.identifier;
352                         error = cloudabi_futex_lock_wrlock(
353                             td, (cloudabi_lock_t *)sub[0].lock.lock,
354                             sub[0].lock.lock_scope, sub[1].clock.clock_id,
355                             sub[1].clock.timeout, sub[1].clock.precision);
356                         if (error == ETIMEDOUT) {
357                                 td->td_retval[0] = 1;
358                                 return (copyout(&ev[1], uap->out,
359                                     sizeof(ev[1])));
360                         }
361
362                         ev[0].error = cloudabi_convert_errno(error);
363                         td->td_retval[0] = 1;
364                         return (copyout(&ev[0], uap->out, sizeof(ev[0])));
365                 }
366         }
367
368         return (kern_kevent_anonymous(td, uap->nevents, &copyops));
369 }
370
371 int
372 cloudabi64_sys_poll_fd(struct thread *td,
373     struct cloudabi64_sys_poll_fd_args *uap)
374 {
375         struct cloudabi64_kevent_args args = {
376                 .in     = uap->in,
377                 .out    = uap->out,
378                 .once   = false,
379         };
380         struct kevent_copyops copyops = {
381                 .k_copyin       = cloudabi64_kevent_copyin,
382                 .k_copyout      = cloudabi64_kevent_copyout,
383                 .arg            = &args,
384         };
385         cloudabi64_subscription_t subtimo;
386         struct timespec timeout;
387         int error;
388
389         if (uap->timeout != NULL) {
390                 /* Poll with a timeout. */
391                 error = copyin(uap->timeout, &subtimo, sizeof(subtimo));
392                 if (error != 0)
393                         return (error);
394                 if (subtimo.type != CLOUDABI_EVENTTYPE_CLOCK ||
395                     subtimo.clock.flags != 0)
396                         return (EINVAL);
397                 timeout.tv_sec = subtimo.clock.timeout / 1000000000;
398                 timeout.tv_nsec = subtimo.clock.timeout % 1000000000;
399                 return (kern_kevent(td, uap->fd, uap->nin, uap->nout, &copyops,
400                     &timeout));
401         } else {
402                 /* Poll without a timeout. */
403                 return (kern_kevent(td, uap->fd, uap->nin, uap->nout, &copyops,
404                     NULL));
405         }
406 }