c19ca36a10ea4c4ab19758b6ff815379f8a24c8c
[dragonfly.git] / sys / emulation / linux / linux_epoll.c
1 /*-
2  * Copyright (c) 2007 Roman Divacky
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  */
26
27 #include "opt_compat.h"
28
29 #include <sys/param.h>
30 #include <sys/systm.h>
31 #include <sys/kern_syscall.h>
32 #include <sys/event.h>
33 #include <sys/lock.h>
34 #include <sys/mplock2.h>
35 #include <sys/malloc.h>
36 #include <sys/ptrace.h>
37 #include <sys/proc.h>
38 #include <sys/signalvar.h>
39 #include <sys/sysent.h>
40 #include <sys/sysproto.h>
41 #include <sys/file.h>
42
43 #include <vm/vm.h>
44 #include <vm/vm_param.h>
45 #include <vm/vm_page.h>
46 #include <vm/vm_extern.h>
47 #include <sys/exec.h>
48 #include <sys/kernel.h>
49 #include <sys/module.h>
50 #include <machine/cpu.h>
51
52 #include "i386/linux.h"
53 #include "i386/linux_proto.h"
54 #include "linux_signal.h"
55 #include "linux_util.h"
56 #include "linux_epoll.h"
57
58
59 /* Create a new epoll file descriptor. */
60 int
61 sys_linux_epoll_create(struct linux_epoll_create_args *args)
62 {
63         struct kqueue_args k_args;
64
65         if (args->size <= 0)
66                 return (EINVAL);
67         /* args->size is unused. Linux ignores it as well. */
68
69         return (sys_kqueue(&k_args));
70 }
71
72 /* Structure converting function from epoll to kevent. */
73 static void
74 linux_epoll_to_kevent(int fd, struct linux_epoll_event *event, struct kevent *kevent)
75 {
76         int filter = 0;
77         int flags = kevent->flags;
78
79         if (event->events & LINUX_EPOLLIN)
80                 filter |= EVFILT_READ;
81         if (event->events & LINUX_EPOLLOUT)
82                 filter |= EVFILT_WRITE;
83         if (event->events & LINUX_EPOLLPRI)
84                 filter |= EVFILT_READ;
85         if (event->events & LINUX_EPOLLET)
86                 flags |= EV_CLEAR;
87         if (event->events & LINUX_EPOLLONESHOT)
88                 flags |= EV_ONESHOT;
89
90         EV_SET(kevent, fd, filter, flags, 0, 0, NULL);
91 }
92
93 /*
94  * Structure converting function from kevent to epoll. In a case
95  * this is called on error in registration we store the error in
96  * event->data and pick it up later in linux_epoll_ctl().
97  */
98 static void
99 linux_kevent_to_epoll(struct kevent *kevent, struct linux_epoll_event *event)
100 {
101         if (kevent->flags & EV_ERROR) {
102                 event->data = kevent->data;
103                 return;
104         }
105         switch (kevent->filter) {
106                 case EVFILT_READ:
107                         if (kevent->data > 0)
108                                 event->events = LINUX_EPOLLIN;
109                         event->data = kevent->ident;
110                 break;
111                 case EVFILT_WRITE:
112                         if (kevent->data > 0)
113                                 event->events = LINUX_EPOLLOUT;
114                         event->data = kevent->ident;
115                 break;
116         }
117 }
118
119 /*
120  * Copyout callback used by kevent. This converts kevent
121  * events to epoll events and copies them back to the
122  * userspace. This is also called on error on registering
123  * of the filter.
124  */
125 static int
126 linux_kev_copyout(void *arg, struct kevent *kevp, int count, int *res)
127 {
128         struct kevent_args *uap;
129         struct linux_epoll_event *eep;
130         int error, i;
131
132         uap = (struct kevent_args*) arg;
133
134         eep = kmalloc(sizeof(*eep) * count, M_TEMP, M_WAITOK | M_ZERO);
135
136         for (i = 0; i < count; i++) {
137                 linux_kevent_to_epoll(&kevp[i], &eep[i]);
138         }
139
140         error = copyout(eep, uap->eventlist, count * sizeof(*eep));
141         if (error == 0) {
142                 uap->eventlist = (struct kevent *)((char *)uap->eventlist + count * sizeof(*eep));
143                 *res += count;
144         }
145
146         kfree(eep, M_TEMP);
147         return (error);
148 }
149
150 /*
151  * Copyin callback used by kevent. This copies already
152  * converted filters to the kevent internal memory.
153  */
154 static int
155 linux_kev_copyin(void *arg, struct kevent *kevp, int maxevents, int *events)
156 {
157         struct kevent_args *uap;
158
159         uap = (struct kevent_args*) arg;
160
161         memcpy(kevp, uap->changelist, maxevents * sizeof(*kevp));
162
163         uap->changelist += maxevents;
164         *events = maxevents;
165
166         return (0);
167 }
168
169 /*
170  * Load epoll filter, convert it to kevent filter
171  * and load it into kevent subsystem.
172  */
173 int
174 sys_linux_epoll_ctl(struct linux_epoll_ctl_args *args)
175 {
176         struct thread *td = curthread;
177         struct proc *p = td->td_proc;
178         struct kevent_args k_args;
179         struct kevent kev;
180         struct kqueue *kq;
181         struct linux_epoll_event le;
182         struct file *fp = NULL;
183         int error;
184
185         error = copyin(args->event, &le, sizeof(le));
186         if (error)
187                 return (error);
188 #ifdef DEBUG
189         if (ldebug(epoll_ctl))
190                 kprintf(ARGS(epoll_ctl,"%i, %i, %i, %u"), args->epfd, args->op,
191                         args->fd, le.events);
192 #endif
193         k_args.fd = args->epfd;
194         k_args.changelist = &kev;
195         /* The epoll can register only 1 filter at once. */
196         k_args.nchanges = 1;
197         k_args.eventlist = NULL;
198         k_args.nevents = 0;
199         k_args.timeout = NULL;
200
201         switch (args->op) {
202         case LINUX_EPOLL_CTL_ADD:
203                         kev.flags = EV_ADD | EV_ENABLE;
204                 break;
205         case LINUX_EPOLL_CTL_MOD:
206                         /* TODO: DELETE && ADD maybe? */
207                         return (EINVAL);
208                 break;
209         case LINUX_EPOLL_CTL_DEL:
210                         kev.flags = EV_DELETE | EV_DISABLE;
211                 break;
212         }
213         linux_epoll_to_kevent(args->fd, &le, &kev);
214
215         fp = holdfp(p->p_fd, args->epfd, -1);
216         if (fp == NULL)
217                 return (EBADF);
218         if (fp->f_type != DTYPE_KQUEUE) {
219                 fdrop(fp);
220                 return (EBADF);
221         }
222
223         kq = (struct kqueue *)fp->f_data;
224
225         error = kern_kevent(kq, 0, &k_args.sysmsg_result, &k_args,
226             linux_kev_copyin, linux_kev_copyout, NULL);
227         /* Check if there was an error during registration. */
228         if (error == 0 && k_args.sysmsg_result != 0) {
229                 /* The copyout callback stored the error there. */
230                 error = le.data;
231         }
232
233         fdrop(fp);
234         return (error);
235 }
236
237 /*
238  * Wait for a filter to be triggered on the epoll file descriptor. */
239 int
240 sys_linux_epoll_wait(struct linux_epoll_wait_args *args)
241 {
242         struct thread *td = curthread;
243         struct proc *p = td->td_proc;
244         struct timespec ts;
245         struct kqueue *kq;
246         struct file *fp = NULL;
247         struct kevent_args k_args;
248         int error;
249
250         /* Convert from milliseconds to timespec. */
251         ts.tv_sec = args->timeout / 1000;
252         ts.tv_nsec = (args->timeout % 1000) * 1000 * 1000;
253
254         k_args.fd = args->epfd;
255         k_args.changelist = NULL;
256         k_args.nchanges = 0;
257         /*
258          * We don't mind the bogus type-cast because
259          * our copyout function knows about this and
260          * handles it correctly.
261          */
262         k_args.eventlist = (struct kevent *)args->events;
263         k_args.nevents = args->maxevents;
264         k_args.timeout = &ts;
265
266         fp = holdfp(p->p_fd, args->epfd, -1);
267         if (fp == NULL)
268                 return (EBADF);
269         if (fp->f_type != DTYPE_KQUEUE) {
270                 fdrop(fp);
271                 return (EBADF);
272         }
273
274         kq = (struct kqueue *)fp->f_data;
275
276         error = kern_kevent(kq, args->maxevents, &args->sysmsg_result,
277             &k_args, linux_kev_copyin, linux_kev_copyout, &ts);
278
279         fdrop(fp);
280         /* translation? */
281         return (error);
282 }