| /* |
| * bsd_epoll.c - Linux epoll.h emulation via kevent API |
| * |
| * Copyright (c) 2009 Boaz Harrosh <bharrosh@panasas.com> |
| * All rights reserved. |
| * |
| * Inspired by code written by Roman Divacky |
| * from the freebsd's linux emulation project at linux_epoll.c file |
| * However none of that code ended up here. |
| * |
| * description: |
| * This file implements a workable set of the <sys/epoll.h> header file |
| * on a linux distribution. The implementation translates back and forth from |
| * epoll calls and constants to Kevent calls and constants. |
| * |
| * This file is copyrighted under the "New BSD License" (see below) so it can |
| * be included in the tirpc library project. |
| * But I fully expect that if you make any fixes/enhancements to bsd_epoll.c |
| * you shall send these changes to me for inclusion in the next version. |
| * (Or I'll hunt your dreams, and you will not have peace) |
| * |
| * License: "New BSD License" |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are met: |
| * * Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * * 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. |
| * * Neither the name of the <organization> nor the |
| * names of its contributors may be used to endorse or promote products |
| * derived from this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 |
| * <COPYRIGHT HOLDER> 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. |
| */ |
| #if defined(__FreeBSD__) |
| |
| #include <sys/types.h> |
| #include <sys/event.h> |
| #include <sys/time.h> |
| #include <errno.h> |
| #include <stdlib.h> |
| #include <stdio.h> |
| #include <string.h> |
| |
| #include <misc/bsd_epoll.h> |
| |
| /* Create a new epoll file descriptor. */ |
| int |
| epoll_create(int size) |
| { |
| if (size <= 0) |
| return -EINVAL; |
| /* |
| * args->size is unused. Linux just tests it |
| * and then forgets it as well. |
| */ |
| |
| return kqueue(); |
| } |
| |
| static void |
| kevent_to_epoll(struct kevent *kevent, struct epoll_event *event) |
| { |
| /* if (kevent->flags & EV_ERROR) { |
| ... Any special event flags on Errors? |
| } */ |
| |
| memset(event, 0, sizeof(*event)); |
| switch (kevent->filter) { |
| case EVFILT_READ: |
| /* in Linux EPOLLIN with Zero Data will signal a close |
| * condition. So do that if EV_EOF. |
| */ |
| if ((kevent->data > 0) || (kevent->flags & EV_EOF)) |
| event->events = EPOLLIN; |
| break; |
| case EVFILT_WRITE: |
| if (kevent->data > 0) |
| event->events = EPOLLOUT; |
| break; |
| default: |
| fprintf(stderr, |
| "Unsupported kevent_to_epoll event=%d" |
| " data=%lx flags=0x%x ident=%ld fflags=0x%x\n", |
| kevent->filter, (long)kevent->data, kevent->flags, |
| (long)kevent->ident, kevent->fflags); |
| } |
| event->data.ptr = kevent->udata; |
| } |
| |
| static void |
| kevent_to_epoll_arr(struct kevent *kevp, int count, |
| struct epoll_event *eep) |
| { |
| int i; |
| |
| for (i = 0; i < count; i++) { |
| kevent_to_epoll(&kevp[i], &eep[i]); |
| /*printf("eep[%d]=%p\n", i, eep[i].data.ptr); */ |
| } |
| } |
| |
| /* |
| * Always create two filters An EVFILT_READ and an EVFILT_WRITE. |
| * EV_ENABLE/EV_DISABLE according to epoll flags. On EPOLL_CTL_MOD |
| * first delete then add a new. On EPOLL_CTL_DEL remove both. |
| */ |
| int |
| epoll_ctl(int epfd, int op, int fd, struct epoll_event *event) |
| { |
| struct kevent kev[2] = { {.flags = 0}, }; |
| int read_flags, write_flags; |
| |
| switch (op) { |
| case EPOLL_CTL_MOD: |
| epoll_ctl(epfd, EPOLL_CTL_DEL, fd, event); |
| /* fall through */ |
| case EPOLL_CTL_ADD: |
| { |
| int flags; |
| |
| if ((event->events & EPOLLIN) |
| || event->events & EPOLLPRI) |
| read_flags = EV_ENABLE; |
| else |
| read_flags = EV_DISABLE; |
| |
| if (event->events & EPOLLOUT) |
| write_flags = EV_ENABLE; |
| else |
| write_flags = EV_DISABLE; |
| |
| flags = EV_ADD; |
| if (event->events & EPOLLET) |
| flags |= EV_CLEAR; |
| if (event->events & EPOLLONESHOT) |
| flags |= EV_ONESHOT; |
| |
| read_flags |= flags; |
| write_flags |= flags; |
| /* printf("%s(fd=%d data.ptr=%p\n", |
| op == EPOLL_CTL_MOD ? "EPOLL_CTL_MOD" : "EPOLL_CTL_ADD", |
| fd ,event->data.ptr); |
| printf(" EVFILT_READ(on=%d read_flags=0x%x\n", |
| read_flags & EV_ENABLE, read_flags); |
| printf(" EVFILT_WRITE(on=%d write_flags=0x%x);\n", |
| write_flags & EV_ENABLE, write_flags);*/ |
| } |
| break; |
| case EPOLL_CTL_DEL: |
| read_flags = write_flags = EV_DELETE | EV_DISABLE; |
| /*printf("EPOLL_CTL_DEL(fd=%d\n",fd); */ |
| break; |
| default: |
| fprintf(stderr, |
| "Unsupported epoll operation=%d" |
| " epfd=%d, fd=%d events=%x\n", op, epfd, fd, |
| event->events); |
| errno = EINVAL; |
| return -1; |
| } |
| |
| EV_SET(&kev[0], fd, EVFILT_READ, read_flags, 0, 0, |
| event ? event->data.ptr : 0); |
| EV_SET(&kev[1], fd, EVFILT_WRITE, write_flags, 0, 0, |
| event ? event->data.ptr : 0); |
| |
| return kevent(epfd, kev, 2, NULL, 0, NULL); |
| } |
| |
| /* |
| * Wait for a filter to be triggered on the epoll file descriptor. |
| */ |
| int |
| epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout) |
| { |
| struct kevent *kevp; |
| struct timespec ts; |
| int rc; |
| |
| /* Convert from miliseconds to timespec. */ |
| ts.tv_sec = timeout / 1000000; |
| ts.tv_nsec = (timeout % 1000000) * 1000; |
| |
| kevp = mem_calloc(maxevents, sizeof(*kevp)); |
| /* |
| * ENOMEM is not expected from epoll_wait. |
| * Maybe we should translate that but I don't think it matters at all. |
| */ |
| |
| rc = kevent(epfd, NULL, 0, kevp, maxevents, &ts); |
| |
| if (rc > 0) { |
| /*printf("epoll_wait LEAVE %d\n", rc); */ |
| kevent_to_epoll_arr(kevp, rc, events); |
| } |
| |
| mem_free(kevp, sizeof(struct kevent) * maxevents); |
| return rc; |
| } |
| |
| #endif /* __FreeBSD__ */ |