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