From: Willy Tarreau Date: Mon, 9 Apr 2007 10:03:06 +0000 (+0200) Subject: [MAJOR] implemented support for FreeBSD's kqueue() polling mechanism X-Git-Tag: v1.3.9~24 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=1e63130a37e4467ee73caa8cf27af1b3ef3acd5b;p=thirdparty%2Fhaproxy.git [MAJOR] implemented support for FreeBSD's kqueue() polling mechanism It has not been tested yet, but at least it builds. --- diff --git a/Makefile.bsd b/Makefile.bsd index 043ff04dce..0050701eef 100644 --- a/Makefile.bsd +++ b/Makefile.bsd @@ -29,7 +29,7 @@ PCREDIR!= pcre-config --prefix 2>/dev/null || : #PCREDIR=/usr/local # This is for OpenBSD 3.0 and above -COPTS.openbsd = -DENABLE_POLL +COPTS.openbsd = -DENABLE_POLL -DENABLE_KQUEUE LIBS.openbsd = # CPU dependant optimizations @@ -87,7 +87,8 @@ OBJS = src/haproxy.o src/list.o src/chtbl.o src/hashpjw.o src/base64.o \ src/time.o src/fd.o src/regex.o src/cfgparse.o src/server.o \ src/checks.o src/queue.o src/capture.o src/client.o src/proxy.o \ src/proto_http.o src/stream_sock.o src/appsession.o src/backend.o \ - src/session.o src/hdr_idx.o src/rbtree.o src/ev_select.o src/ev_poll.o + src/session.o src/hdr_idx.o src/rbtree.o src/ev_select.o src/ev_poll.o \ + src/ev_kqueue.o all: haproxy diff --git a/include/types/polling.h b/include/types/polling.h index ed3cf64dc0..81921ee4ab 100644 --- a/include/types/polling.h +++ b/include/types/polling.h @@ -52,8 +52,9 @@ #define POLL_USE_SELECT (1<<0) #define POLL_USE_POLL (1<<1) #define POLL_USE_EPOLL (1<<2) +#define POLL_USE_KQUEUE (1<<3) -extern int cfg_polling_mechanism; /* POLL_USE_{SELECT|POLL|EPOLL} */ +extern int cfg_polling_mechanism; /* POLL_USE_{SELECT|POLL|EPOLL|KQUEUE} */ #endif /* _TYPES_POLLING_H */ diff --git a/src/ev_kqueue.c b/src/ev_kqueue.c new file mode 100644 index 0000000000..98115fa1c5 --- /dev/null +++ b/src/ev_kqueue.c @@ -0,0 +1,238 @@ +/* + * FD polling functions for FreeBSD kqueue() + * + * Copyright 2000-2007 Willy Tarreau + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * Note: not knowing much about kqueue, I had to rely on OpenBSD's detailed man + * page and to check how it was implemented in lighttpd to understand it better. + * But it is possible that I got things wrong. + * + */ + +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include +#include + +#include +#include +#include + +/* private data */ +static fd_set *fd_evts[2]; +static int kqueue_fd; +static struct kevent *kev = NULL; + +/* speeds up conversion of DIR_RD/DIR_WR to EVFILT* */ +static const int dir2filt[2] = { EVFILT_READ, EVFILT_WRITE }; + +/* completes a change list for deletion */ +REGPRM3 static int kqev_del(struct kevent *kev, const int fd, const int dir) +{ + if (FD_ISSET(fd, fd_evts[dir])) { + FD_CLR(fd, fd_evts[dir]); + EV_SET(kev, fd, dir2filt[dir], EV_DELETE, 0, 0, NULL); + return 1; + } + return 0; +} + +/* + * Returns non-zero if direction is already set for . + */ +REGPRM2 static int __fd_isset(const int fd, int dir) +{ + return FD_ISSET(fd, fd_evts[dir]); +} + +REGPRM2 static int __fd_set(const int fd, int dir) +{ + /* if the value was set, do nothing */ + if (FD_ISSET(fd, fd_evts[dir])) + return 0; + + FD_SET(fd, fd_evts[dir]); + EV_SET(kev, fd, dir2filt[dir], EV_ADD|EV_CLEAR, 0, 0, NULL); + kevent(kqueue_fd, kev, 1, NULL, 0, NULL); + return 1; +} + +REGPRM2 static int __fd_clr(const int fd, int dir) +{ + /* if the value was not set, do nothing */ + //if (!FD_ISSET(fd, fd_evts[dir])) + // return 0; + // + //FD_CLR(fd, fd_evts[dir]); + //EV_SET(&kev, fd, dir2filt[dir], EV_DELETE, 0, 0, NULL); + //kevent(kqueue_fd, &kev, 1, NULL, 0, NULL); + //return 1; + if (!kqev_del(kev, fd, dir)) + return 0; + kevent(kqueue_fd, kev, 1, NULL, 0, NULL); + return 1; +} + +REGPRM1 static void __fd_rem(int fd) +{ + int changes = 0; + + //if (FD_ISSET(fd, fd_evts[DIR_RD])) { + // FD_CLR(fd, fd_evts[DIR_RD]); + // EV_SET(&kev[changes], fd, dir2filt[DIR_RD], EV_DELETE, 0, 0, NULL); + // changes++; + //} + //if (FD_ISSET(fd, fd_evts[DIR_WR])) { + // FD_CLR(fd, fd_evts[DIR_WR]); + // EV_SET(&kev[changes], fd, dir2filt[DIR_WR], EV_DELETE, 0, 0, NULL); + // changes++; + //} + changes += kqev_del(&kev[changes], fd, DIR_RD); + changes += kqev_del(&kev[changes], fd, DIR_WR); + + if (changes) + kevent(kqueue_fd, kev, changes, NULL, 0, NULL); +} + +/* + * kqueue() poller + */ +REGPRM2 static void kqueue_poll(struct poller *p, int wait_time) +{ + int status; + int count, fd; + struct timespec timeout; + + timeout.tv_sec = wait_time / 1000; + timeout.tv_nsec = (wait_time % 1000) * 1000000; + + status = kevent(kqueue_fd, // int kq + NULL, // const struct kevent *changelist + 0, // int nchanges + kev, // struct kevent *eventlist + maxfd, // int nevents + &timeout); // const struct timespec *timeout + + for (count = 0; count < status; count++) { + fd = kev[count].ident; + if (kev[count].filter == EVFILT_READ) { + if (FD_ISSET(fd, fd_evts[DIR_RD])) { + if (fdtab[fd].state == FD_STCLOSE) + continue; + fdtab[fd].cb[DIR_RD].f(fd); + } + } else if (kev[count].filter == EVFILT_WRITE) { + if (FD_ISSET(fd, fd_evts[DIR_WR])) { + if (fdtab[fd].state == FD_STCLOSE) + continue; + fdtab[fd].cb[DIR_WR].f(fd); + } + } + } +} + +/* + * Initialization of the kqueue() poller. + * Returns 0 in case of failure, non-zero in case of success. If it fails, it + * disables the poller by setting its pref to 0. + */ +REGPRM1 static int kqueue_init(struct poller *p) +{ + __label__ fail_wevt, fail_revt, fail_fd; + int fd_set_bytes; + + p->private = NULL; + fd_set_bytes = sizeof(fd_set) * (global.maxsock + FD_SETSIZE - 1) / FD_SETSIZE; + + kqueue_fd = kqueue(); + if (kqueue_fd < 0) + goto fail_fd; + + kev = (struct kevent*)calloc(1, sizeof(struct kevent) * global.maxsock); + + if (kev == NULL) + goto fail_kev; + + if ((fd_evts[DIR_RD] = (fd_set *)calloc(1, fd_set_bytes)) == NULL) + goto fail_revt; + + if ((fd_evts[DIR_WR] = (fd_set *)calloc(1, fd_set_bytes)) == NULL) + goto fail_wevt; + + return 1; + + fail_wevt: + free(fd_evts[DIR_RD]); + fail_revt: + free(kev); + fail_kev: + close(kqueue_fd); + kqueue_fd = 0; + fail_fd: + p->pref = 0; + return 0; +} + +/* + * Termination of the kqueue() poller. + * Memory is released and the poller is marked as unselectable. + */ +REGPRM1 static void kqueue_term(struct poller *p) +{ + if (fd_evts[DIR_WR]) + free(fd_evts[DIR_WR]); + if (fd_evts[DIR_RD]) + free(fd_evts[DIR_RD]); + if (kev) + free(kev); + close(kqueue_fd); + kqueue_fd = 0; + + p->private = NULL; + p->pref = 0; +} + +/* + * The only exported function. Returns 1. + */ +//int kqueue_native_register(struct poller *p) +int kqueue_register(struct poller *p) +{ + p->name = "kqueue"; + p->pref = 300; + p->private = NULL; + + p->init = kqueue_init; + p->term = kqueue_term; + p->poll = kqueue_poll; + + p->isset = __fd_isset; + p->cond_s = p->set = __fd_set; + p->cond_c = p->clr = __fd_clr; + p->clo = p->rem = __fd_rem; + + return 1; +} + + +/* + * Local variables: + * c-indent-level: 8 + * c-basic-offset: 8 + * End: + */ diff --git a/src/haproxy.c b/src/haproxy.c index bafff4bfb1..0ab069994a 100644 --- a/src/haproxy.c +++ b/src/haproxy.c @@ -175,6 +175,9 @@ void usage(char *name) #if defined(ENABLE_EPOLL) " -de disables epoll() usage even when available\n" #endif +#if defined(ENABLE_KQUEUE) + " -dk disables kqueue() usage even when available\n" +#endif #if defined(ENABLE_POLL) " -dp disables poll() usage even when available\n" #endif @@ -371,6 +374,9 @@ void init(int argc, char **argv) #if defined(ENABLE_EPOLL) cfg_polling_mechanism |= POLL_USE_EPOLL; #endif +#if defined(ENABLE_KQUEUE) + cfg_polling_mechanism |= POLL_USE_KQUEUE; +#endif pid = getpid(); progname = *argv; @@ -396,6 +402,10 @@ void init(int argc, char **argv) #if defined(ENABLE_POLL) else if (*flag == 'd' && flag[1] == 'p') cfg_polling_mechanism &= ~POLL_USE_POLL; +#endif +#if defined(ENABLE_POLL) + else if (*flag == 'd' && flag[1] == 'k') + cfg_polling_mechanism &= ~POLL_USE_KQUEUE; #endif else if (*flag == 'V') arg_mode |= MODE_VERBOSE; @@ -516,6 +526,9 @@ void init(int argc, char **argv) register_pollers(); /* Note: we could register external pollers here */ + if (!(cfg_polling_mechanism & POLL_USE_KQUEUE)) + disable_poller("kqueue"); + if (!(cfg_polling_mechanism & POLL_USE_EPOLL)) disable_poller("epoll");