linux emulation - Major update
[dragonfly.git] / sys / emulation / linux / linux_epoll.c
blob9e8cd7499cf6442ecc55328c0ceaac8803f644b3
1 /*-
2 * Copyright (c) 2007 Roman Divacky
3 * All rights reserved.
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.
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.
27 #include "opt_compat.h"
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>
42 #include <vm/vm.h>
43 #include <vm/vm_param.h>
44 #include <vm/vm_page.h>
45 #include <vm/vm_extern.h>
46 #include <sys/exec.h>
47 #include <sys/kernel.h>
48 #include <sys/module.h>
49 #include <machine/cpu.h>
51 #include "i386/linux.h"
52 #include "i386/linux_proto.h"
53 #include "linux_signal.h"
54 #include "linux_util.h"
55 #include "linux_epoll.h"
58 /* Create a new epoll file descriptor. */
59 int
60 sys_linux_epoll_create(struct linux_epoll_create_args *args)
62 struct kqueue_args k_args;
64 if (args->size <= 0)
65 return (EINVAL);
66 /* args->size is unused. Linux ignores it as well. */
68 return (sys_kqueue(&k_args));
71 /* Structure converting function from epoll to kevent. */
72 static void
73 linux_epoll_to_kevent(int fd, struct linux_epoll_event *event, struct kevent *kevent)
75 int filter = 0;
76 int flags = kevent->flags;
78 if (event->events & LINUX_EPOLLIN)
79 filter |= EVFILT_READ;
80 if (event->events & LINUX_EPOLLOUT)
81 filter |= EVFILT_WRITE;
82 if (event->events & LINUX_EPOLLPRI)
83 filter |= EVFILT_READ;
84 if (event->events & LINUX_EPOLLET)
85 flags |= EV_CLEAR;
86 if (event->events & LINUX_EPOLLONESHOT)
87 flags |= EV_ONESHOT;
89 EV_SET(kevent, fd, filter, flags, 0, 0, NULL);
93 * Structure converting function from kevent to epoll. In a case
94 * this is called on error in registration we store the error in
95 * event->data and pick it up later in linux_epoll_ctl().
97 static void
98 linux_kevent_to_epoll(struct kevent *kevent, struct linux_epoll_event *event)
100 if (kevent->flags & EV_ERROR) {
101 event->data = kevent->data;
102 return;
104 switch (kevent->filter) {
105 case EVFILT_READ:
106 if (kevent->data > 0)
107 event->events = LINUX_EPOLLIN;
108 event->data = kevent->ident;
109 break;
110 case EVFILT_WRITE:
111 if (kevent->data > 0)
112 event->events = LINUX_EPOLLOUT;
113 event->data = kevent->ident;
114 break;
119 * Copyout callback used by kevent. This converts kevent
120 * events to epoll events and copies them back to the
121 * userspace. This is also called on error on registering
122 * of the filter.
124 static int
125 linux_kev_copyout(void *arg, struct kevent *kevp, int count)
127 struct kevent_args *uap;
128 struct linux_epoll_event *eep;
129 int error, i;
131 uap = (struct kevent_args*) arg;
133 eep = kmalloc(sizeof(*eep) * count, M_TEMP, M_WAITOK | M_ZERO);
135 for (i = 0; i < count; i++) {
136 linux_kevent_to_epoll(&kevp[i], &eep[i]);
139 error = copyout(eep, uap->eventlist, count * sizeof(*eep));
140 if (error)
141 uap->eventlist = (struct kevent *)((char *)uap->eventlist + count * sizeof(*eep));
143 kfree(eep, M_TEMP);
144 return (0);
148 * Copyin callback used by kevent. This copies already
149 * converted filters to the kevent internal memory.
151 static int
152 linux_kev_copyin(void *arg, struct kevent *kevp, int count)
154 struct kevent_args *uap;
156 uap = (struct kevent_args*) arg;
158 memcpy(kevp, uap->changelist, count * sizeof(*kevp));
160 uap->changelist += count;
162 return (0);
166 * Load epoll filter, convert it to kevent filter
167 * and load it into kevent subsystem.
170 sys_linux_epoll_ctl(struct linux_epoll_ctl_args *args)
172 struct kevent_args k_args;
173 struct kevent kev;
174 struct linux_epoll_event le;
175 int error;
177 error = copyin(args->event, &le, sizeof(le));
178 if (error)
179 return (error);
180 #ifdef DEBUG
181 if (ldebug(epoll_ctl))
182 kprintf(ARGS(epoll_ctl,"%i, %i, %i, %u"), args->epfd, args->op,
183 args->fd, le.events);
184 #endif
185 k_args.fd = args->epfd;
186 k_args.changelist = &kev;
187 /* The epoll can register only 1 filter at once. */
188 k_args.nchanges = 1;
189 k_args.eventlist = NULL;
190 k_args.nevents = 0;
191 k_args.timeout = NULL;
193 switch (args->op) {
194 case LINUX_EPOLL_CTL_ADD:
195 kev.flags = EV_ADD | EV_ENABLE;
196 break;
197 case LINUX_EPOLL_CTL_MOD:
198 /* TODO: DELETE && ADD maybe? */
199 return (EINVAL);
200 break;
201 case LINUX_EPOLL_CTL_DEL:
202 kev.flags = EV_DELETE | EV_DISABLE;
203 break;
205 linux_epoll_to_kevent(args->fd, &le, &kev);
207 error = kern_kevent(args->epfd, 1, 0, &k_args, linux_kev_copyin,
208 linux_kev_copyout, NULL);
209 /* Check if there was an error during registration. */
210 if (error == 0 && k_args.sysmsg_result != 0) {
211 /* The copyout callback stored the error there. */
212 error = le.data;
215 return (error);
219 * Wait for a filter to be triggered on the epoll file descriptor. */
221 sys_linux_epoll_wait(struct linux_epoll_wait_args *args)
223 struct timespec ts;
224 struct kevent_args k_args;
225 int error;
227 /* Convert from miliseconds to timespec. */
228 ts.tv_sec = args->timeout / 1000000;
229 ts.tv_nsec = (args->timeout % 1000000) * 1000;
231 k_args.fd = args->epfd;
232 k_args.changelist = NULL;
233 k_args.nchanges = 0;
235 * We don't mind the bogus type-cast because
236 * our copyout function knows about this and
237 * handles it correctly.
239 k_args.eventlist = (struct kevent *)args->events;
240 k_args.nevents = args->maxevents;
241 k_args.timeout = &ts;
243 error = kern_kevent(args->epfd, 0, args->maxevents, &k_args,
244 linux_kev_copyin, linux_kev_copyout, &ts);
246 /* translation? */
247 return (error);