]> git.ipfire.org Git - thirdparty/squid.git/blob - src/comm/ModDevPoll.cc
Merge from trunk. Remove dead files and fluff changes
[thirdparty/squid.git] / src / comm / ModDevPoll.cc
1 /*
2 * $Id$
3 *
4 * DEBUG: section 05 Socket Functions
5 *
6 * SQUID Web Proxy Cache http://www.squid-cache.org/
7 * ----------------------------------------------------------
8 *
9 * Squid is the result of efforts by numerous individuals from
10 * the Internet community; see the CONTRIBUTORS file for full
11 * details. Many organizations have provided support for Squid's
12 * development; see the SPONSORS file for full details. Squid is
13 * Copyrighted (C) 2001 by the Regents of the University of
14 * California; see the COPYRIGHT file for full details. Squid
15 * incorporates software developed and/or copyrighted by other
16 * sources; see the CREDITS file for full details.
17 *
18 * This program is free software; you can redistribute it and/or modify
19 * it under the terms of the GNU General Public License as published by
20 * the Free Software Foundation; either version 2 of the License, or
21 * (at your option) any later version.
22 *
23 * This program is distributed in the hope that it will be useful,
24 * but WITHOUT ANY WARRANTY; without even the implied warranty of
25 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
26 * GNU General Public License for more details.
27 *
28 * You should have received a copy of the GNU General Public License
29 * along with this program; if not, write to the Free Software
30 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
31 *
32 */
33
34 /*
35 * This is a very simple driver for Solaris /dev/poll.
36 *
37 * The updates are batched, one trip through the comm loop.
38 * (like libevent.) We keep a pointer into the structs so we
39 * can zero out an entry in the poll list if its active.
40 *
41 * Ported by Peter Payne from Squid 2.7.STABLE9 comm_devpoll.c
42 * on August 11, 2010 at 3pm (GMT+0100 Europe/London).
43 *
44 * Last modified 2010-10-08
45 */
46
47 /*
48 * There are several poll types in Squid, ALL of which are compiled and linked
49 * in. Thus conditional compile-time flags are used to prevent the different
50 * modules from creating several versions of the same function simultaneously.
51 */
52
53 #include "config.h"
54
55 #if USE_DEVPOLL
56
57 #include "squid.h"
58 #include "comm/Loops.h"
59 #include "fde.h"
60 #include "mgr/Registration.h"
61 #include "SquidTime.h"
62 #include "Store.h"
63
64 #if HAVE_SYS_DEVPOLL_H
65 /* Solaris /dev/poll support, see "man -s 7D poll" */
66 #include <sys/devpoll.h>
67 #endif
68
69 #define DEBUG_DEVPOLL 0
70
71 /* OPEN_MAX is defined in <limits.h>, presumably included by sys/devpoll.h */
72 #define DEVPOLL_UPDATESIZE OPEN_MAX
73 #define DEVPOLL_QUERYSIZE OPEN_MAX
74
75 /* TYPEDEFS */
76 typedef short pollfd_events_t; /* type of pollfd.events from sys/poll.h */
77
78 /* STRUCTURES */
79 /** \brief Current state */
80 struct _devpoll_state {
81 pollfd_events_t state; /**< current known state of file handle */
82 };
83
84 /** \brief Update list
85 *
86 * This structure contains an array of settings to send to the /dev/poll
87 * interface. Rather than send changes to /dev/poll one at a time they
88 * are pushed onto this array (updating cur to indicate how many of the
89 * pfds structure elements have been set) until it is full before it
90 * is written out the API.
91 */
92 static struct {
93 struct pollfd *pfds; /**< ptr to array of struct pollfd config elements */
94 int cur; /**< index of last written element of array, or -1 if none */
95 int size; /**< maximum number of elements in array */
96 } devpoll_update;
97
98
99 /* STATIC VARIABLES */
100 static int devpoll_fd; /**< handle to /dev/poll device */
101 static int max_poll_time = 1000; /**< maximum milliseconds to spend in poll */
102
103 static struct _devpoll_state *devpoll_state; /**< array of socket states */
104 static struct dvpoll do_poll; /**< data struct for storing poll results */
105 static int dpoll_nfds; /**< maximum number of poll results */
106
107 /* PROTOTYPES */
108 static void commDevPollRegisterWithCacheManager(void);
109
110
111 /* PRIVATE FUNCTIONS */
112 /** \brief Write batched file descriptor event changes to poll device
113 *
114 * Writes out the static array of file descriptor event changes to the
115 * poll device. This is done only when necessary (i.e. just before
116 * the poll device is queried during the select call, and whenever
117 * the number of changes to store in the array exceeds the size of the
118 * array).
119 */
120 static void
121 comm_flush_updates(void)
122 {
123 int i;
124 if (devpoll_update.cur == -1)
125 return; /* array of changes to make is empty */
126
127 debugs(
128 5,
129 DEBUG_DEVPOLL ? 0 : 8,
130 HERE << (devpoll_update.cur + 1) << " fds queued"
131 );
132
133 i = write(
134 devpoll_fd, /* open handle to /dev/poll */
135 devpoll_update.pfds, /* pointer to array of struct pollfd */
136 (devpoll_update.cur + 1) * sizeof(struct pollfd) /* bytes to process */
137 );
138 assert(i > 0);
139 assert(static_cast<size_t>(i) == (sizeof(struct pollfd) * (devpoll_update.cur + 1)));
140 devpoll_update.cur = -1; /* reset size of array, no elements remain */
141 }
142
143 /** \brief Register change in desired polling state for file descriptor
144 *
145 * Prevents unnecessary calls to the /dev/poll API by queueing changes
146 * in the devpoll_update array. If the array fills up the comm_flush_updates
147 * function is called.
148 *
149 * @param fd file descriptor to register change with
150 * @param events events to register (usually POLLIN, POLLOUT, or POLLREMOVE)
151 */
152 static void
153 comm_update_fd(int fd, int events)
154 {
155 debugs(
156 5,
157 DEBUG_DEVPOLL ? 0 : 8,
158 HERE << "FD " << fd << ", events=" << events
159 );
160
161 /* Is the array already full and in need of flushing? */
162 if (devpoll_update.cur != -1 && (devpoll_update.cur == devpoll_update.size))
163 comm_flush_updates();
164
165 /* Push new event onto array */
166 devpoll_update.cur++;
167 devpoll_update.pfds[devpoll_update.cur].fd = fd;
168 devpoll_update.pfds[devpoll_update.cur].events = events;
169 devpoll_update.pfds[devpoll_update.cur].revents = 0;
170 }
171
172
173 static void commIncomingStats(StoreEntry *sentry)
174 {
175 StatCounters *f = &statCounter;
176 storeAppendPrintf(sentry, "Total number of devpoll loops: %ld\n", statCounter.select_loops);
177 storeAppendPrintf(sentry, "Histogram of returned filedescriptors\n");
178 statHistDump(&f->select_fds_hist, sentry, statHistIntDumper);
179 }
180
181
182 static void
183 commDevPollRegisterWithCacheManager(void)
184 {
185 Mgr::RegisterAction(
186 "comm_devpoll_incoming",
187 "comm_incoming() stats",
188 commIncomingStats,
189 0,
190 1
191 );
192 }
193
194
195 /* PUBLIC FUNCTIONS */
196
197 /** \brief Initialise /dev/poll support
198 *
199 * Allocates memory, opens /dev/poll device handle.
200 */
201 void
202 Comm::SelectLoopInit(void)
203 {
204 /* allocate memory first before attempting to open poll device */
205 /* This tracks the FD devpoll offset+state */
206 devpoll_state = (struct _devpoll_state *)xcalloc(
207 SQUID_MAXFD, sizeof(struct _devpoll_state)
208 );
209
210 /* And this is the stuff we use to read events */
211 do_poll.dp_fds = (struct pollfd *)xcalloc(
212 DEVPOLL_QUERYSIZE, sizeof(struct pollfd)
213 );
214 dpoll_nfds = DEVPOLL_QUERYSIZE;
215
216 devpoll_update.pfds = (struct pollfd *)xcalloc(
217 DEVPOLL_UPDATESIZE, sizeof(struct pollfd)
218 );
219 devpoll_update.cur = -1;
220 devpoll_update.size = DEVPOLL_UPDATESIZE;
221
222 /* attempt to open /dev/poll device */
223 devpoll_fd = open("/dev/poll", O_RDWR);
224 if (devpoll_fd < 0)
225 fatalf("comm_select_init: can't open /dev/poll: %s\n", xstrerror());
226
227 fd_open(devpoll_fd, FD_UNKNOWN, "devpoll ctl");
228
229 commDevPollRegisterWithCacheManager();
230 }
231
232 /** \brief Set polling state of file descriptor and callback functions
233 *
234 * Sets requested polling state for given file handle along with
235 * desired callback function in the event the request event triggers.
236 *
237 * Note that setting a polling state with a NULL callback function will
238 * clear the polling for that event on that file descriptor.
239 *
240 * @param fd file descriptor to change
241 * @param type may be COMM_SELECT_READ (input) or COMM_SELECT_WRITE (output)
242 * @param handler callback function, or NULL to stop type of polling
243 * @param client_data pointer to be provided to call back function
244 * @param timeout if non-zero then timeout relative to now
245 */
246 void
247 Comm::SetSelect(int fd, unsigned int type, PF * handler, void *client_data, time_t timeout)
248 {
249 assert(fd >= 0);
250 debugs(
251 5,
252 DEBUG_DEVPOLL ? 0 : 8,
253 HERE << "FD " << fd << ",type=" << type
254 << ",handler=" << handler << ",client_data=" << client_data
255 << ",timeout=" << timeout << ")"
256 );
257
258 /* POLLIN/POLLOUT are defined in <sys/poll.h> */
259 fde *F = &fd_table[fd];
260 if (!F->flags.open) {
261 /* remove from poll set */
262 comm_update_fd( fd, POLLREMOVE );
263 devpoll_state[fd].state = 0;
264 return;
265 }
266
267 pollfd_events_t state_old = devpoll_state[fd].state;
268 pollfd_events_t state_new = 0; /* new state (derive from old state) */
269
270 if ( type & COMM_SELECT_READ ) {
271 if ( handler != NULL ) {
272 /* we want to POLLIN */
273 state_new |= POLLIN;
274 } else {
275 ; /* we want to clear POLLIN because handler is NULL */
276 }
277
278 F->read_handler = handler;
279 F->read_data = client_data;
280 } else if ( state_old & POLLIN ) {
281 /* we're not changing reading state so take from existing */
282 state_new |= POLLIN;
283 }
284
285 if ( type & COMM_SELECT_WRITE ) {
286 if ( handler != NULL ) {
287 /* we want to POLLOUT */
288 state_new |= POLLOUT;
289 } else {
290 ; /* we want to clear POLLOUT because handler is NULL */
291 }
292
293 F->write_handler = handler;
294 F->write_data = client_data;
295 } else if ( state_old & POLLOUT ) {
296 /* we're not changing writing state so take from existing */
297 state_new |= POLLOUT;
298 }
299
300 if ( pollfd_events_t bits_changed = (state_old ^ state_new) ) {
301 /* something has changed, update /dev/poll of what to listen for */
302
303 /* did any bits clear? (in which case a poll remove is necessary) */
304 if ( bits_changed & state_old ) {
305 comm_update_fd( fd, POLLREMOVE );
306 /* existing state cleared, so update with all required events */
307 if ( state_new )
308 comm_update_fd( fd, state_new );
309 } else {
310 /* only update with new required event */
311 if ( pollfd_events_t newly_set_only = (bits_changed & state_new) )
312 comm_update_fd( fd, newly_set_only );
313 }
314
315 devpoll_state[fd].state = state_new;
316 }
317
318 if (timeout)
319 F->timeout = squid_curtime + timeout;
320 }
321
322
323 /** \brief Clear polling of file handle (both read and write)
324 *
325 * @param fd file descriptor to clear polling on
326 */
327 void
328 Comm::ResetSelect(int fd)
329 {
330 SetSelect(fd, COMM_SELECT_WRITE, NULL, NULL, 0);
331 SetSelect(fd, COMM_SELECT_READ, NULL, NULL, 0);
332 }
333
334
335 /** \brief Do poll and trigger callback functions as appropriate
336 *
337 * Check all connections for new connections and input data that is to be
338 * processed. Also check for connections with data queued and whether we can
339 * write it out.
340 *
341 * Called to do the new-style IO, courtesy of of squid (like most of this
342 * new IO code). This routine handles the stuff we've hidden in
343 * comm_setselect and fd_table[] and calls callbacks for IO ready
344 * events.
345 *
346 * @param msec milliseconds to poll for (limited by max_poll_time)
347 */
348 comm_err_t
349 Comm::DoSelect(int msec)
350 {
351 int num, i;
352 fde *F;
353 PF *hdl;
354
355 PROF_start(comm_check_incoming);
356
357 if (msec > max_poll_time)
358 msec = max_poll_time;
359
360 for (;;) {
361 do_poll.dp_timeout = msec;
362 do_poll.dp_nfds = dpoll_nfds;
363
364 comm_flush_updates(); /* ensure latest changes are sent to /dev/poll */
365
366 num = ioctl(devpoll_fd, DP_POLL, &do_poll);
367 ++statCounter.select_loops;
368
369 if (num >= 0)
370 break; /* no error, skip out of loop */
371
372 if (ignoreErrno(errno))
373 break; /* error is one we may ignore, skip out of loop */
374
375 /* error during poll */
376 getCurrentTime();
377 PROF_stop(comm_check_incoming);
378 return COMM_ERROR;
379 }
380
381 PROF_stop(comm_check_incoming);
382 getCurrentTime();
383
384 statHistCount(&statCounter.select_fds_hist, num);
385
386 if (num == 0)
387 return COMM_TIMEOUT; /* no error */
388
389 PROF_start(comm_handle_ready_fd);
390
391 for (i = 0; i < num; i++) {
392 int fd = (int)do_poll.dp_fds[i].fd;
393 F = &fd_table[fd];
394 debugs(
395 5,
396 DEBUG_DEVPOLL ? 0 : 8,
397 HERE << "got FD " << fd
398 << ",events=" << std::hex << do_poll.dp_fds[i].revents
399 << ",monitoring=" << devpoll_state[fd].state
400 << ",F->read_handler=" << F->read_handler
401 << ",F->write_handler=" << F->write_handler
402 );
403
404 /* handle errors */
405 if (do_poll.dp_fds[i].revents & (POLLERR | POLLHUP | POLLNVAL)) {
406 debugs(
407 5,
408 DEBUG_DEVPOLL ? 0 : 8,
409 HERE << "devpoll event error: fd " << fd
410 );
411 continue;
412 }
413
414 /* check if file descriptor has data to read */
415 if (do_poll.dp_fds[i].revents & POLLIN || F->flags.read_pending) {
416 if ( (hdl = F->read_handler) != NULL ) {
417 debugs(
418 5,
419 DEBUG_DEVPOLL ? 0 : 8,
420 HERE << "Calling read handler on FD " << fd
421 );
422 PROF_start(comm_read_handler);
423 F->flags.read_pending = 0;
424 F->read_handler = NULL;
425 hdl(fd, F->read_data);
426 PROF_stop(comm_read_handler);
427 statCounter.select_fds++;
428 } else {
429 debugs(
430 5,
431 DEBUG_DEVPOLL ? 0 : 8,
432 HERE << "no read handler for FD " << fd
433 );
434 // remove interest since no handler exist for this event.
435 SetSelect(fd, COMM_SELECT_READ, NULL, NULL, 0);
436 }
437 }
438
439 /* check if file descriptor is ready to write */
440 if (do_poll.dp_fds[i].revents & POLLOUT) {
441 if ((hdl = F->write_handler) != NULL) {
442 debugs(
443 5,
444 DEBUG_DEVPOLL ? 0 : 8,
445 HERE << "Calling write handler on FD " << fd
446 );
447 PROF_start(comm_write_handler);
448 F->write_handler = NULL;
449 hdl(fd, F->write_data);
450 PROF_stop(comm_write_handler);
451 statCounter.select_fds++;
452 } else {
453 debugs(
454 5,
455 DEBUG_DEVPOLL ? 0 : 8,
456 HERE << "no write handler for FD " << fd
457 );
458 // remove interest since no handler exist for this event.
459 SetSelect(fd, COMM_SELECT_WRITE, NULL, NULL, 0);
460 }
461 }
462 }
463
464 PROF_stop(comm_handle_ready_fd);
465 return COMM_OK;
466 }
467
468 void
469 Comm::QuickPollRequired(void)
470 {
471 max_poll_time = 10;
472 }
473
474 #endif /* USE_DEVPOLL */