]> git.ipfire.org Git - thirdparty/squid.git/commitdiff
Author: Adrian Chadd <adri@squid-cache.org>
authorAmos Jeffries <amosjeffries@squid-cache.org>
Sun, 17 Oct 2010 04:49:27 +0000 (22:49 -0600)
committerAmos Jeffries <amosjeffries@squid-cache.org>
Sun, 17 Oct 2010 04:49:27 +0000 (22:49 -0600)
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)

SPONSORS
acinclude/os-deps.m4
compat/compat_shared.h
configure.in
src/comm_devpoll.cc [new file with mode: 0644]
src/unlinkd.cc

index 8e850de8db252f74ec1bc72c9fe35f44cde25a60..02855238f13128718da5dc2f9f211651e147634e 100644 (file)
--- 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.
index 2ae250bd9baf491439676d5f5e7ba907548e3833..a71c8ecb43ce8ab71b30f149bad0d6856bb60bbe 100644 (file)
@@ -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 <sys/devpoll.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <stdio.h>
+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"
index 3730b8cd838e02874f5f3321085ffc5cec34755e..35fdabb6f290d438a94a7e8f0b80d927910bb147 100644 (file)
@@ -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
index 3ea7d60d94e069cf5abcd60b7e08c79cd7ec5dcc..9c38b6949f880b2df466af6a31ded9cf5a953a8b 100644 (file)
@@ -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 (file)
index 0000000..953386c
--- /dev/null
@@ -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 <sys/devpoll.h>
+#endif
+
+#define DEBUG_DEVPOLL 0
+
+/* OPEN_MAX is defined in <limits.h>, 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 <sys/poll.h> */
+    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 */
index d0eed07c1342159f9bdd580619fc312321c58c2a..3e9a7d121a2c004217701b561e32d1ad4e74b3d9 100644 (file)
@@ -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