]>
Commit | Line | Data |
---|---|---|
a1ad2f9b AJ |
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 | ||
a1ad2f9b AJ |
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 | ||
f7f3304a | 53 | #include "squid.h" |
d841c88d | 54 | |
a1ad2f9b AJ |
55 | #if USE_DEVPOLL |
56 | ||
f7f3304a | 57 | #include "squid-old.h" |
d841c88d | 58 | #include "comm/Loops.h" |
a1ad2f9b | 59 | #include "fde.h" |
8af645e7 | 60 | #include "mgr/Registration.h" |
a1ad2f9b | 61 | #include "SquidTime.h" |
e1656dc4 | 62 | #include "StatCounters.h" |
00a7574e | 63 | #include "StatHist.h" |
d841c88d | 64 | #include "Store.h" |
a1ad2f9b AJ |
65 | |
66 | #if HAVE_SYS_DEVPOLL_H | |
67 | /* Solaris /dev/poll support, see "man -s 7D poll" */ | |
68 | #include <sys/devpoll.h> | |
69 | #endif | |
70 | ||
71 | #define DEBUG_DEVPOLL 0 | |
72 | ||
73 | /* OPEN_MAX is defined in <limits.h>, presumably included by sys/devpoll.h */ | |
74 | #define DEVPOLL_UPDATESIZE OPEN_MAX | |
75 | #define DEVPOLL_QUERYSIZE OPEN_MAX | |
76 | ||
77 | /* TYPEDEFS */ | |
78 | typedef short pollfd_events_t; /* type of pollfd.events from sys/poll.h */ | |
79 | ||
80 | /* STRUCTURES */ | |
81 | /** \brief Current state */ | |
82 | struct _devpoll_state { | |
83 | pollfd_events_t state; /**< current known state of file handle */ | |
84 | }; | |
85 | ||
86 | /** \brief Update list | |
87 | * | |
88 | * This structure contains an array of settings to send to the /dev/poll | |
89 | * interface. Rather than send changes to /dev/poll one at a time they | |
90 | * are pushed onto this array (updating cur to indicate how many of the | |
91 | * pfds structure elements have been set) until it is full before it | |
92 | * is written out the API. | |
93 | */ | |
14302ffa | 94 | static struct { |
a1ad2f9b AJ |
95 | struct pollfd *pfds; /**< ptr to array of struct pollfd config elements */ |
96 | int cur; /**< index of last written element of array, or -1 if none */ | |
97 | int size; /**< maximum number of elements in array */ | |
98 | } devpoll_update; | |
99 | ||
100 | ||
101 | /* STATIC VARIABLES */ | |
102 | static int devpoll_fd; /**< handle to /dev/poll device */ | |
103 | static int max_poll_time = 1000; /**< maximum milliseconds to spend in poll */ | |
104 | ||
105 | static struct _devpoll_state *devpoll_state; /**< array of socket states */ | |
106 | static struct dvpoll do_poll; /**< data struct for storing poll results */ | |
107 | static int dpoll_nfds; /**< maximum number of poll results */ | |
108 | ||
109 | /* PROTOTYPES */ | |
110 | static void commDevPollRegisterWithCacheManager(void); | |
111 | ||
112 | ||
113 | /* PRIVATE FUNCTIONS */ | |
114 | /** \brief Write batched file descriptor event changes to poll device | |
115 | * | |
116 | * Writes out the static array of file descriptor event changes to the | |
117 | * poll device. This is done only when necessary (i.e. just before | |
118 | * the poll device is queried during the select call, and whenever | |
119 | * the number of changes to store in the array exceeds the size of the | |
120 | * array). | |
121 | */ | |
122 | static void | |
123 | comm_flush_updates(void) | |
124 | { | |
125 | int i; | |
126 | if (devpoll_update.cur == -1) | |
127 | return; /* array of changes to make is empty */ | |
128 | ||
129 | debugs( | |
130 | 5, | |
131 | DEBUG_DEVPOLL ? 0 : 8, | |
132 | HERE << (devpoll_update.cur + 1) << " fds queued" | |
133 | ); | |
134 | ||
135 | i = write( | |
136 | devpoll_fd, /* open handle to /dev/poll */ | |
137 | devpoll_update.pfds, /* pointer to array of struct pollfd */ | |
138 | (devpoll_update.cur + 1) * sizeof(struct pollfd) /* bytes to process */ | |
139 | ); | |
140 | assert(i > 0); | |
8b37be18 | 141 | assert(static_cast<size_t>(i) == (sizeof(struct pollfd) * (devpoll_update.cur + 1))); |
a1ad2f9b AJ |
142 | devpoll_update.cur = -1; /* reset size of array, no elements remain */ |
143 | } | |
144 | ||
145 | /** \brief Register change in desired polling state for file descriptor | |
146 | * | |
147 | * Prevents unnecessary calls to the /dev/poll API by queueing changes | |
148 | * in the devpoll_update array. If the array fills up the comm_flush_updates | |
149 | * function is called. | |
150 | * | |
151 | * @param fd file descriptor to register change with | |
152 | * @param events events to register (usually POLLIN, POLLOUT, or POLLREMOVE) | |
153 | */ | |
154 | static void | |
155 | comm_update_fd(int fd, int events) | |
156 | { | |
157 | debugs( | |
158 | 5, | |
159 | DEBUG_DEVPOLL ? 0 : 8, | |
160 | HERE << "FD " << fd << ", events=" << events | |
161 | ); | |
162 | ||
163 | /* Is the array already full and in need of flushing? */ | |
164 | if (devpoll_update.cur != -1 && (devpoll_update.cur == devpoll_update.size)) | |
165 | comm_flush_updates(); | |
166 | ||
167 | /* Push new event onto array */ | |
168 | devpoll_update.cur++; | |
169 | devpoll_update.pfds[devpoll_update.cur].fd = fd; | |
170 | devpoll_update.pfds[devpoll_update.cur].events = events; | |
171 | devpoll_update.pfds[devpoll_update.cur].revents = 0; | |
172 | } | |
173 | ||
174 | ||
175 | static void commIncomingStats(StoreEntry *sentry) | |
176 | { | |
a1ad2f9b AJ |
177 | storeAppendPrintf(sentry, "Total number of devpoll loops: %ld\n", statCounter.select_loops); |
178 | storeAppendPrintf(sentry, "Histogram of returned filedescriptors\n"); | |
82146cc8 | 179 | statCounter.select_fds_hist.dump(sentry, statHistIntDumper); |
a1ad2f9b AJ |
180 | } |
181 | ||
182 | ||
183 | static void | |
184 | commDevPollRegisterWithCacheManager(void) | |
185 | { | |
8af645e7 | 186 | Mgr::RegisterAction( |
a1ad2f9b AJ |
187 | "comm_devpoll_incoming", |
188 | "comm_incoming() stats", | |
189 | commIncomingStats, | |
190 | 0, | |
191 | 1 | |
192 | ); | |
193 | } | |
194 | ||
195 | ||
196 | /* PUBLIC FUNCTIONS */ | |
197 | ||
198 | /** \brief Initialise /dev/poll support | |
199 | * | |
200 | * Allocates memory, opens /dev/poll device handle. | |
201 | */ | |
202 | void | |
d841c88d | 203 | Comm::SelectLoopInit(void) |
a1ad2f9b AJ |
204 | { |
205 | /* allocate memory first before attempting to open poll device */ | |
206 | /* This tracks the FD devpoll offset+state */ | |
207 | devpoll_state = (struct _devpoll_state *)xcalloc( | |
208 | SQUID_MAXFD, sizeof(struct _devpoll_state) | |
209 | ); | |
210 | ||
211 | /* And this is the stuff we use to read events */ | |
212 | do_poll.dp_fds = (struct pollfd *)xcalloc( | |
213 | DEVPOLL_QUERYSIZE, sizeof(struct pollfd) | |
214 | ); | |
215 | dpoll_nfds = DEVPOLL_QUERYSIZE; | |
216 | ||
217 | devpoll_update.pfds = (struct pollfd *)xcalloc( | |
218 | DEVPOLL_UPDATESIZE, sizeof(struct pollfd) | |
219 | ); | |
220 | devpoll_update.cur = -1; | |
221 | devpoll_update.size = DEVPOLL_UPDATESIZE; | |
222 | ||
223 | /* attempt to open /dev/poll device */ | |
224 | devpoll_fd = open("/dev/poll", O_RDWR); | |
225 | if (devpoll_fd < 0) | |
226 | fatalf("comm_select_init: can't open /dev/poll: %s\n", xstrerror()); | |
227 | ||
228 | fd_open(devpoll_fd, FD_UNKNOWN, "devpoll ctl"); | |
229 | ||
230 | commDevPollRegisterWithCacheManager(); | |
231 | } | |
232 | ||
233 | /** \brief Set polling state of file descriptor and callback functions | |
234 | * | |
235 | * Sets requested polling state for given file handle along with | |
236 | * desired callback function in the event the request event triggers. | |
237 | * | |
238 | * Note that setting a polling state with a NULL callback function will | |
239 | * clear the polling for that event on that file descriptor. | |
240 | * | |
241 | * @param fd file descriptor to change | |
242 | * @param type may be COMM_SELECT_READ (input) or COMM_SELECT_WRITE (output) | |
243 | * @param handler callback function, or NULL to stop type of polling | |
244 | * @param client_data pointer to be provided to call back function | |
245 | * @param timeout if non-zero then timeout relative to now | |
246 | */ | |
247 | void | |
d841c88d | 248 | Comm::SetSelect(int fd, unsigned int type, PF * handler, void *client_data, time_t timeout) |
a1ad2f9b AJ |
249 | { |
250 | assert(fd >= 0); | |
48e7baac AJ |
251 | debugs(5, 5, HERE << "FD " << fd << ", type=" << type << |
252 | ", handler=" << handler << ", client_data=" << client_data << | |
253 | ", timeout=" << timeout); | |
a1ad2f9b AJ |
254 | |
255 | /* POLLIN/POLLOUT are defined in <sys/poll.h> */ | |
256 | fde *F = &fd_table[fd]; | |
257 | if (!F->flags.open) { | |
258 | /* remove from poll set */ | |
259 | comm_update_fd( fd, POLLREMOVE ); | |
260 | devpoll_state[fd].state = 0; | |
261 | return; | |
262 | } | |
263 | ||
264 | pollfd_events_t state_old = devpoll_state[fd].state; | |
265 | pollfd_events_t state_new = 0; /* new state (derive from old state) */ | |
266 | ||
267 | if ( type & COMM_SELECT_READ ) { | |
268 | if ( handler != NULL ) { | |
269 | /* we want to POLLIN */ | |
270 | state_new |= POLLIN; | |
271 | } else { | |
272 | ; /* we want to clear POLLIN because handler is NULL */ | |
273 | } | |
274 | ||
275 | F->read_handler = handler; | |
276 | F->read_data = client_data; | |
277 | } else if ( state_old & POLLIN ) { | |
278 | /* we're not changing reading state so take from existing */ | |
279 | state_new |= POLLIN; | |
280 | } | |
281 | ||
282 | if ( type & COMM_SELECT_WRITE ) { | |
283 | if ( handler != NULL ) { | |
284 | /* we want to POLLOUT */ | |
285 | state_new |= POLLOUT; | |
286 | } else { | |
287 | ; /* we want to clear POLLOUT because handler is NULL */ | |
288 | } | |
289 | ||
290 | F->write_handler = handler; | |
291 | F->write_data = client_data; | |
292 | } else if ( state_old & POLLOUT ) { | |
293 | /* we're not changing writing state so take from existing */ | |
294 | state_new |= POLLOUT; | |
295 | } | |
296 | ||
297 | if ( pollfd_events_t bits_changed = (state_old ^ state_new) ) { | |
298 | /* something has changed, update /dev/poll of what to listen for */ | |
299 | ||
300 | /* did any bits clear? (in which case a poll remove is necessary) */ | |
301 | if ( bits_changed & state_old ) { | |
302 | comm_update_fd( fd, POLLREMOVE ); | |
303 | /* existing state cleared, so update with all required events */ | |
304 | if ( state_new ) | |
305 | comm_update_fd( fd, state_new ); | |
306 | } else { | |
307 | /* only update with new required event */ | |
308 | if ( pollfd_events_t newly_set_only = (bits_changed & state_new) ) | |
309 | comm_update_fd( fd, newly_set_only ); | |
310 | } | |
311 | ||
312 | devpoll_state[fd].state = state_new; | |
313 | } | |
314 | ||
315 | if (timeout) | |
316 | F->timeout = squid_curtime + timeout; | |
317 | } | |
318 | ||
319 | ||
320 | /** \brief Clear polling of file handle (both read and write) | |
321 | * | |
322 | * @param fd file descriptor to clear polling on | |
323 | */ | |
324 | void | |
d841c88d | 325 | Comm::ResetSelect(int fd) |
a1ad2f9b | 326 | { |
d841c88d AJ |
327 | SetSelect(fd, COMM_SELECT_WRITE, NULL, NULL, 0); |
328 | SetSelect(fd, COMM_SELECT_READ, NULL, NULL, 0); | |
a1ad2f9b AJ |
329 | } |
330 | ||
331 | ||
332 | /** \brief Do poll and trigger callback functions as appropriate | |
333 | * | |
334 | * Check all connections for new connections and input data that is to be | |
335 | * processed. Also check for connections with data queued and whether we can | |
336 | * write it out. | |
337 | * | |
338 | * Called to do the new-style IO, courtesy of of squid (like most of this | |
339 | * new IO code). This routine handles the stuff we've hidden in | |
340 | * comm_setselect and fd_table[] and calls callbacks for IO ready | |
341 | * events. | |
342 | * | |
343 | * @param msec milliseconds to poll for (limited by max_poll_time) | |
344 | */ | |
345 | comm_err_t | |
d841c88d | 346 | Comm::DoSelect(int msec) |
a1ad2f9b AJ |
347 | { |
348 | int num, i; | |
349 | fde *F; | |
350 | PF *hdl; | |
351 | ||
352 | PROF_start(comm_check_incoming); | |
353 | ||
354 | if (msec > max_poll_time) | |
355 | msec = max_poll_time; | |
356 | ||
357 | for (;;) { | |
358 | do_poll.dp_timeout = msec; | |
359 | do_poll.dp_nfds = dpoll_nfds; | |
360 | ||
361 | comm_flush_updates(); /* ensure latest changes are sent to /dev/poll */ | |
362 | ||
363 | num = ioctl(devpoll_fd, DP_POLL, &do_poll); | |
364 | ++statCounter.select_loops; | |
365 | ||
366 | if (num >= 0) | |
367 | break; /* no error, skip out of loop */ | |
368 | ||
369 | if (ignoreErrno(errno)) | |
370 | break; /* error is one we may ignore, skip out of loop */ | |
371 | ||
372 | /* error during poll */ | |
373 | getCurrentTime(); | |
374 | PROF_stop(comm_check_incoming); | |
375 | return COMM_ERROR; | |
376 | } | |
377 | ||
378 | PROF_stop(comm_check_incoming); | |
379 | getCurrentTime(); | |
380 | ||
f30f7998 | 381 | statCounter.select_fds_hist.count(num); |
a1ad2f9b AJ |
382 | |
383 | if (num == 0) | |
384 | return COMM_TIMEOUT; /* no error */ | |
385 | ||
386 | PROF_start(comm_handle_ready_fd); | |
387 | ||
388 | for (i = 0; i < num; i++) { | |
389 | int fd = (int)do_poll.dp_fds[i].fd; | |
390 | F = &fd_table[fd]; | |
391 | debugs( | |
392 | 5, | |
393 | DEBUG_DEVPOLL ? 0 : 8, | |
394 | HERE << "got FD " << fd | |
395 | << ",events=" << std::hex << do_poll.dp_fds[i].revents | |
396 | << ",monitoring=" << devpoll_state[fd].state | |
397 | << ",F->read_handler=" << F->read_handler | |
398 | << ",F->write_handler=" << F->write_handler | |
399 | ); | |
400 | ||
401 | /* handle errors */ | |
402 | if (do_poll.dp_fds[i].revents & (POLLERR | POLLHUP | POLLNVAL)) { | |
403 | debugs( | |
404 | 5, | |
405 | DEBUG_DEVPOLL ? 0 : 8, | |
406 | HERE << "devpoll event error: fd " << fd | |
407 | ); | |
408 | continue; | |
409 | } | |
410 | ||
411 | /* check if file descriptor has data to read */ | |
412 | if (do_poll.dp_fds[i].revents & POLLIN || F->flags.read_pending) { | |
413 | if ( (hdl = F->read_handler) != NULL ) { | |
414 | debugs( | |
415 | 5, | |
416 | DEBUG_DEVPOLL ? 0 : 8, | |
417 | HERE << "Calling read handler on FD " << fd | |
418 | ); | |
419 | PROF_start(comm_read_handler); | |
420 | F->flags.read_pending = 0; | |
421 | F->read_handler = NULL; | |
422 | hdl(fd, F->read_data); | |
423 | PROF_stop(comm_read_handler); | |
424 | statCounter.select_fds++; | |
425 | } else { | |
426 | debugs( | |
427 | 5, | |
428 | DEBUG_DEVPOLL ? 0 : 8, | |
429 | HERE << "no read handler for FD " << fd | |
430 | ); | |
431 | // remove interest since no handler exist for this event. | |
d841c88d | 432 | SetSelect(fd, COMM_SELECT_READ, NULL, NULL, 0); |
a1ad2f9b AJ |
433 | } |
434 | } | |
435 | ||
436 | /* check if file descriptor is ready to write */ | |
437 | if (do_poll.dp_fds[i].revents & POLLOUT) { | |
438 | if ((hdl = F->write_handler) != NULL) { | |
439 | debugs( | |
440 | 5, | |
441 | DEBUG_DEVPOLL ? 0 : 8, | |
442 | HERE << "Calling write handler on FD " << fd | |
443 | ); | |
444 | PROF_start(comm_write_handler); | |
445 | F->write_handler = NULL; | |
446 | hdl(fd, F->write_data); | |
447 | PROF_stop(comm_write_handler); | |
448 | statCounter.select_fds++; | |
449 | } else { | |
450 | debugs( | |
451 | 5, | |
452 | DEBUG_DEVPOLL ? 0 : 8, | |
453 | HERE << "no write handler for FD " << fd | |
454 | ); | |
455 | // remove interest since no handler exist for this event. | |
d841c88d | 456 | SetSelect(fd, COMM_SELECT_WRITE, NULL, NULL, 0); |
a1ad2f9b AJ |
457 | } |
458 | } | |
459 | } | |
460 | ||
461 | PROF_stop(comm_handle_ready_fd); | |
462 | return COMM_OK; | |
463 | } | |
464 | ||
a1ad2f9b | 465 | void |
d841c88d | 466 | Comm::QuickPollRequired(void) |
a1ad2f9b AJ |
467 | { |
468 | max_poll_time = 10; | |
469 | } | |
470 | ||
471 | #endif /* USE_DEVPOLL */ |