From: Amos Jeffries Date: Sun, 17 Oct 2010 04:49:27 +0000 (-0600) Subject: Author: Adrian Chadd X-Git-Tag: take1~177 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=a1ad2f9b4fc8d5540bd531dad4bf1b70bf6e34f9;p=thirdparty%2Fsquid.git Author: Adrian Chadd Author: Peter Payne Bug 2356: Solaris /dev/poll event ports support. Original code by Adrian Chadd. Ported from Squid-2 by Peter Payne, Pirosa Limited UK. With sponsorship of development and testing from: BBC (UK) Siemens IT Solutions and Services (UK) --- diff --git a/SPONSORS b/SPONSORS index 8e850de8db..02855238f1 100644 --- a/SPONSORS +++ b/SPONSORS @@ -77,3 +77,8 @@ Treehouse Networks, NZ - http://treenet.co.nz/ Treehouse Networks has contributed significant development resources toward Squid-3 development and maintenance for their customer gateways and CDN. + +BBC (UK) and Siemens IT Solutions and Services (UK) + + Provided developement and testing resources for Solaris /dev/poll + support. diff --git a/acinclude/os-deps.m4 b/acinclude/os-deps.m4 index 2ae250bd9b..a71c8ecb43 100644 --- a/acinclude/os-deps.m4 +++ b/acinclude/os-deps.m4 @@ -136,6 +136,29 @@ int main(int argc, char **argv) ]) dnl SQUID_CHECK_EPOLL +dnl check that /dev/poll actually works +dnl sets squid_cv_devpoll_works to "yes" or "no" +AC_DEFUN([SQUID_CHECK_DEVPOLL],[ + + AC_CACHE_CHECK(if /dev/poll works, squid_cv_devpoll_works, + AC_RUN_IFELSE([AC_LANG_SOURCE([[ +#include +#include +#include +#include +int main(int argc, char **argv) +{ + int fd = open("/dev/poll", O_RDWR); + if (fd < 0) { + perror("devpoll_create:"); + return 1; + } + return 0; +} + ]])],[squid_cv_devpoll_works=yes],[squid_cv_devpoll_works=no],[:])) + +]) dnl SQUID_CHECK_DEVPOLL + dnl check that we have functional libcap2 headers dnl sets squid_cv_sys_capability_works to "yes" or "no" diff --git a/compat/compat_shared.h b/compat/compat_shared.h index 3730b8cd83..35fdabb6f2 100644 --- a/compat/compat_shared.h +++ b/compat/compat_shared.h @@ -67,7 +67,7 @@ /* Limited due to delay pools */ # define SQUID_MAXFD_LIMIT ((signed int)FD_SETSIZE) -#elif defined(USE_KQUEUE) || defined(USE_EPOLL) +#elif defined(USE_KQUEUE) || defined(USE_EPOLL) || defined(USE_DEVPOLL) # define SQUID_FDSET_NOUSE 1 #else diff --git a/configure.in b/configure.in index 3ea7d60d94..9c38b6949f 100644 --- a/configure.in +++ b/configure.in @@ -1254,6 +1254,39 @@ if test "x$enable_epoll" != "xno" ; then fi fi +dnl Enable /dev/poll +AC_ARG_ENABLE(devpoll, + AS_HELP_STRING([--disable-devpoll],[Disable Solaris /dev/poll support.]), +[ +SQUID_YESNO($enableval,[--disable-devpoll takes no extra argument]) +test "x$enableval" = "xyes" && squid_opt_io_loop_engine="devpoll" +]) +AC_MSG_NOTICE([enabling /dev/poll for net I/O: ${enable_devpoll:=auto}]) + +## auto-detect and verify devpoll header and library present and working +if test "x$enable_devpoll" != "xno"; then + + # /dev/poll requires ioctl() and write() + AC_CHECK_FUNCS(ioctl) + AC_CHECK_FUNCS(write) + + # /dev/poll requires sys/devpoll.h + AC_CHECK_HEADERS([sys/devpoll.h],,[ + if test "x$enable_devpoll" = "xyes"; then + AC_MSG_ERROR([--enable-devpoll specified but /dev/poll headers not found]) + fi + enable_devpoll=no]) + + # Verify that /dev/poll really works + if test "x$enable_devpoll" != 'xno' ; then + SQUID_CHECK_DEVPOLL + fi + + if test "x$enable_devpoll" = "xyes" -a "x$squid_cv_devpoll_works" = "xno" ; then + AC_MSG_ERROR([/dev/poll does not work. Force-enabling it is not going to help.]) + fi +fi + AC_ARG_ENABLE(http-violations, AS_HELP_STRING([--disable-http-violations], @@ -3024,6 +3057,8 @@ elif test "x$enable_epoll" != "xno" -a "x$squid_cv_epoll_works" = "xyes" ; then squid_opt_io_loop_engine="epoll" elif test "x$enable_kqueue" != "xno" -a "x$ac_cv_func_kqueue" = "xyes" ; then squid_opt_io_loop_engine="kqueue" +elif test "x$enable_devpoll" != "xno" ; then + squid_opt_io_loop_engine="devpoll" elif test "x$enable_poll" != "xno" -a "x$ac_cv_func_poll" = "xyes" ; then squid_opt_io_loop_engine="poll" elif test "x$enable_select" != "xno" -a "x$ac_cv_func_select" = "xyes" ; then @@ -3032,7 +3067,7 @@ elif test "x$enable_select" != "xno" -a "x$ac_cv_func_select" = "xyes" ; then squid_opt_io_loop_engine="select_win32" fi else - AC_MSG_WARN([Eep! Cannot find poll, kqueue, epoll, or select!]) + AC_MSG_WARN([Eep! Cannot find epoll, kqueue, /dev/poll, poll or select!]) AC_MSG_WARN([Will try select and hope for the best.]) squid_opt_io_loop_engine="select" fi @@ -3049,6 +3084,7 @@ AM_CONDITIONAL([USE_DEVPOLL], [test $squid_opt_io_loop_engine = devpoll]) case $squid_opt_io_loop_engine in epoll) AC_DEFINE(USE_EPOLL,1,[Use epoll() for the IO loop]) ;; + devpoll) AC_DEFINE(USE_DEVPOLL,1,[Use /dev/poll for the IO loop]) ;; poll) AC_DEFINE(USE_POLL,1,[Use poll() for the IO loop]) ;; kqueue) AC_DEFINE(USE_KQUEUE,1,[Use kqueue() for the IO loop]) ;; select_win32) AC_DEFINE(USE_SELECT_WIN32,1,[Use Winsock select() for the IO loop]) ;; diff --git a/src/comm_devpoll.cc b/src/comm_devpoll.cc new file mode 100644 index 0000000000..953386c36b --- /dev/null +++ b/src/comm_devpoll.cc @@ -0,0 +1,476 @@ +/* + * $Id$ + * + * DEBUG: section 05 Socket Functions + * + * SQUID Web Proxy Cache http://www.squid-cache.org/ + * ---------------------------------------------------------- + * + * Squid is the result of efforts by numerous individuals from + * the Internet community; see the CONTRIBUTORS file for full + * details. Many organizations have provided support for Squid's + * development; see the SPONSORS file for full details. Squid is + * Copyrighted (C) 2001 by the Regents of the University of + * California; see the COPYRIGHT file for full details. Squid + * incorporates software developed and/or copyrighted by other + * sources; see the CREDITS file for full details. + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. + * + */ + +/* + * This is a very simple driver for Solaris /dev/poll. + * + * The updates are batched, one trip through the comm loop. + * (like libevent.) We keep a pointer into the structs so we + * can zero out an entry in the poll list if its active. + * + * Ported by Peter Payne from Squid 2.7.STABLE9 comm_devpoll.c + * on August 11, 2010 at 3pm (GMT+0100 Europe/London). + * + * Last modified 2010-10-08 + */ + + +#include "squid.h" + +/* + * There are several poll types in Squid, ALL of which are compiled and linked + * in. Thus conditional compile-time flags are used to prevent the different + * modules from creating several versions of the same function simultaneously. + */ + +#if USE_DEVPOLL + +#include "CacheManager.h" +#include "Store.h" +#include "fde.h" +#include "SquidTime.h" + +#if HAVE_SYS_DEVPOLL_H +/* Solaris /dev/poll support, see "man -s 7D poll" */ +#include +#endif + +#define DEBUG_DEVPOLL 0 + +/* OPEN_MAX is defined in , presumably included by sys/devpoll.h */ +#define DEVPOLL_UPDATESIZE OPEN_MAX +#define DEVPOLL_QUERYSIZE OPEN_MAX + +/* TYPEDEFS */ +typedef short pollfd_events_t; /* type of pollfd.events from sys/poll.h */ + +/* STRUCTURES */ +/** \brief Current state */ +struct _devpoll_state { + pollfd_events_t state; /**< current known state of file handle */ +}; + +/** \brief Update list + * + * This structure contains an array of settings to send to the /dev/poll + * interface. Rather than send changes to /dev/poll one at a time they + * are pushed onto this array (updating cur to indicate how many of the + * pfds structure elements have been set) until it is full before it + * is written out the API. + */ +struct { + struct pollfd *pfds; /**< ptr to array of struct pollfd config elements */ + int cur; /**< index of last written element of array, or -1 if none */ + int size; /**< maximum number of elements in array */ +} devpoll_update; + + +/* STATIC VARIABLES */ +static int devpoll_fd; /**< handle to /dev/poll device */ +static int max_poll_time = 1000; /**< maximum milliseconds to spend in poll */ + +static struct _devpoll_state *devpoll_state; /**< array of socket states */ +static struct dvpoll do_poll; /**< data struct for storing poll results */ +static int dpoll_nfds; /**< maximum number of poll results */ + +/* PROTOTYPES */ +static void commDevPollRegisterWithCacheManager(void); + + +/* PRIVATE FUNCTIONS */ +/** \brief Write batched file descriptor event changes to poll device + * + * Writes out the static array of file descriptor event changes to the + * poll device. This is done only when necessary (i.e. just before + * the poll device is queried during the select call, and whenever + * the number of changes to store in the array exceeds the size of the + * array). + */ +static void +comm_flush_updates(void) +{ + int i; + if (devpoll_update.cur == -1) + return; /* array of changes to make is empty */ + + debugs( + 5, + DEBUG_DEVPOLL ? 0 : 8, + HERE << (devpoll_update.cur + 1) << " fds queued" + ); + + i = write( + devpoll_fd, /* open handle to /dev/poll */ + devpoll_update.pfds, /* pointer to array of struct pollfd */ + (devpoll_update.cur + 1) * sizeof(struct pollfd) /* bytes to process */ + ); + assert(i > 0); + assert(i == sizeof(struct pollfd) * (devpoll_update.cur + 1)); + devpoll_update.cur = -1; /* reset size of array, no elements remain */ +} + +/** \brief Register change in desired polling state for file descriptor + * + * Prevents unnecessary calls to the /dev/poll API by queueing changes + * in the devpoll_update array. If the array fills up the comm_flush_updates + * function is called. + * + * @param fd file descriptor to register change with + * @param events events to register (usually POLLIN, POLLOUT, or POLLREMOVE) + */ +static void +comm_update_fd(int fd, int events) +{ + debugs( + 5, + DEBUG_DEVPOLL ? 0 : 8, + HERE << "FD " << fd << ", events=" << events + ); + + /* Is the array already full and in need of flushing? */ + if (devpoll_update.cur != -1 && (devpoll_update.cur == devpoll_update.size)) + comm_flush_updates(); + + /* Push new event onto array */ + devpoll_update.cur++; + devpoll_update.pfds[devpoll_update.cur].fd = fd; + devpoll_update.pfds[devpoll_update.cur].events = events; + devpoll_update.pfds[devpoll_update.cur].revents = 0; +} + + +static void commIncomingStats(StoreEntry *sentry) +{ + StatCounters *f = &statCounter; + storeAppendPrintf(sentry, "Total number of devpoll loops: %ld\n", statCounter.select_loops); + storeAppendPrintf(sentry, "Histogram of returned filedescriptors\n"); + statHistDump(&f->select_fds_hist, sentry, statHistIntDumper); +} + + +static void +commDevPollRegisterWithCacheManager(void) +{ + CacheManager::GetInstance()-> + registerAction( + "comm_devpoll_incoming", + "comm_incoming() stats", + commIncomingStats, + 0, + 1 + ); +} + + +/* PUBLIC FUNCTIONS */ + +/** \brief Initialise /dev/poll support + * + * Allocates memory, opens /dev/poll device handle. + */ +void +comm_select_init(void) +{ + /* allocate memory first before attempting to open poll device */ + /* This tracks the FD devpoll offset+state */ + devpoll_state = (struct _devpoll_state *)xcalloc( + SQUID_MAXFD, sizeof(struct _devpoll_state) + ); + + /* And this is the stuff we use to read events */ + do_poll.dp_fds = (struct pollfd *)xcalloc( + DEVPOLL_QUERYSIZE, sizeof(struct pollfd) + ); + dpoll_nfds = DEVPOLL_QUERYSIZE; + + devpoll_update.pfds = (struct pollfd *)xcalloc( + DEVPOLL_UPDATESIZE, sizeof(struct pollfd) + ); + devpoll_update.cur = -1; + devpoll_update.size = DEVPOLL_UPDATESIZE; + + /* attempt to open /dev/poll device */ + devpoll_fd = open("/dev/poll", O_RDWR); + if (devpoll_fd < 0) + fatalf("comm_select_init: can't open /dev/poll: %s\n", xstrerror()); + + fd_open(devpoll_fd, FD_UNKNOWN, "devpoll ctl"); + + commDevPollRegisterWithCacheManager(); +} + +/** \brief Set polling state of file descriptor and callback functions + * + * Sets requested polling state for given file handle along with + * desired callback function in the event the request event triggers. + * + * Note that setting a polling state with a NULL callback function will + * clear the polling for that event on that file descriptor. + * + * @param fd file descriptor to change + * @param type may be COMM_SELECT_READ (input) or COMM_SELECT_WRITE (output) + * @param handler callback function, or NULL to stop type of polling + * @param client_data pointer to be provided to call back function + * @param timeout if non-zero then timeout relative to now + */ +void +commSetSelect(int fd, unsigned int type, PF * handler, + void *client_data, time_t timeout) +{ + assert(fd >= 0); + debugs( + 5, + DEBUG_DEVPOLL ? 0 : 8, + HERE << "FD " << fd << ",type=" << type + << ",handler=" << handler << ",client_data=" << client_data + << ",timeout=" << timeout << ")" + ); + + /* POLLIN/POLLOUT are defined in */ + fde *F = &fd_table[fd]; + if (!F->flags.open) { + /* remove from poll set */ + comm_update_fd( fd, POLLREMOVE ); + devpoll_state[fd].state = 0; + return; + } + + pollfd_events_t state_old = devpoll_state[fd].state; + pollfd_events_t state_new = 0; /* new state (derive from old state) */ + + if ( type & COMM_SELECT_READ ) { + if ( handler != NULL ) { + /* we want to POLLIN */ + state_new |= POLLIN; + } else { + ; /* we want to clear POLLIN because handler is NULL */ + } + + F->read_handler = handler; + F->read_data = client_data; + } else if ( state_old & POLLIN ) { + /* we're not changing reading state so take from existing */ + state_new |= POLLIN; + } + + if ( type & COMM_SELECT_WRITE ) { + if ( handler != NULL ) { + /* we want to POLLOUT */ + state_new |= POLLOUT; + } else { + ; /* we want to clear POLLOUT because handler is NULL */ + } + + F->write_handler = handler; + F->write_data = client_data; + } else if ( state_old & POLLOUT ) { + /* we're not changing writing state so take from existing */ + state_new |= POLLOUT; + } + + if ( pollfd_events_t bits_changed = (state_old ^ state_new) ) { + /* something has changed, update /dev/poll of what to listen for */ + + /* did any bits clear? (in which case a poll remove is necessary) */ + if ( bits_changed & state_old ) { + comm_update_fd( fd, POLLREMOVE ); + /* existing state cleared, so update with all required events */ + if ( state_new ) + comm_update_fd( fd, state_new ); + } else { + /* only update with new required event */ + if ( pollfd_events_t newly_set_only = (bits_changed & state_new) ) + comm_update_fd( fd, newly_set_only ); + } + + devpoll_state[fd].state = state_new; + } + + if (timeout) + F->timeout = squid_curtime + timeout; +} + + +/** \brief Clear polling of file handle (both read and write) + * + * @param fd file descriptor to clear polling on + */ +void +commResetSelect(int fd) +{ + commSetSelect(fd, COMM_SELECT_WRITE, NULL, NULL, 0); + commSetSelect(fd, COMM_SELECT_READ, NULL, NULL, 0); +} + + +/** \brief Do poll and trigger callback functions as appropriate + * + * Check all connections for new connections and input data that is to be + * processed. Also check for connections with data queued and whether we can + * write it out. + * + * Called to do the new-style IO, courtesy of of squid (like most of this + * new IO code). This routine handles the stuff we've hidden in + * comm_setselect and fd_table[] and calls callbacks for IO ready + * events. + * + * @param msec milliseconds to poll for (limited by max_poll_time) + */ +comm_err_t +comm_select(int msec) +{ + int num, i; + fde *F; + PF *hdl; + + PROF_start(comm_check_incoming); + + if (msec > max_poll_time) + msec = max_poll_time; + + for (;;) { + do_poll.dp_timeout = msec; + do_poll.dp_nfds = dpoll_nfds; + + comm_flush_updates(); /* ensure latest changes are sent to /dev/poll */ + + num = ioctl(devpoll_fd, DP_POLL, &do_poll); + ++statCounter.select_loops; + + if (num >= 0) + break; /* no error, skip out of loop */ + + if (ignoreErrno(errno)) + break; /* error is one we may ignore, skip out of loop */ + + /* error during poll */ + getCurrentTime(); + PROF_stop(comm_check_incoming); + return COMM_ERROR; + } + + PROF_stop(comm_check_incoming); + getCurrentTime(); + + statHistCount(&statCounter.select_fds_hist, num); + + if (num == 0) + return COMM_TIMEOUT; /* no error */ + + PROF_start(comm_handle_ready_fd); + + for (i = 0; i < num; i++) { + int fd = (int)do_poll.dp_fds[i].fd; + F = &fd_table[fd]; + debugs( + 5, + DEBUG_DEVPOLL ? 0 : 8, + HERE << "got FD " << fd + << ",events=" << std::hex << do_poll.dp_fds[i].revents + << ",monitoring=" << devpoll_state[fd].state + << ",F->read_handler=" << F->read_handler + << ",F->write_handler=" << F->write_handler + ); + + /* handle errors */ + if (do_poll.dp_fds[i].revents & (POLLERR | POLLHUP | POLLNVAL)) { + debugs( + 5, + DEBUG_DEVPOLL ? 0 : 8, + HERE << "devpoll event error: fd " << fd + ); + continue; + } + + /* check if file descriptor has data to read */ + if (do_poll.dp_fds[i].revents & POLLIN || F->flags.read_pending) { + if ( (hdl = F->read_handler) != NULL ) { + debugs( + 5, + DEBUG_DEVPOLL ? 0 : 8, + HERE << "Calling read handler on FD " << fd + ); + PROF_start(comm_read_handler); + F->flags.read_pending = 0; + F->read_handler = NULL; + hdl(fd, F->read_data); + PROF_stop(comm_read_handler); + statCounter.select_fds++; + } else { + debugs( + 5, + DEBUG_DEVPOLL ? 0 : 8, + HERE << "no read handler for FD " << fd + ); + // remove interest since no handler exist for this event. + commSetSelect(fd, COMM_SELECT_READ, NULL, NULL, 0); + } + } + + /* check if file descriptor is ready to write */ + if (do_poll.dp_fds[i].revents & POLLOUT) { + if ((hdl = F->write_handler) != NULL) { + debugs( + 5, + DEBUG_DEVPOLL ? 0 : 8, + HERE << "Calling write handler on FD " << fd + ); + PROF_start(comm_write_handler); + F->write_handler = NULL; + hdl(fd, F->write_data); + PROF_stop(comm_write_handler); + statCounter.select_fds++; + } else { + debugs( + 5, + DEBUG_DEVPOLL ? 0 : 8, + HERE << "no write handler for FD " << fd + ); + // remove interest since no handler exist for this event. + commSetSelect(fd, COMM_SELECT_WRITE, NULL, NULL, 0); + } + } + } + + PROF_stop(comm_handle_ready_fd); + return COMM_OK; +} + + +void +comm_quick_poll_required(void) +{ + max_poll_time = 10; +} + +#endif /* USE_DEVPOLL */ diff --git a/src/unlinkd.cc b/src/unlinkd.cc index d0eed07c13..3e9a7d121a 100644 --- a/src/unlinkd.cc +++ b/src/unlinkd.cc @@ -69,7 +69,7 @@ unlinkdUnlink(const char *path) * time. */ if (queuelen >= UNLINKD_QUEUE_LIMIT) { -#if defined(USE_EPOLL) || defined(USE_KQUEUE) +#if defined(USE_EPOLL) || defined(USE_KQUEUE) || defined(USE_DEVPOLL) /* * DPW 2007-04-23 * We can't use fd_set when using epoll() or kqueue(). In