1e16fe1c7SDmitry Chagin /*-
24d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause
311c9f2ffSKonstantin Belousov *
4e16fe1c7SDmitry Chagin * Copyright (c) 2007 Roman Divacky
51ca6b15bSDmitry Chagin * Copyright (c) 2014 Dmitry Chagin <dchagin@FreeBSD.org>
6e16fe1c7SDmitry Chagin *
7e16fe1c7SDmitry Chagin * Redistribution and use in source and binary forms, with or without
8e16fe1c7SDmitry Chagin * modification, are permitted provided that the following conditions
9e16fe1c7SDmitry Chagin * are met:
10e16fe1c7SDmitry Chagin * 1. Redistributions of source code must retain the above copyright
11e16fe1c7SDmitry Chagin * notice, this list of conditions and the following disclaimer.
12e16fe1c7SDmitry Chagin * 2. Redistributions in binary form must reproduce the above copyright
13e16fe1c7SDmitry Chagin * notice, this list of conditions and the following disclaimer in the
14e16fe1c7SDmitry Chagin * documentation and/or other materials provided with the distribution.
15e16fe1c7SDmitry Chagin *
16e16fe1c7SDmitry Chagin * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17e16fe1c7SDmitry Chagin * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18e16fe1c7SDmitry Chagin * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19e16fe1c7SDmitry Chagin * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20e16fe1c7SDmitry Chagin * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21e16fe1c7SDmitry Chagin * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22e16fe1c7SDmitry Chagin * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23e16fe1c7SDmitry Chagin * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24e16fe1c7SDmitry Chagin * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25e16fe1c7SDmitry Chagin * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26e16fe1c7SDmitry Chagin * SUCH DAMAGE.
27e16fe1c7SDmitry Chagin */
28e16fe1c7SDmitry Chagin
29e16fe1c7SDmitry Chagin #include <sys/param.h>
30dd93b628SDmitry Chagin #include <sys/callout.h>
31df4336ddSEd Maste #include <sys/capsicum.h>
32d8e53d94SDmitry Chagin #include <sys/errno.h>
33d8e53d94SDmitry Chagin #include <sys/event.h>
34d8e53d94SDmitry Chagin #include <sys/eventfd.h>
35e16fe1c7SDmitry Chagin #include <sys/file.h>
36e16fe1c7SDmitry Chagin #include <sys/filedesc.h>
372bb14e75SDmitry Chagin #include <sys/filio.h>
38d8e53d94SDmitry Chagin #include <sys/limits.h>
39d8e53d94SDmitry Chagin #include <sys/lock.h>
40d8e53d94SDmitry Chagin #include <sys/mutex.h>
41a31d7686SDmitry Chagin #include <sys/poll.h>
42e16fe1c7SDmitry Chagin #include <sys/proc.h>
43a31d7686SDmitry Chagin #include <sys/selinfo.h>
447a202823SKonstantin Belousov #include <sys/specialfd.h>
45e16fe1c7SDmitry Chagin #include <sys/sx.h>
46e16fe1c7SDmitry Chagin #include <sys/syscallsubr.h>
47af93fea7SJake Freeland #include <sys/timerfd.h>
48e16fe1c7SDmitry Chagin #include <sys/timespec.h>
49d8e53d94SDmitry Chagin #include <sys/user.h>
50e16fe1c7SDmitry Chagin
51e16fe1c7SDmitry Chagin #ifdef COMPAT_LINUX32
52e16fe1c7SDmitry Chagin #include <machine/../linux32/linux.h>
53e16fe1c7SDmitry Chagin #include <machine/../linux32/linux32_proto.h>
54e16fe1c7SDmitry Chagin #else
55e16fe1c7SDmitry Chagin #include <machine/../linux/linux.h>
56e16fe1c7SDmitry Chagin #include <machine/../linux/linux_proto.h>
57e16fe1c7SDmitry Chagin #endif
58e16fe1c7SDmitry Chagin
59e16fe1c7SDmitry Chagin #include <compat/linux/linux_emul.h>
60e16fe1c7SDmitry Chagin #include <compat/linux/linux_event.h>
61e16fe1c7SDmitry Chagin #include <compat/linux/linux_file.h>
623923e632SDmitry Chagin #include <compat/linux/linux_signal.h>
63c8a79231SDmitry Chagin #include <compat/linux/linux_time.h>
64e16fe1c7SDmitry Chagin #include <compat/linux/linux_util.h>
65e16fe1c7SDmitry Chagin
66e16fe1c7SDmitry Chagin typedef uint64_t epoll_udata_t;
67e16fe1c7SDmitry Chagin
68e16fe1c7SDmitry Chagin struct epoll_event {
69e16fe1c7SDmitry Chagin uint32_t events;
70e16fe1c7SDmitry Chagin epoll_udata_t data;
71e16fe1c7SDmitry Chagin }
72e16fe1c7SDmitry Chagin #if defined(__amd64__)
73e16fe1c7SDmitry Chagin __attribute__((packed))
74e16fe1c7SDmitry Chagin #endif
75e16fe1c7SDmitry Chagin ;
76e16fe1c7SDmitry Chagin
77e16fe1c7SDmitry Chagin #define LINUX_MAX_EVENTS (INT_MAX / sizeof(struct epoll_event))
78e16fe1c7SDmitry Chagin
79461120b8SVladimir Kondratyev static int epoll_to_kevent(struct thread *td, int fd,
80461120b8SVladimir Kondratyev struct epoll_event *l_event, struct kevent *kevent,
81461120b8SVladimir Kondratyev int *nkevents);
82e16fe1c7SDmitry Chagin static void kevent_to_epoll(struct kevent *kevent, struct epoll_event *l_event);
83e16fe1c7SDmitry Chagin static int epoll_kev_copyout(void *arg, struct kevent *kevp, int count);
84e16fe1c7SDmitry Chagin static int epoll_kev_copyin(void *arg, struct kevent *kevp, int count);
85461120b8SVladimir Kondratyev static int epoll_register_kevent(struct thread *td, struct file *epfp,
86461120b8SVladimir Kondratyev int fd, int filter, unsigned int flags);
87461120b8SVladimir Kondratyev static int epoll_fd_registered(struct thread *td, struct file *epfp,
88461120b8SVladimir Kondratyev int fd);
89e16fe1c7SDmitry Chagin static int epoll_delete_all_events(struct thread *td, struct file *epfp,
90e16fe1c7SDmitry Chagin int fd);
91e16fe1c7SDmitry Chagin
92e16fe1c7SDmitry Chagin struct epoll_copyin_args {
93e16fe1c7SDmitry Chagin struct kevent *changelist;
94e16fe1c7SDmitry Chagin };
95e16fe1c7SDmitry Chagin
96e16fe1c7SDmitry Chagin struct epoll_copyout_args {
97e16fe1c7SDmitry Chagin struct epoll_event *leventlist;
98e16fe1c7SDmitry Chagin struct proc *p;
99e16fe1c7SDmitry Chagin uint32_t count;
100e16fe1c7SDmitry Chagin int error;
101e16fe1c7SDmitry Chagin };
102e16fe1c7SDmitry Chagin
103e16fe1c7SDmitry Chagin static int
epoll_create_common(struct thread * td,int flags)104e16fe1c7SDmitry Chagin epoll_create_common(struct thread *td, int flags)
105e16fe1c7SDmitry Chagin {
106e16fe1c7SDmitry Chagin
107b3c6fe66SVladimir Kondratyev return (kern_kqueue(td, flags, NULL));
108e16fe1c7SDmitry Chagin }
109e16fe1c7SDmitry Chagin
110931e2a1aSEd Maste #ifdef LINUX_LEGACY_SYSCALLS
111e16fe1c7SDmitry Chagin int
linux_epoll_create(struct thread * td,struct linux_epoll_create_args * args)112e16fe1c7SDmitry Chagin linux_epoll_create(struct thread *td, struct linux_epoll_create_args *args)
113e16fe1c7SDmitry Chagin {
114e16fe1c7SDmitry Chagin
115e16fe1c7SDmitry Chagin /*
116e16fe1c7SDmitry Chagin * args->size is unused. Linux just tests it
117e16fe1c7SDmitry Chagin * and then forgets it as well.
118e16fe1c7SDmitry Chagin */
119e16fe1c7SDmitry Chagin if (args->size <= 0)
120e16fe1c7SDmitry Chagin return (EINVAL);
121e16fe1c7SDmitry Chagin
122e16fe1c7SDmitry Chagin return (epoll_create_common(td, 0));
123e16fe1c7SDmitry Chagin }
124931e2a1aSEd Maste #endif
125e16fe1c7SDmitry Chagin
126e16fe1c7SDmitry Chagin int
linux_epoll_create1(struct thread * td,struct linux_epoll_create1_args * args)127e16fe1c7SDmitry Chagin linux_epoll_create1(struct thread *td, struct linux_epoll_create1_args *args)
128e16fe1c7SDmitry Chagin {
129e16fe1c7SDmitry Chagin int flags;
130e16fe1c7SDmitry Chagin
131e16fe1c7SDmitry Chagin if ((args->flags & ~(LINUX_O_CLOEXEC)) != 0)
132e16fe1c7SDmitry Chagin return (EINVAL);
133e16fe1c7SDmitry Chagin
134e16fe1c7SDmitry Chagin flags = 0;
135e16fe1c7SDmitry Chagin if ((args->flags & LINUX_O_CLOEXEC) != 0)
136e16fe1c7SDmitry Chagin flags |= O_CLOEXEC;
137e16fe1c7SDmitry Chagin
138e16fe1c7SDmitry Chagin return (epoll_create_common(td, flags));
139e16fe1c7SDmitry Chagin }
140e16fe1c7SDmitry Chagin
141e16fe1c7SDmitry Chagin /* Structure converting function from epoll to kevent. */
142e16fe1c7SDmitry Chagin static int
epoll_to_kevent(struct thread * td,int fd,struct epoll_event * l_event,struct kevent * kevent,int * nkevents)143461120b8SVladimir Kondratyev epoll_to_kevent(struct thread *td, int fd, struct epoll_event *l_event,
144e16fe1c7SDmitry Chagin struct kevent *kevent, int *nkevents)
145e16fe1c7SDmitry Chagin {
146e16fe1c7SDmitry Chagin uint32_t levents = l_event->events;
147e16fe1c7SDmitry Chagin struct linux_pemuldata *pem;
148e16fe1c7SDmitry Chagin struct proc *p;
149461120b8SVladimir Kondratyev unsigned short kev_flags = EV_ADD | EV_ENABLE;
150e16fe1c7SDmitry Chagin
151e16fe1c7SDmitry Chagin /* flags related to how event is registered */
152e16fe1c7SDmitry Chagin if ((levents & LINUX_EPOLLONESHOT) != 0)
153461120b8SVladimir Kondratyev kev_flags |= EV_DISPATCH;
154e16fe1c7SDmitry Chagin if ((levents & LINUX_EPOLLET) != 0)
155461120b8SVladimir Kondratyev kev_flags |= EV_CLEAR;
15676672e11SDmitry Chagin if ((levents & LINUX_EPOLLERR) != 0)
157461120b8SVladimir Kondratyev kev_flags |= EV_ERROR;
1583c91646bSDmitry Chagin if ((levents & LINUX_EPOLLRDHUP) != 0)
159461120b8SVladimir Kondratyev kev_flags |= EV_EOF;
160e16fe1c7SDmitry Chagin
161e16fe1c7SDmitry Chagin /* flags related to what event is registered */
162e16fe1c7SDmitry Chagin if ((levents & LINUX_EPOLL_EVRD) != 0) {
163b3c6fe66SVladimir Kondratyev EV_SET(kevent, fd, EVFILT_READ, kev_flags, 0, 0, 0);
164b3c6fe66SVladimir Kondratyev kevent->ext[0] = l_event->data;
165b3c6fe66SVladimir Kondratyev ++kevent;
166e16fe1c7SDmitry Chagin ++(*nkevents);
167e16fe1c7SDmitry Chagin }
168e16fe1c7SDmitry Chagin if ((levents & LINUX_EPOLL_EVWR) != 0) {
169b3c6fe66SVladimir Kondratyev EV_SET(kevent, fd, EVFILT_WRITE, kev_flags, 0, 0, 0);
170b3c6fe66SVladimir Kondratyev kevent->ext[0] = l_event->data;
171b3c6fe66SVladimir Kondratyev ++kevent;
172e16fe1c7SDmitry Chagin ++(*nkevents);
173e16fe1c7SDmitry Chagin }
174335fe0afSVladimir Kondratyev /* zero event mask is legal */
175335fe0afSVladimir Kondratyev if ((levents & (LINUX_EPOLL_EVRD | LINUX_EPOLL_EVWR)) == 0) {
176335fe0afSVladimir Kondratyev EV_SET(kevent++, fd, EVFILT_READ, EV_ADD|EV_DISABLE, 0, 0, 0);
177335fe0afSVladimir Kondratyev ++(*nkevents);
178335fe0afSVladimir Kondratyev }
179e16fe1c7SDmitry Chagin
180e16fe1c7SDmitry Chagin if ((levents & ~(LINUX_EPOLL_EVSUP)) != 0) {
181e16fe1c7SDmitry Chagin p = td->td_proc;
182e16fe1c7SDmitry Chagin
183e16fe1c7SDmitry Chagin pem = pem_find(p);
184e16fe1c7SDmitry Chagin KASSERT(pem != NULL, ("epoll proc emuldata not found.\n"));
185e16fe1c7SDmitry Chagin
186e16fe1c7SDmitry Chagin LINUX_PEM_XLOCK(pem);
187e16fe1c7SDmitry Chagin if ((pem->flags & LINUX_XUNSUP_EPOLL) == 0) {
188e16fe1c7SDmitry Chagin pem->flags |= LINUX_XUNSUP_EPOLL;
189e16fe1c7SDmitry Chagin LINUX_PEM_XUNLOCK(pem);
19086e794ebSEdward Tomasz Napierala linux_msg(td, "epoll_ctl unsupported flags: 0x%x",
191e16fe1c7SDmitry Chagin levents);
192e16fe1c7SDmitry Chagin } else
193e16fe1c7SDmitry Chagin LINUX_PEM_XUNLOCK(pem);
194e16fe1c7SDmitry Chagin return (EINVAL);
195e16fe1c7SDmitry Chagin }
196e16fe1c7SDmitry Chagin
197e16fe1c7SDmitry Chagin return (0);
198e16fe1c7SDmitry Chagin }
199e16fe1c7SDmitry Chagin
200e16fe1c7SDmitry Chagin /*
201e16fe1c7SDmitry Chagin * Structure converting function from kevent to epoll. In a case
202e16fe1c7SDmitry Chagin * this is called on error in registration we store the error in
203e16fe1c7SDmitry Chagin * event->data and pick it up later in linux_epoll_ctl().
204e16fe1c7SDmitry Chagin */
205e16fe1c7SDmitry Chagin static void
kevent_to_epoll(struct kevent * kevent,struct epoll_event * l_event)206e16fe1c7SDmitry Chagin kevent_to_epoll(struct kevent *kevent, struct epoll_event *l_event)
207e16fe1c7SDmitry Chagin {
208e16fe1c7SDmitry Chagin
209b3c6fe66SVladimir Kondratyev l_event->data = kevent->ext[0];
210b3c6fe66SVladimir Kondratyev
21176672e11SDmitry Chagin if ((kevent->flags & EV_ERROR) != 0) {
21276672e11SDmitry Chagin l_event->events = LINUX_EPOLLERR;
213e16fe1c7SDmitry Chagin return;
21476672e11SDmitry Chagin }
215e16fe1c7SDmitry Chagin
21629b1ecbeSDmitry Chagin /* XXX EPOLLPRI, EPOLLHUP */
217e16fe1c7SDmitry Chagin switch (kevent->filter) {
218e16fe1c7SDmitry Chagin case EVFILT_READ:
21929b1ecbeSDmitry Chagin l_event->events = LINUX_EPOLLIN;
2203c91646bSDmitry Chagin if ((kevent->flags & EV_EOF) != 0)
2213c91646bSDmitry Chagin l_event->events |= LINUX_EPOLLRDHUP;
222e16fe1c7SDmitry Chagin break;
223e16fe1c7SDmitry Chagin case EVFILT_WRITE:
22429b1ecbeSDmitry Chagin l_event->events = LINUX_EPOLLOUT;
225e16fe1c7SDmitry Chagin break;
226e16fe1c7SDmitry Chagin }
227e16fe1c7SDmitry Chagin }
228e16fe1c7SDmitry Chagin
229e16fe1c7SDmitry Chagin /*
230e16fe1c7SDmitry Chagin * Copyout callback used by kevent. This converts kevent
231e16fe1c7SDmitry Chagin * events to epoll events and copies them back to the
232e16fe1c7SDmitry Chagin * userspace. This is also called on error on registering
233e16fe1c7SDmitry Chagin * of the filter.
234e16fe1c7SDmitry Chagin */
235e16fe1c7SDmitry Chagin static int
epoll_kev_copyout(void * arg,struct kevent * kevp,int count)236e16fe1c7SDmitry Chagin epoll_kev_copyout(void *arg, struct kevent *kevp, int count)
237e16fe1c7SDmitry Chagin {
238e16fe1c7SDmitry Chagin struct epoll_copyout_args *args;
239e16fe1c7SDmitry Chagin struct epoll_event *eep;
240b3c6fe66SVladimir Kondratyev int error, i;
241e16fe1c7SDmitry Chagin
242e16fe1c7SDmitry Chagin args = (struct epoll_copyout_args*) arg;
243e16fe1c7SDmitry Chagin eep = malloc(sizeof(*eep) * count, M_EPOLL, M_WAITOK | M_ZERO);
244e16fe1c7SDmitry Chagin
245b3c6fe66SVladimir Kondratyev for (i = 0; i < count; i++)
246e16fe1c7SDmitry Chagin kevent_to_epoll(&kevp[i], &eep[i]);
247e16fe1c7SDmitry Chagin
248e16fe1c7SDmitry Chagin error = copyout(eep, args->leventlist, count * sizeof(*eep));
249e16fe1c7SDmitry Chagin if (error == 0) {
250e16fe1c7SDmitry Chagin args->leventlist += count;
251e16fe1c7SDmitry Chagin args->count += count;
252e16fe1c7SDmitry Chagin } else if (args->error == 0)
253e16fe1c7SDmitry Chagin args->error = error;
254e16fe1c7SDmitry Chagin
255e16fe1c7SDmitry Chagin free(eep, M_EPOLL);
256e16fe1c7SDmitry Chagin return (error);
257e16fe1c7SDmitry Chagin }
258e16fe1c7SDmitry Chagin
259e16fe1c7SDmitry Chagin /*
260e16fe1c7SDmitry Chagin * Copyin callback used by kevent. This copies already
261e16fe1c7SDmitry Chagin * converted filters from kernel memory to the kevent
262e16fe1c7SDmitry Chagin * internal kernel memory. Hence the memcpy instead of
263e16fe1c7SDmitry Chagin * copyin.
264e16fe1c7SDmitry Chagin */
265e16fe1c7SDmitry Chagin static int
epoll_kev_copyin(void * arg,struct kevent * kevp,int count)266e16fe1c7SDmitry Chagin epoll_kev_copyin(void *arg, struct kevent *kevp, int count)
267e16fe1c7SDmitry Chagin {
268e16fe1c7SDmitry Chagin struct epoll_copyin_args *args;
269e16fe1c7SDmitry Chagin
270e16fe1c7SDmitry Chagin args = (struct epoll_copyin_args*) arg;
271e16fe1c7SDmitry Chagin
272e16fe1c7SDmitry Chagin memcpy(kevp, args->changelist, count * sizeof(*kevp));
273e16fe1c7SDmitry Chagin args->changelist += count;
274e16fe1c7SDmitry Chagin
275e16fe1c7SDmitry Chagin return (0);
276e16fe1c7SDmitry Chagin }
277e16fe1c7SDmitry Chagin
278e16fe1c7SDmitry Chagin /*
279e16fe1c7SDmitry Chagin * Load epoll filter, convert it to kevent filter
280e16fe1c7SDmitry Chagin * and load it into kevent subsystem.
281e16fe1c7SDmitry Chagin */
282e16fe1c7SDmitry Chagin int
linux_epoll_ctl(struct thread * td,struct linux_epoll_ctl_args * args)283e16fe1c7SDmitry Chagin linux_epoll_ctl(struct thread *td, struct linux_epoll_ctl_args *args)
284e16fe1c7SDmitry Chagin {
285e16fe1c7SDmitry Chagin struct file *epfp, *fp;
286e16fe1c7SDmitry Chagin struct epoll_copyin_args ciargs;
287e16fe1c7SDmitry Chagin struct kevent kev[2];
288e16fe1c7SDmitry Chagin struct kevent_copyops k_ops = { &ciargs,
289e16fe1c7SDmitry Chagin NULL,
290e16fe1c7SDmitry Chagin epoll_kev_copyin};
291e16fe1c7SDmitry Chagin struct epoll_event le;
292e16fe1c7SDmitry Chagin cap_rights_t rights;
293e16fe1c7SDmitry Chagin int nchanges = 0;
294e16fe1c7SDmitry Chagin int error;
295e16fe1c7SDmitry Chagin
296e16fe1c7SDmitry Chagin if (args->op != LINUX_EPOLL_CTL_DEL) {
297e16fe1c7SDmitry Chagin error = copyin(args->event, &le, sizeof(le));
298e16fe1c7SDmitry Chagin if (error != 0)
299e16fe1c7SDmitry Chagin return (error);
300e16fe1c7SDmitry Chagin }
301e16fe1c7SDmitry Chagin
302e16fe1c7SDmitry Chagin error = fget(td, args->epfd,
3036b3a9a0fSMateusz Guzik cap_rights_init_one(&rights, CAP_KQUEUE_CHANGE), &epfp);
304e16fe1c7SDmitry Chagin if (error != 0)
305e16fe1c7SDmitry Chagin return (error);
306e0a254f6SDmitry Chagin if (epfp->f_type != DTYPE_KQUEUE) {
307e0a254f6SDmitry Chagin error = EINVAL;
308e16fe1c7SDmitry Chagin goto leave1;
309e0a254f6SDmitry Chagin }
310e16fe1c7SDmitry Chagin
311e16fe1c7SDmitry Chagin /* Protect user data vector from incorrectly supplied fd. */
3126b3a9a0fSMateusz Guzik error = fget(td, args->fd,
3136b3a9a0fSMateusz Guzik cap_rights_init_one(&rights, CAP_POLL_EVENT), &fp);
314e16fe1c7SDmitry Chagin if (error != 0)
315e16fe1c7SDmitry Chagin goto leave1;
316e16fe1c7SDmitry Chagin
317e16fe1c7SDmitry Chagin /* Linux disallows spying on himself */
318e16fe1c7SDmitry Chagin if (epfp == fp) {
319e16fe1c7SDmitry Chagin error = EINVAL;
320e16fe1c7SDmitry Chagin goto leave0;
321e16fe1c7SDmitry Chagin }
322e16fe1c7SDmitry Chagin
323e16fe1c7SDmitry Chagin ciargs.changelist = kev;
324e16fe1c7SDmitry Chagin
3250633df29SDmitry Chagin if (args->op != LINUX_EPOLL_CTL_DEL) {
326461120b8SVladimir Kondratyev error = epoll_to_kevent(td, args->fd, &le, kev, &nchanges);
3270633df29SDmitry Chagin if (error != 0)
3280633df29SDmitry Chagin goto leave0;
3290633df29SDmitry Chagin }
3300633df29SDmitry Chagin
331e16fe1c7SDmitry Chagin switch (args->op) {
332e16fe1c7SDmitry Chagin case LINUX_EPOLL_CTL_MOD:
333e16fe1c7SDmitry Chagin error = epoll_delete_all_events(td, epfp, args->fd);
33401d4a346SDmitry Chagin if (error != 0)
335e16fe1c7SDmitry Chagin goto leave0;
3360633df29SDmitry Chagin break;
337e16fe1c7SDmitry Chagin
338e16fe1c7SDmitry Chagin case LINUX_EPOLL_CTL_ADD:
339461120b8SVladimir Kondratyev if (epoll_fd_registered(td, epfp, args->fd)) {
3400633df29SDmitry Chagin error = EEXIST;
3410633df29SDmitry Chagin goto leave0;
3420633df29SDmitry Chagin }
343e16fe1c7SDmitry Chagin break;
344e16fe1c7SDmitry Chagin
345e16fe1c7SDmitry Chagin case LINUX_EPOLL_CTL_DEL:
346e16fe1c7SDmitry Chagin /* CTL_DEL means unregister this fd with this epoll */
347e16fe1c7SDmitry Chagin error = epoll_delete_all_events(td, epfp, args->fd);
348e16fe1c7SDmitry Chagin goto leave0;
349e16fe1c7SDmitry Chagin
350e16fe1c7SDmitry Chagin default:
351e16fe1c7SDmitry Chagin error = EINVAL;
352e16fe1c7SDmitry Chagin goto leave0;
353e16fe1c7SDmitry Chagin }
354e16fe1c7SDmitry Chagin
355e16fe1c7SDmitry Chagin error = kern_kevent_fp(td, epfp, nchanges, 0, &k_ops, NULL);
356e16fe1c7SDmitry Chagin
357e16fe1c7SDmitry Chagin leave0:
358e16fe1c7SDmitry Chagin fdrop(fp, td);
359e16fe1c7SDmitry Chagin
360e16fe1c7SDmitry Chagin leave1:
361e16fe1c7SDmitry Chagin fdrop(epfp, td);
362e16fe1c7SDmitry Chagin return (error);
363e16fe1c7SDmitry Chagin }
364e16fe1c7SDmitry Chagin
365e16fe1c7SDmitry Chagin /*
366e16fe1c7SDmitry Chagin * Wait for a filter to be triggered on the epoll file descriptor.
367e16fe1c7SDmitry Chagin */
36827a25179SDmitry Chagin
3696e4c8004SDmitry Chagin static int
linux_epoll_wait_ts(struct thread * td,int epfd,struct epoll_event * events,int maxevents,struct timespec * tsp,sigset_t * uset)37027a25179SDmitry Chagin linux_epoll_wait_ts(struct thread *td, int epfd, struct epoll_event *events,
37127a25179SDmitry Chagin int maxevents, struct timespec *tsp, sigset_t *uset)
372e16fe1c7SDmitry Chagin {
373e16fe1c7SDmitry Chagin struct epoll_copyout_args coargs;
374e16fe1c7SDmitry Chagin struct kevent_copyops k_ops = { &coargs,
375e16fe1c7SDmitry Chagin epoll_kev_copyout,
376e16fe1c7SDmitry Chagin NULL};
37780c7315dSDmitry Chagin cap_rights_t rights;
37880c7315dSDmitry Chagin struct file *epfp;
37980c7315dSDmitry Chagin sigset_t omask;
380e16fe1c7SDmitry Chagin int error;
381e16fe1c7SDmitry Chagin
3826e4c8004SDmitry Chagin if (maxevents <= 0 || maxevents > LINUX_MAX_EVENTS)
383e16fe1c7SDmitry Chagin return (EINVAL);
384e16fe1c7SDmitry Chagin
38580c7315dSDmitry Chagin error = fget(td, epfd,
3866b3a9a0fSMateusz Guzik cap_rights_init_one(&rights, CAP_KQUEUE_EVENT), &epfp);
3876e4c8004SDmitry Chagin if (error != 0)
3886e4c8004SDmitry Chagin return (error);
38980c7315dSDmitry Chagin if (epfp->f_type != DTYPE_KQUEUE) {
39080c7315dSDmitry Chagin error = EINVAL;
39171b8e362SVladimir Kondratyev goto leave;
39280c7315dSDmitry Chagin }
39380c7315dSDmitry Chagin if (uset != NULL) {
39480c7315dSDmitry Chagin error = kern_sigprocmask(td, SIG_SETMASK, uset,
39580c7315dSDmitry Chagin &omask, 0);
39680c7315dSDmitry Chagin if (error != 0)
39771b8e362SVladimir Kondratyev goto leave;
3986e4c8004SDmitry Chagin td->td_pflags |= TDP_OLDMASK;
3996e4c8004SDmitry Chagin /*
4006e4c8004SDmitry Chagin * Make sure that ast() is called on return to
4016e4c8004SDmitry Chagin * usermode and TDP_OLDMASK is cleared, restoring old
4026e4c8004SDmitry Chagin * sigmask.
4036e4c8004SDmitry Chagin */
404c6d31b83SKonstantin Belousov ast_sched(td, TDA_SIGSUSPEND);
4056e4c8004SDmitry Chagin }
4066e4c8004SDmitry Chagin
4076e4c8004SDmitry Chagin coargs.leventlist = events;
408e16fe1c7SDmitry Chagin coargs.p = td->td_proc;
409e16fe1c7SDmitry Chagin coargs.count = 0;
410e16fe1c7SDmitry Chagin coargs.error = 0;
411e16fe1c7SDmitry Chagin
4126e4c8004SDmitry Chagin error = kern_kevent_fp(td, epfp, 0, maxevents, &k_ops, tsp);
413e16fe1c7SDmitry Chagin if (error == 0 && coargs.error != 0)
414e16fe1c7SDmitry Chagin error = coargs.error;
415e16fe1c7SDmitry Chagin
416e16fe1c7SDmitry Chagin /*
417e16fe1c7SDmitry Chagin * kern_kevent might return ENOMEM which is not expected from epoll_wait.
418e16fe1c7SDmitry Chagin * Maybe we should translate that but I don't think it matters at all.
419e16fe1c7SDmitry Chagin */
420e16fe1c7SDmitry Chagin if (error == 0)
421e16fe1c7SDmitry Chagin td->td_retval[0] = coargs.count;
42280c7315dSDmitry Chagin
42380c7315dSDmitry Chagin if (uset != NULL)
42480c7315dSDmitry Chagin error = kern_sigprocmask(td, SIG_SETMASK, &omask,
42580c7315dSDmitry Chagin NULL, 0);
42671b8e362SVladimir Kondratyev leave:
427e16fe1c7SDmitry Chagin fdrop(epfp, td);
428e16fe1c7SDmitry Chagin return (error);
429e16fe1c7SDmitry Chagin }
430e16fe1c7SDmitry Chagin
43127a25179SDmitry Chagin static int
linux_epoll_wait_common(struct thread * td,int epfd,struct epoll_event * events,int maxevents,int timeout,sigset_t * uset)43227a25179SDmitry Chagin linux_epoll_wait_common(struct thread *td, int epfd, struct epoll_event *events,
43327a25179SDmitry Chagin int maxevents, int timeout, sigset_t *uset)
43427a25179SDmitry Chagin {
43527a25179SDmitry Chagin struct timespec ts, *tsp;
43627a25179SDmitry Chagin
43727a25179SDmitry Chagin /*
43827a25179SDmitry Chagin * Linux epoll_wait(2) man page states that timeout of -1 causes caller
43927a25179SDmitry Chagin * to block indefinitely. Real implementation does it if any negative
44027a25179SDmitry Chagin * timeout value is passed.
44127a25179SDmitry Chagin */
44227a25179SDmitry Chagin if (timeout >= 0) {
44327a25179SDmitry Chagin /* Convert from milliseconds to timespec. */
44427a25179SDmitry Chagin ts.tv_sec = timeout / 1000;
44527a25179SDmitry Chagin ts.tv_nsec = (timeout % 1000) * 1000000;
44627a25179SDmitry Chagin tsp = &ts;
44727a25179SDmitry Chagin } else {
44827a25179SDmitry Chagin tsp = NULL;
44927a25179SDmitry Chagin }
45027a25179SDmitry Chagin return (linux_epoll_wait_ts(td, epfd, events, maxevents, tsp, uset));
45127a25179SDmitry Chagin
45227a25179SDmitry Chagin }
45327a25179SDmitry Chagin
454931e2a1aSEd Maste #ifdef LINUX_LEGACY_SYSCALLS
4556e4c8004SDmitry Chagin int
linux_epoll_wait(struct thread * td,struct linux_epoll_wait_args * args)4566e4c8004SDmitry Chagin linux_epoll_wait(struct thread *td, struct linux_epoll_wait_args *args)
4576e4c8004SDmitry Chagin {
4586e4c8004SDmitry Chagin
4596e4c8004SDmitry Chagin return (linux_epoll_wait_common(td, args->epfd, args->events,
4606e4c8004SDmitry Chagin args->maxevents, args->timeout, NULL));
4616e4c8004SDmitry Chagin }
462931e2a1aSEd Maste #endif
4636e4c8004SDmitry Chagin
4646e4c8004SDmitry Chagin int
linux_epoll_pwait(struct thread * td,struct linux_epoll_pwait_args * args)4656e4c8004SDmitry Chagin linux_epoll_pwait(struct thread *td, struct linux_epoll_pwait_args *args)
4666e4c8004SDmitry Chagin {
4676e4c8004SDmitry Chagin sigset_t mask, *pmask;
4686e4c8004SDmitry Chagin int error;
4696e4c8004SDmitry Chagin
47093107373SDmitry Chagin error = linux_copyin_sigset(td, args->mask, sizeof(l_sigset_t),
4713923e632SDmitry Chagin &mask, &pmask);
4726e4c8004SDmitry Chagin if (error != 0)
4736e4c8004SDmitry Chagin return (error);
4743923e632SDmitry Chagin
4756e4c8004SDmitry Chagin return (linux_epoll_wait_common(td, args->epfd, args->events,
4766e4c8004SDmitry Chagin args->maxevents, args->timeout, pmask));
4776e4c8004SDmitry Chagin }
4786e4c8004SDmitry Chagin
479e00aad10SDmitry Chagin #if defined(__i386__) || (defined(__amd64__) && defined(COMPAT_LINUX32))
480e00aad10SDmitry Chagin int
linux_epoll_pwait2_64(struct thread * td,struct linux_epoll_pwait2_64_args * args)481e00aad10SDmitry Chagin linux_epoll_pwait2_64(struct thread *td, struct linux_epoll_pwait2_64_args *args)
482e00aad10SDmitry Chagin {
483e00aad10SDmitry Chagin struct timespec ts, *tsa;
484e00aad10SDmitry Chagin sigset_t mask, *pmask;
485e00aad10SDmitry Chagin int error;
486e00aad10SDmitry Chagin
48793107373SDmitry Chagin error = linux_copyin_sigset(td, args->mask, sizeof(l_sigset_t),
488e00aad10SDmitry Chagin &mask, &pmask);
489e00aad10SDmitry Chagin if (error != 0)
490e00aad10SDmitry Chagin return (error);
491e00aad10SDmitry Chagin
492e00aad10SDmitry Chagin if (args->timeout) {
493707e567aSDmitry Chagin error = linux_get_timespec64(&ts, args->timeout);
494e00aad10SDmitry Chagin if (error != 0)
495e00aad10SDmitry Chagin return (error);
496e00aad10SDmitry Chagin tsa = &ts;
497e00aad10SDmitry Chagin } else
498e00aad10SDmitry Chagin tsa = NULL;
499e00aad10SDmitry Chagin
500e00aad10SDmitry Chagin return (linux_epoll_wait_ts(td, args->epfd, args->events,
501e00aad10SDmitry Chagin args->maxevents, tsa, pmask));
502e00aad10SDmitry Chagin }
503e00aad10SDmitry Chagin #else
504e00aad10SDmitry Chagin int
linux_epoll_pwait2(struct thread * td,struct linux_epoll_pwait2_args * args)505e00aad10SDmitry Chagin linux_epoll_pwait2(struct thread *td, struct linux_epoll_pwait2_args *args)
506e00aad10SDmitry Chagin {
507e00aad10SDmitry Chagin struct timespec ts, *tsa;
508e00aad10SDmitry Chagin sigset_t mask, *pmask;
509e00aad10SDmitry Chagin int error;
510e00aad10SDmitry Chagin
51193107373SDmitry Chagin error = linux_copyin_sigset(td, args->mask, sizeof(l_sigset_t),
512e00aad10SDmitry Chagin &mask, &pmask);
513e00aad10SDmitry Chagin if (error != 0)
514e00aad10SDmitry Chagin return (error);
515e00aad10SDmitry Chagin
516e00aad10SDmitry Chagin if (args->timeout) {
517707e567aSDmitry Chagin error = linux_get_timespec(&ts, args->timeout);
518e00aad10SDmitry Chagin if (error != 0)
519e00aad10SDmitry Chagin return (error);
520e00aad10SDmitry Chagin tsa = &ts;
521e00aad10SDmitry Chagin } else
522e00aad10SDmitry Chagin tsa = NULL;
523e00aad10SDmitry Chagin
524e00aad10SDmitry Chagin return (linux_epoll_wait_ts(td, args->epfd, args->events,
525e00aad10SDmitry Chagin args->maxevents, tsa, pmask));
526e00aad10SDmitry Chagin }
527e00aad10SDmitry Chagin #endif /* __i386__ || (__amd64__ && COMPAT_LINUX32) */
528e00aad10SDmitry Chagin
529e16fe1c7SDmitry Chagin static int
epoll_register_kevent(struct thread * td,struct file * epfp,int fd,int filter,unsigned int flags)530461120b8SVladimir Kondratyev epoll_register_kevent(struct thread *td, struct file *epfp, int fd, int filter,
531461120b8SVladimir Kondratyev unsigned int flags)
532e16fe1c7SDmitry Chagin {
533e16fe1c7SDmitry Chagin struct epoll_copyin_args ciargs;
534e16fe1c7SDmitry Chagin struct kevent kev;
535e16fe1c7SDmitry Chagin struct kevent_copyops k_ops = { &ciargs,
536e16fe1c7SDmitry Chagin NULL,
537e16fe1c7SDmitry Chagin epoll_kev_copyin};
538e16fe1c7SDmitry Chagin
539e16fe1c7SDmitry Chagin ciargs.changelist = &kev;
540461120b8SVladimir Kondratyev EV_SET(&kev, fd, filter, flags, 0, 0, 0);
541e16fe1c7SDmitry Chagin
5428bc4edfdSDmitry Chagin return (kern_kevent_fp(td, epfp, 1, 0, &k_ops, NULL));
543e16fe1c7SDmitry Chagin }
544e16fe1c7SDmitry Chagin
545e16fe1c7SDmitry Chagin static int
epoll_fd_registered(struct thread * td,struct file * epfp,int fd)546461120b8SVladimir Kondratyev epoll_fd_registered(struct thread *td, struct file *epfp, int fd)
547461120b8SVladimir Kondratyev {
548461120b8SVladimir Kondratyev /*
549461120b8SVladimir Kondratyev * Set empty filter flags to avoid accidental modification of already
550461120b8SVladimir Kondratyev * registered events. In the case of event re-registration:
551461120b8SVladimir Kondratyev * 1. If event does not exists kevent() does nothing and returns ENOENT
552461120b8SVladimir Kondratyev * 2. If event does exists, it's enabled/disabled state is preserved
553461120b8SVladimir Kondratyev * but fflags, data and udata fields are overwritten. So we can not
554461120b8SVladimir Kondratyev * set socket lowats and store user's context pointer in udata.
555461120b8SVladimir Kondratyev */
556461120b8SVladimir Kondratyev if (epoll_register_kevent(td, epfp, fd, EVFILT_READ, 0) != ENOENT ||
557461120b8SVladimir Kondratyev epoll_register_kevent(td, epfp, fd, EVFILT_WRITE, 0) != ENOENT)
558461120b8SVladimir Kondratyev return (1);
559461120b8SVladimir Kondratyev
560461120b8SVladimir Kondratyev return (0);
561461120b8SVladimir Kondratyev }
562461120b8SVladimir Kondratyev
563461120b8SVladimir Kondratyev static int
epoll_delete_all_events(struct thread * td,struct file * epfp,int fd)564e16fe1c7SDmitry Chagin epoll_delete_all_events(struct thread *td, struct file *epfp, int fd)
565e16fe1c7SDmitry Chagin {
566e16fe1c7SDmitry Chagin int error1, error2;
567e16fe1c7SDmitry Chagin
568461120b8SVladimir Kondratyev error1 = epoll_register_kevent(td, epfp, fd, EVFILT_READ, EV_DELETE);
569461120b8SVladimir Kondratyev error2 = epoll_register_kevent(td, epfp, fd, EVFILT_WRITE, EV_DELETE);
570e16fe1c7SDmitry Chagin
5718bc4edfdSDmitry Chagin /* return 0 if at least one result positive */
5728bc4edfdSDmitry Chagin return (error1 == 0 ? 0 : error2);
573e16fe1c7SDmitry Chagin }
574a31d7686SDmitry Chagin
575931e2a1aSEd Maste #ifdef LINUX_LEGACY_SYSCALLS
576a31d7686SDmitry Chagin int
linux_eventfd(struct thread * td,struct linux_eventfd_args * args)577a31d7686SDmitry Chagin linux_eventfd(struct thread *td, struct linux_eventfd_args *args)
578a31d7686SDmitry Chagin {
5797a202823SKonstantin Belousov struct specialfd_eventfd ae;
580a31d7686SDmitry Chagin
5817a202823SKonstantin Belousov bzero(&ae, sizeof(ae));
5827a202823SKonstantin Belousov ae.initval = args->initval;
5837a202823SKonstantin Belousov return (kern_specialfd(td, SPECIALFD_EVENTFD, &ae));
584a31d7686SDmitry Chagin }
585931e2a1aSEd Maste #endif
586a31d7686SDmitry Chagin
587a31d7686SDmitry Chagin int
linux_eventfd2(struct thread * td,struct linux_eventfd2_args * args)588a31d7686SDmitry Chagin linux_eventfd2(struct thread *td, struct linux_eventfd2_args *args)
589a31d7686SDmitry Chagin {
5907a202823SKonstantin Belousov struct specialfd_eventfd ae;
5917a202823SKonstantin Belousov int flags;
592a31d7686SDmitry Chagin
5937a202823SKonstantin Belousov if ((args->flags & ~(LINUX_O_CLOEXEC | LINUX_O_NONBLOCK |
5947a202823SKonstantin Belousov LINUX_EFD_SEMAPHORE)) != 0)
595a31d7686SDmitry Chagin return (EINVAL);
5967a202823SKonstantin Belousov flags = 0;
5977a202823SKonstantin Belousov if ((args->flags & LINUX_O_CLOEXEC) != 0)
5987a202823SKonstantin Belousov flags |= EFD_CLOEXEC;
5997a202823SKonstantin Belousov if ((args->flags & LINUX_O_NONBLOCK) != 0)
6007a202823SKonstantin Belousov flags |= EFD_NONBLOCK;
6017a202823SKonstantin Belousov if ((args->flags & LINUX_EFD_SEMAPHORE) != 0)
6027a202823SKonstantin Belousov flags |= EFD_SEMAPHORE;
603a31d7686SDmitry Chagin
6047a202823SKonstantin Belousov bzero(&ae, sizeof(ae));
6057a202823SKonstantin Belousov ae.flags = flags;
6067a202823SKonstantin Belousov ae.initval = args->initval;
6077a202823SKonstantin Belousov return (kern_specialfd(td, SPECIALFD_EVENTFD, &ae));
608e2ff4b98SDmitry Chagin }
609dd93b628SDmitry Chagin
610dd93b628SDmitry Chagin int
linux_timerfd_create(struct thread * td,struct linux_timerfd_create_args * args)611dd93b628SDmitry Chagin linux_timerfd_create(struct thread *td, struct linux_timerfd_create_args *args)
612dd93b628SDmitry Chagin {
613dd93b628SDmitry Chagin clockid_t clockid;
614*aadc14bcSVico Chen int error, flags;
615dd93b628SDmitry Chagin
616dd93b628SDmitry Chagin error = linux_to_native_clockid(&clockid, args->clockid);
617dd93b628SDmitry Chagin if (error != 0)
618dd93b628SDmitry Chagin return (error);
619*aadc14bcSVico Chen flags = 0;
620*aadc14bcSVico Chen if ((args->flags & LINUX_TFD_CLOEXEC) != 0)
621*aadc14bcSVico Chen flags |= O_CLOEXEC;
622*aadc14bcSVico Chen if ((args->flags & LINUX_TFD_NONBLOCK) != 0)
623*aadc14bcSVico Chen flags |= TFD_NONBLOCK;
624dd93b628SDmitry Chagin
625*aadc14bcSVico Chen return (kern_timerfd_create(td, clockid, flags));
626dd93b628SDmitry Chagin }
627dd93b628SDmitry Chagin
628ce9f8d6aSDmitry Chagin int
linux_timerfd_gettime(struct thread * td,struct linux_timerfd_gettime_args * args)629ce9f8d6aSDmitry Chagin linux_timerfd_gettime(struct thread *td, struct linux_timerfd_gettime_args *args)
630ce9f8d6aSDmitry Chagin {
631ce9f8d6aSDmitry Chagin struct l_itimerspec lots;
632ce9f8d6aSDmitry Chagin struct itimerspec ots;
633ce9f8d6aSDmitry Chagin int error;
634ce9f8d6aSDmitry Chagin
635af93fea7SJake Freeland error = kern_timerfd_gettime(td, args->fd, &ots);
636ce9f8d6aSDmitry Chagin if (error != 0)
637ce9f8d6aSDmitry Chagin return (error);
638af93fea7SJake Freeland
639ce9f8d6aSDmitry Chagin error = native_to_linux_itimerspec(&lots, &ots);
640ce9f8d6aSDmitry Chagin if (error == 0)
641ce9f8d6aSDmitry Chagin error = copyout(&lots, args->old_value, sizeof(lots));
642af93fea7SJake Freeland
643af93fea7SJake Freeland return (error);
644af93fea7SJake Freeland }
645af93fea7SJake Freeland
646af93fea7SJake Freeland int
linux_timerfd_settime(struct thread * td,struct linux_timerfd_settime_args * args)647af93fea7SJake Freeland linux_timerfd_settime(struct thread *td, struct linux_timerfd_settime_args *args)
648af93fea7SJake Freeland {
649af93fea7SJake Freeland struct l_itimerspec lots;
650af93fea7SJake Freeland struct itimerspec nts, ots;
651af93fea7SJake Freeland int error;
652af93fea7SJake Freeland
653af93fea7SJake Freeland error = copyin(args->new_value, &lots, sizeof(lots));
654af93fea7SJake Freeland if (error != 0)
655af93fea7SJake Freeland return (error);
656af93fea7SJake Freeland error = linux_to_native_itimerspec(&nts, &lots);
657af93fea7SJake Freeland if (error != 0)
658af93fea7SJake Freeland return (error);
659af93fea7SJake Freeland if (args->old_value == NULL)
660af93fea7SJake Freeland error = kern_timerfd_settime(td, args->fd, args->flags, &nts, NULL);
661af93fea7SJake Freeland else
662af93fea7SJake Freeland error = kern_timerfd_settime(td, args->fd, args->flags, &nts, &ots);
663af93fea7SJake Freeland if (error == 0 && args->old_value != NULL) {
664af93fea7SJake Freeland error = native_to_linux_itimerspec(&lots, &ots);
665af93fea7SJake Freeland if (error == 0)
666af93fea7SJake Freeland error = copyout(&lots, args->old_value, sizeof(lots));
667af93fea7SJake Freeland }
668af93fea7SJake Freeland
669ce9f8d6aSDmitry Chagin return (error);
670ce9f8d6aSDmitry Chagin }
671ce9f8d6aSDmitry Chagin
672ce9f8d6aSDmitry Chagin #if defined(__i386__) || (defined(__amd64__) && defined(COMPAT_LINUX32))
673ce9f8d6aSDmitry Chagin int
linux_timerfd_gettime64(struct thread * td,struct linux_timerfd_gettime64_args * args)674ce9f8d6aSDmitry Chagin linux_timerfd_gettime64(struct thread *td, struct linux_timerfd_gettime64_args *args)
675ce9f8d6aSDmitry Chagin {
676ce9f8d6aSDmitry Chagin struct l_itimerspec64 lots;
677ce9f8d6aSDmitry Chagin struct itimerspec ots;
678ce9f8d6aSDmitry Chagin int error;
679ce9f8d6aSDmitry Chagin
680af93fea7SJake Freeland error = kern_timerfd_gettime(td, args->fd, &ots);
681ce9f8d6aSDmitry Chagin if (error != 0)
682ce9f8d6aSDmitry Chagin return (error);
683af93fea7SJake Freeland
684ce9f8d6aSDmitry Chagin error = native_to_linux_itimerspec64(&lots, &ots);
685ce9f8d6aSDmitry Chagin if (error == 0)
686ce9f8d6aSDmitry Chagin error = copyout(&lots, args->old_value, sizeof(lots));
687ce9f8d6aSDmitry Chagin
688dd93b628SDmitry Chagin return (error);
689dd93b628SDmitry Chagin }
690dd93b628SDmitry Chagin
691b1f0b08dSDmitry Chagin int
linux_timerfd_settime64(struct thread * td,struct linux_timerfd_settime64_args * args)692b1f0b08dSDmitry Chagin linux_timerfd_settime64(struct thread *td, struct linux_timerfd_settime64_args *args)
693b1f0b08dSDmitry Chagin {
694b1f0b08dSDmitry Chagin struct l_itimerspec64 lots;
695af93fea7SJake Freeland struct itimerspec nts, ots;
696b1f0b08dSDmitry Chagin int error;
697b1f0b08dSDmitry Chagin
698b1f0b08dSDmitry Chagin error = copyin(args->new_value, &lots, sizeof(lots));
699b1f0b08dSDmitry Chagin if (error != 0)
700b1f0b08dSDmitry Chagin return (error);
701b1f0b08dSDmitry Chagin error = linux_to_native_itimerspec64(&nts, &lots);
702b1f0b08dSDmitry Chagin if (error != 0)
703b1f0b08dSDmitry Chagin return (error);
704af93fea7SJake Freeland if (args->old_value == NULL)
705af93fea7SJake Freeland error = kern_timerfd_settime(td, args->fd, args->flags, &nts, NULL);
706af93fea7SJake Freeland else
707af93fea7SJake Freeland error = kern_timerfd_settime(td, args->fd, args->flags, &nts, &ots);
708b1f0b08dSDmitry Chagin if (error == 0 && args->old_value != NULL) {
709b1f0b08dSDmitry Chagin error = native_to_linux_itimerspec64(&lots, &ots);
710b1f0b08dSDmitry Chagin if (error == 0)
711b1f0b08dSDmitry Chagin error = copyout(&lots, args->old_value, sizeof(lots));
712b1f0b08dSDmitry Chagin }
713af93fea7SJake Freeland
714b1f0b08dSDmitry Chagin return (error);
715b1f0b08dSDmitry Chagin }
716b1f0b08dSDmitry Chagin #endif
717