Hi, I've made patch for kqueue FreeBSD support, could somebody review it?
diff -Naur openvpn-2.1.4/event.c openvpn-2.1.4.new/event.c --- openvpn-2.1.4/event.c 2010-10-21 10:37:51.000000000 -0700 +++ openvpn-2.1.4.new/event.c 2011-01-26 15:11:06.000000000 -0800 @@ -30,6 +30,10 @@ #include "event.h" #include "memdbg.h" +#define KQUEUE 1 +#if KQUEUE +#include <sys/event.h> +#endif /* * Some OSes will prefer select() over poll() @@ -599,7 +603,163 @@ return (struct event_set *) eps; } -#endif /* EPOLL */ + +#elif KQUEUE /* EPOLL */ + +struct kq_set +{ + struct event_set_functions func; + bool fast; + int kqfd; + int maxevents; + struct kevent *changes; + size_t changes_size; + size_t maxchanges; + struct kevent *events; +}; + +static void +kq_free (struct event_set *es) +{ + struct kq_set *kqs = (struct kq_set *) es; + close (kqs->kqfd); + free (kqs->changes); + free (kqs->events); + free (kqs); +} + +static void +kq_reset (struct event_set *es) +{ + const struct kq_set *kqs = (struct kq_set *) es; + ASSERT (kqs->fast); +} + +static void +kq_del (struct event_set *es, event_t event) +{ + struct kq_set *kqs = (struct kq_set *) es; + struct kevent *ev; + + dmsg (D_EVENT_WAIT, "KQ_DEL ev=%d", (int)event); + + ASSERT (!kqs->fast); + if ( kqs->changes_size+2 > kqs->maxchanges) { + kqs->maxchanges+=128; + kqs->changes = realloc(kqs->changes, sizeof(struct kevent)*kqs->maxchanges); + } + + ev=&kqs->changes[kqs->changes_size++]; + ev->ident = event; + ev->flags = EV_DELETE; + ev->filter = EVFILT_READ; + + ev=&kqs->changes[kqs->changes_size++]; + ev->ident = event; + ev->flags = EV_DELETE; + ev->filter = EVFILT_WRITE; +} + +static void +kq_ctl (struct event_set *es, event_t event, unsigned int rwflags, void *arg) +{ + struct kq_set *kqs = (struct kq_set *) es; + if ( kqs->changes_size+2 > kqs->maxchanges) { + kqs->maxchanges+=128; + kqs->changes = realloc(kqs->changes, sizeof(struct kevent)*kqs->maxchanges); + } + + if (rwflags & EVENT_READ) { + struct kevent *ev=&kqs->changes[kqs->changes_size++]; + ev->udata=arg; + ev->ident = event; + ev->flags = EV_ADD; + ev->filter = EVFILT_READ; + } + if (rwflags & EVENT_WRITE) { + struct kevent *ev=&kqs->changes[kqs->changes_size++]; + ev->udata=arg; + ev->ident = event; + ev->flags = EV_ADD; + ev->filter = EVFILT_WRITE; + } +} + +static int +kq_wait (struct event_set *es, const struct timeval *tv, struct event_set_return *out, int outlen) +{ + struct kq_set *kqs = (struct kq_set *) es; + int stat; + struct timespec ts; + TIMEVAL_TO_TIMESPEC(tv, &ts); + + if (outlen > kqs->maxevents) { + kqs->maxevents=outlen; + kqs->events = realloc(kqs->events, sizeof(struct kevent)*outlen); + } + + stat = kevent (kqs->kqfd, kqs->changes, kqs->changes_size, kqs->events, outlen, &ts ); + kqs->changes_size=0; + + if (stat > 0) + { + int i; + const struct kevent *ev = kqs->events; + struct event_set_return *esr = out; + for (i = 0; i < stat; ++i) + { + esr->rwflags = 0; + if (ev->filter == EVFILT_READ) + esr->rwflags |= EVENT_READ; + if (ev->filter == EVFILT_WRITE) + esr->rwflags |= EVENT_WRITE; + esr->arg = ev->udata; + dmsg (D_EVENT_WAIT, "KQ_WAIT[%d] rwflags=0x%04x ev=0x%08x arg=" ptr_format, + i, esr->rwflags, ev->flags, (ptr_type)ev->udata); + ++ev; + ++esr; + } + } + return stat; +} + +static struct event_set * +kq_init (int *maxevents, unsigned int flags) +{ + struct kq_set *kqs; + int fd; + + dmsg (D_EVENT_WAIT, "KQ_INIT maxevents=%d flags=0x%08x", *maxevents, flags); + + /* open epoll file descriptor */ + fd = kqueue (); + if (fd < 0) + return NULL; + + ALLOC_OBJ_CLEAR (kqs, struct kq_set); + + /* set dispatch functions */ + kqs->func.free = kq_free; + kqs->func.reset = kq_reset; + kqs->func.del = kq_del; + kqs->func.ctl = kq_ctl; + kqs->func.wait = kq_wait; + + /* fast method ("sort of") corresponds to epoll one-shot */ + if (flags & EVENT_METHOD_FAST) + kqs->fast = true; + + /* allocate space for epoll_wait return */ + ASSERT (*maxevents > 0); + kqs->maxevents = *maxevents; + ALLOC_ARRAY_CLEAR (kqs->events, struct kevent, kqs->maxevents); + + /* set epoll control fd */ + kqs->kqfd = fd; + + return (struct event_set *) kqs; +} +#endif #if POLL @@ -1005,6 +1165,10 @@ if (flags & EVENT_METHOD_US_TIMEOUT) ret = se_init (maxevents, flags); #endif +# ifdef KQUEUE + if (!ret) + ret = kq_init (maxevents, flags); +# endif # ifdef SELECT_PREFERRED_OVER_POLL if (!ret) ret = se_init (maxevents, flags); @@ -1038,6 +1202,13 @@ msg (M_WARN, "Note: sys_epoll API is unavailable, falling back to poll/select API"); ret = event_set_init_simple (maxevents, flags); } +#elif KQUEUE + ret = kq_init (maxevents, flags); + if (!ret) + { + msg (M_WARN, "Note: sys_epoll API is unavailable, falling back to poll/select API"); + ret = event_set_init_simple (maxevents, flags); + } #else ret = event_set_init_simple (maxevents, flags); #endif