]>
Commit | Line | Data |
---|---|---|
6a988308 | 1 | /* |
b510f3a1 | 2 | * DEBUG: section 05 Socket Functions |
6a988308 | 3 | * |
2b6662ba | 4 | * SQUID Web Proxy Cache http://www.squid-cache.org/ |
e25c139f | 5 | * ---------------------------------------------------------- |
6a988308 | 6 | * |
2b6662ba | 7 | * Squid is the result of efforts by numerous individuals from |
8 | * the Internet community; see the CONTRIBUTORS file for full | |
9 | * details. Many organizations have provided support for Squid's | |
10 | * development; see the SPONSORS file for full details. Squid is | |
11 | * Copyrighted (C) 2001 by the Regents of the University of | |
12 | * California; see the COPYRIGHT file for full details. Squid | |
13 | * incorporates software developed and/or copyrighted by other | |
14 | * sources; see the CREDITS file for full details. | |
6a988308 | 15 | * |
16 | * This program is free software; you can redistribute it and/or modify | |
17 | * it under the terms of the GNU General Public License as published by | |
18 | * the Free Software Foundation; either version 2 of the License, or | |
19 | * (at your option) any later version. | |
26ac0430 | 20 | * |
6a988308 | 21 | * This program is distributed in the hope that it will be useful, |
22 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
23 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
24 | * GNU General Public License for more details. | |
26ac0430 | 25 | * |
6a988308 | 26 | * You should have received a copy of the GNU General Public License |
27 | * along with this program; if not, write to the Free Software | |
28 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. | |
e25c139f | 29 | * |
6a988308 | 30 | */ |
f7f3304a | 31 | #include "squid.h" |
d841c88d AJ |
32 | |
33 | #if USE_SELECT | |
6a988308 | 34 | |
65d448bc | 35 | #include "anyp/PortCfg.h" |
1b76e6c1 | 36 | #include "comm/Connection.h" |
d841c88d | 37 | #include "comm/Loops.h" |
f8171fc7 | 38 | #include "fde.h" |
582c2af2 | 39 | #include "globals.h" |
1b76e6c1 | 40 | #include "ICP.h" |
8822ebee | 41 | #include "mgr/Registration.h" |
45e009e9 | 42 | #include "SquidTime.h" |
e1656dc4 | 43 | #include "StatCounters.h" |
00a7574e | 44 | #include "StatHist.h" |
e1656dc4 | 45 | #include "Store.h" |
1b3db6d9 | 46 | |
582c2af2 FC |
47 | #if HAVE_SYS_STAT_H |
48 | #include <sys/stat.h> | |
49 | #endif | |
21d845b1 FC |
50 | #if HAVE_ERRNO_H |
51 | #include <errno.h> | |
52 | #endif | |
582c2af2 | 53 | |
d841c88d | 54 | static int MAX_POLL_TIME = 1000; /* see also Comm::QuickPollRequired() */ |
6a988308 | 55 | |
588f223c | 56 | #ifndef howmany |
57 | #define howmany(x, y) (((x)+((y)-1))/(y)) | |
58 | #endif | |
59 | #ifndef NBBY | |
60 | #define NBBY 8 | |
61 | #endif | |
e42d5181 | 62 | #define FD_MASK_BYTES sizeof(fd_mask) |
63 | #define FD_MASK_BITS (FD_MASK_BYTES*NBBY) | |
64 | ||
6a988308 | 65 | /* STATIC */ |
60df005c | 66 | static int examine_select(fd_set *, fd_set *); |
65d448bc AJ |
67 | static int fdIsTcpListener(int fd); |
68 | static int fdIsUdpListener(int fd); | |
60df005c | 69 | static int fdIsDns(int fd); |
6a988308 | 70 | static OBJH commIncomingStats; |
60df005c | 71 | static int comm_check_incoming_select_handlers(int nfds, int *fds); |
72 | static void comm_select_dns_incoming(void); | |
1b3db6d9 | 73 | static void commUpdateReadBits(int fd, PF * handler); |
74 | static void commUpdateWriteBits(int fd, PF * handler); | |
75 | ||
6a988308 | 76 | static struct timeval zero_tv; |
d072e3a1 | 77 | static fd_set global_readfds; |
78 | static fd_set global_writefds; | |
79 | static int nreadfds; | |
80 | static int nwritefds; | |
6a988308 | 81 | |
82 | /* | |
83 | * Automatic tuning for incoming requests: | |
84 | * | |
85 | * INCOMING sockets are the ICP and HTTP ports. We need to check these | |
86 | * fairly regularly, but how often? When the load increases, we | |
87 | * want to check the incoming sockets more often. If we have a lot | |
88 | * of incoming ICP, then we need to check these sockets more than | |
89 | * if we just have HTTP. | |
90 | * | |
65d448bc | 91 | * The variables 'incoming_udp_interval' and 'incoming_tcp_interval' |
6a988308 | 92 | * determine how many normal I/O events to process before checking |
93 | * incoming sockets again. Note we store the incoming_interval | |
94 | * multipled by a factor of (2^INCOMING_FACTOR) to have some | |
95 | * pseudo-floating point precision. | |
96 | * | |
65d448bc | 97 | * The variable 'udp_io_events' and 'tcp_io_events' counts how many normal |
6a988308 | 98 | * I/O events have been processed since the last check on the incoming |
99 | * sockets. When io_events > incoming_interval, its time to check incoming | |
100 | * sockets. | |
101 | * | |
102 | * Every time we check incoming sockets, we count how many new messages | |
103 | * or connections were processed. This is used to adjust the | |
104 | * incoming_interval for the next iteration. The new incoming_interval | |
105 | * is calculated as the current incoming_interval plus what we would | |
106 | * like to see as an average number of events minus the number of | |
107 | * events just processed. | |
108 | * | |
e207c652 | 109 | * incoming_interval = incoming_interval + target_average - number_of_events_processed |
6a988308 | 110 | * |
65d448bc | 111 | * There are separate incoming_interval counters for DNS, UDP and TCP events |
26ac0430 | 112 | * |
6a988308 | 113 | * You can see the current values of the incoming_interval's, as well as |
114 | * a histogram of 'incoming_events' by asking the cache manager | |
115 | * for 'comm_incoming', e.g.: | |
116 | * | |
117 | * % ./client mgr:comm_incoming | |
118 | * | |
119 | * Caveats: | |
120 | * | |
121 | * - We have MAX_INCOMING_INTEGER as a magic upper limit on | |
122 | * incoming_interval for both types of sockets. At the | |
123 | * largest value the cache will effectively be idling. | |
124 | * | |
125 | * - The higher the INCOMING_FACTOR, the slower the algorithm will | |
126 | * respond to load spikes/increases/decreases in demand. A value | |
127 | * between 3 and 8 is recommended. | |
128 | */ | |
129 | ||
130 | #define MAX_INCOMING_INTEGER 256 | |
131 | #define INCOMING_FACTOR 5 | |
132 | #define MAX_INCOMING_INTERVAL (MAX_INCOMING_INTEGER << INCOMING_FACTOR) | |
65d448bc | 133 | static int udp_io_events = 0; |
ef523f99 | 134 | static int dns_io_events = 0; |
65d448bc AJ |
135 | static int tcp_io_events = 0; |
136 | static int incoming_udp_interval = 16 << INCOMING_FACTOR; | |
ef523f99 | 137 | static int incoming_dns_interval = 16 << INCOMING_FACTOR; |
65d448bc AJ |
138 | static int incoming_tcp_interval = 16 << INCOMING_FACTOR; |
139 | #define commCheckUdpIncoming (++udp_io_events > (incoming_udp_interval>> INCOMING_FACTOR)) | |
140 | #define commCheckDnsIncoming (++dns_io_events > (incoming_dns_interval>> INCOMING_FACTOR)) | |
141 | #define commCheckTcpIncoming (++tcp_io_events > (incoming_tcp_interval>> INCOMING_FACTOR)) | |
6a988308 | 142 | |
1b3db6d9 | 143 | void |
d841c88d | 144 | Comm::SetSelect(int fd, unsigned int type, PF * handler, void *client_data, time_t timeout) |
6a988308 | 145 | { |
60df005c | 146 | fde *F = &fd_table[fd]; |
1b3db6d9 | 147 | assert(fd >= 0); |
148 | assert(F->flags.open); | |
48e7baac AJ |
149 | debugs(5, 5, HERE << "FD " << fd << ", type=" << type << |
150 | ", handler=" << handler << ", client_data=" << client_data << | |
151 | ", timeout=" << timeout); | |
62e76326 | 152 | |
1b3db6d9 | 153 | if (type & COMM_SELECT_READ) { |
62e76326 | 154 | F->read_handler = handler; |
155 | F->read_data = client_data; | |
156 | commUpdateReadBits(fd, handler); | |
1b3db6d9 | 157 | } |
62e76326 | 158 | |
1b3db6d9 | 159 | if (type & COMM_SELECT_WRITE) { |
62e76326 | 160 | F->write_handler = handler; |
161 | F->write_data = client_data; | |
162 | commUpdateWriteBits(fd, handler); | |
1b3db6d9 | 163 | } |
62e76326 | 164 | |
1b3db6d9 | 165 | if (timeout) |
62e76326 | 166 | F->timeout = squid_curtime + timeout; |
6a988308 | 167 | } |
168 | ||
3a5a4930 | 169 | void |
d841c88d | 170 | Comm::ResetSelect(int fd) |
3a5a4930 | 171 | { |
172 | } | |
1b3db6d9 | 173 | |
6a988308 | 174 | static int |
65d448bc | 175 | fdIsUdpListener(int fd) |
6a988308 | 176 | { |
1b76e6c1 | 177 | if (icpIncomingConn != NULL && fd == icpIncomingConn->fd) |
62e76326 | 178 | return 1; |
179 | ||
1b76e6c1 | 180 | if (icpOutgoingConn != NULL && fd == icpOutgoingConn->fd) |
62e76326 | 181 | return 1; |
182 | ||
60df005c | 183 | return 0; |
6a988308 | 184 | } |
185 | ||
ef523f99 | 186 | static int |
60df005c | 187 | fdIsDns(int fd) |
ef523f99 | 188 | { |
4d6c8504 AJ |
189 | if (fd == DnsSocketA) |
190 | return 1; | |
191 | ||
192 | if (fd == DnsSocketB) | |
62e76326 | 193 | return 1; |
194 | ||
60df005c | 195 | return 0; |
ef523f99 | 196 | } |
197 | ||
6a988308 | 198 | static int |
65d448bc | 199 | fdIsTcpListener(int fd) |
6a988308 | 200 | { |
65d448bc AJ |
201 | for (const AnyP::PortCfg *s = Config.Sockaddr.http; s; s = s->next) { |
202 | if (s->listenConn != NULL && s->listenConn->fd == fd) | |
62e76326 | 203 | return 1; |
6a988308 | 204 | } |
62e76326 | 205 | |
60df005c | 206 | return 0; |
6a988308 | 207 | } |
208 | ||
6a988308 | 209 | static int |
60df005c | 210 | comm_check_incoming_select_handlers(int nfds, int *fds) |
6a988308 | 211 | { |
60df005c | 212 | int i; |
213 | int fd; | |
60df005c | 214 | int maxfd = 0; |
215 | PF *hdl = NULL; | |
216 | fd_set read_mask; | |
217 | fd_set write_mask; | |
218 | FD_ZERO(&read_mask); | |
219 | FD_ZERO(&write_mask); | |
d193a436 | 220 | incoming_sockets_accepted = 0; |
62e76326 | 221 | |
a0808b48 | 222 | for (i = 0; i < nfds; ++i) { |
62e76326 | 223 | fd = fds[i]; |
224 | ||
225 | if (fd_table[fd].read_handler) { | |
226 | FD_SET(fd, &read_mask); | |
227 | ||
228 | if (fd > maxfd) | |
229 | maxfd = fd; | |
230 | } | |
231 | ||
232 | if (fd_table[fd].write_handler) { | |
233 | FD_SET(fd, &write_mask); | |
234 | ||
235 | if (fd > maxfd) | |
236 | maxfd = fd; | |
237 | } | |
6a988308 | 238 | } |
62e76326 | 239 | |
60df005c | 240 | if (maxfd++ == 0) |
62e76326 | 241 | return -1; |
242 | ||
60df005c | 243 | getCurrentTime(); |
62e76326 | 244 | |
a0808b48 | 245 | ++ statCounter.syscalls.selects; |
62e76326 | 246 | |
60df005c | 247 | if (select(maxfd, &read_mask, &write_mask, NULL, &zero_tv) < 1) |
62e76326 | 248 | return incoming_sockets_accepted; |
249 | ||
cbebe602 | 250 | for (i = 0; i < nfds; ++i) { |
62e76326 | 251 | fd = fds[i]; |
252 | ||
253 | if (FD_ISSET(fd, &read_mask)) { | |
254 | if ((hdl = fd_table[fd].read_handler) != NULL) { | |
255 | fd_table[fd].read_handler = NULL; | |
256 | commUpdateReadBits(fd, NULL); | |
257 | hdl(fd, fd_table[fd].read_data); | |
258 | } else { | |
e0236918 | 259 | debugs(5, DBG_IMPORTANT, "comm_select_incoming: FD " << fd << " NULL read handler"); |
62e76326 | 260 | } |
261 | } | |
262 | ||
263 | if (FD_ISSET(fd, &write_mask)) { | |
264 | if ((hdl = fd_table[fd].write_handler) != NULL) { | |
265 | fd_table[fd].write_handler = NULL; | |
266 | commUpdateWriteBits(fd, NULL); | |
267 | hdl(fd, fd_table[fd].write_data); | |
268 | } else { | |
e0236918 | 269 | debugs(5, DBG_IMPORTANT, "comm_select_incoming: FD " << fd << " NULL write handler"); |
62e76326 | 270 | } |
271 | } | |
6a988308 | 272 | } |
62e76326 | 273 | |
d193a436 | 274 | return incoming_sockets_accepted; |
6a988308 | 275 | } |
276 | ||
277 | static void | |
65d448bc | 278 | comm_select_udp_incoming(void) |
6a988308 | 279 | { |
60df005c | 280 | int nfds = 0; |
281 | int fds[2]; | |
282 | int nevents; | |
65d448bc | 283 | udp_io_events = 0; |
62e76326 | 284 | |
a0808b48 FC |
285 | if (Comm::IsConnOpen(icpIncomingConn)) { |
286 | fds[nfds] = icpIncomingConn->fd; | |
287 | ++nfds; | |
288 | } | |
62e76326 | 289 | |
a0808b48 FC |
290 | if (Comm::IsConnOpen(icpOutgoingConn) && icpIncomingConn != icpOutgoingConn) { |
291 | fds[nfds] = icpOutgoingConn->fd; | |
292 | ++nfds; | |
293 | } | |
62e76326 | 294 | |
60df005c | 295 | if (nfds == 0) |
62e76326 | 296 | return; |
297 | ||
60df005c | 298 | nevents = comm_check_incoming_select_handlers(nfds, fds); |
62e76326 | 299 | |
65d448bc | 300 | incoming_udp_interval += Config.comm_incoming.udp.average - nevents; |
62e76326 | 301 | |
65d448bc AJ |
302 | if (incoming_udp_interval < 0) |
303 | incoming_udp_interval = 0; | |
62e76326 | 304 | |
65d448bc AJ |
305 | if (incoming_udp_interval > MAX_INCOMING_INTERVAL) |
306 | incoming_udp_interval = MAX_INCOMING_INTERVAL; | |
62e76326 | 307 | |
65d448bc AJ |
308 | if (nevents > INCOMING_UDP_MAX) |
309 | nevents = INCOMING_UDP_MAX; | |
62e76326 | 310 | |
65d448bc | 311 | statCounter.comm_udp_incoming.count(nevents); |
6a988308 | 312 | } |
313 | ||
ef523f99 | 314 | static void |
65d448bc | 315 | comm_select_tcp_incoming(void) |
ef523f99 | 316 | { |
60df005c | 317 | int nfds = 0; |
65d448bc | 318 | int fds[MAXTCPLISTENPORTS]; |
60df005c | 319 | int nevents; |
65d448bc | 320 | tcp_io_events = 0; |
62e76326 | 321 | |
65d448bc | 322 | // XXX: only poll sockets that won't be deferred. But how do we identify them? |
62e76326 | 323 | |
65d448bc | 324 | for (const AnyP::PortCfg *s = Config.Sockaddr.http; s; s = s->next) { |
a0808b48 FC |
325 | if (Comm::IsConnOpen(s->listenConn)) { |
326 | fds[nfds] = s->listenConn->fd; | |
327 | ++nfds; | |
328 | } | |
6a988308 | 329 | } |
62e76326 | 330 | |
60df005c | 331 | nevents = comm_check_incoming_select_handlers(nfds, fds); |
65d448bc | 332 | incoming_tcp_interval += Config.comm_incoming.tcp.average - nevents; |
62e76326 | 333 | |
65d448bc AJ |
334 | if (incoming_tcp_interval < 0) |
335 | incoming_tcp_interval = 0; | |
62e76326 | 336 | |
65d448bc AJ |
337 | if (incoming_tcp_interval > MAX_INCOMING_INTERVAL) |
338 | incoming_tcp_interval = MAX_INCOMING_INTERVAL; | |
62e76326 | 339 | |
65d448bc AJ |
340 | if (nevents > INCOMING_TCP_MAX) |
341 | nevents = INCOMING_TCP_MAX; | |
62e76326 | 342 | |
65d448bc | 343 | statCounter.comm_tcp_incoming.count(nevents); |
6a988308 | 344 | } |
345 | ||
654570c2 | 346 | #define DEBUG_FDBITS 0 |
6a988308 | 347 | /* Select on all sockets; call handlers for those that are ready. */ |
3d7e9d7c | 348 | comm_err_t |
d841c88d | 349 | Comm::DoSelect(int msec) |
6a988308 | 350 | { |
60df005c | 351 | fd_set readfds; |
79d4ccdf | 352 | fd_set pendingfds; |
60df005c | 353 | fd_set writefds; |
62e76326 | 354 | |
60df005c | 355 | PF *hdl = NULL; |
356 | int fd; | |
357 | int maxfd; | |
358 | int num; | |
79d4ccdf | 359 | int pending; |
65d448bc | 360 | int calldns = 0, calludp = 0, calltcp = 0; |
60df005c | 361 | int maxindex; |
ac458aed | 362 | unsigned int k; |
60df005c | 363 | int j; |
f4e030f1 | 364 | #if DEBUG_FDBITS |
62e76326 | 365 | |
60df005c | 366 | int i; |
f4e030f1 | 367 | #endif |
62e76326 | 368 | |
60df005c | 369 | fd_mask *fdsp; |
79d4ccdf | 370 | fd_mask *pfdsp; |
60df005c | 371 | fd_mask tmask; |
62e76326 | 372 | |
60df005c | 373 | struct timeval poll_time; |
374 | double timeout = current_dtime + (msec / 1000.0); | |
375 | fde *F; | |
62e76326 | 376 | |
60df005c | 377 | do { |
62e76326 | 378 | double start; |
379 | getCurrentTime(); | |
380 | start = current_dtime; | |
62e76326 | 381 | |
65d448bc AJ |
382 | if (commCheckUdpIncoming) |
383 | comm_select_udp_incoming(); | |
62e76326 | 384 | |
65d448bc | 385 | if (commCheckDnsIncoming) |
62e76326 | 386 | comm_select_dns_incoming(); |
387 | ||
65d448bc AJ |
388 | if (commCheckTcpIncoming) |
389 | comm_select_tcp_incoming(); | |
62e76326 | 390 | |
65d448bc | 391 | calldns = calludp = calltcp = 0; |
62e76326 | 392 | |
393 | maxfd = Biggest_FD + 1; | |
394 | ||
41d00cd3 | 395 | memcpy(&readfds, &global_readfds, |
e34763f4 | 396 | howmany(maxfd, FD_MASK_BITS) * FD_MASK_BYTES); |
62e76326 | 397 | |
41d00cd3 | 398 | memcpy(&writefds, &global_writefds, |
e34763f4 | 399 | howmany(maxfd, FD_MASK_BITS) * FD_MASK_BYTES); |
62e76326 | 400 | |
401 | /* remove stalled FDs, and deal with pending descriptors */ | |
402 | pending = 0; | |
403 | ||
404 | FD_ZERO(&pendingfds); | |
405 | ||
406 | maxindex = howmany(maxfd, FD_MASK_BITS); | |
407 | ||
408 | fdsp = (fd_mask *) & readfds; | |
409 | ||
a0808b48 | 410 | for (j = 0; j < maxindex; ++j) { |
62e76326 | 411 | if ((tmask = fdsp[j]) == 0) |
412 | continue; /* no bits here */ | |
413 | ||
a0808b48 | 414 | for (k = 0; k < FD_MASK_BITS; ++k) { |
62e76326 | 415 | if (!EBIT_TEST(tmask, k)) |
416 | continue; | |
417 | ||
418 | /* Found a set bit */ | |
419 | fd = (j * FD_MASK_BITS) + k; | |
420 | ||
62e76326 | 421 | if (FD_ISSET(fd, &readfds) && fd_table[fd].flags.read_pending) { |
422 | FD_SET(fd, &pendingfds); | |
a0808b48 | 423 | ++pending; |
62e76326 | 424 | } |
425 | } | |
426 | } | |
427 | ||
d072e3a1 | 428 | #if DEBUG_FDBITS |
a0808b48 | 429 | for (i = 0; i < maxfd; ++i) { |
62e76326 | 430 | /* Check each open socket for a handler. */ |
62e76326 | 431 | |
a46d2c0e | 432 | if (fd_table[i].read_handler) { |
62e76326 | 433 | assert(FD_ISSET(i, &readfds)); |
434 | } | |
435 | ||
436 | if (fd_table[i].write_handler) { | |
437 | assert(FD_ISSET(i, &writefds)); | |
438 | } | |
439 | } | |
440 | ||
d072e3a1 | 441 | #endif |
62e76326 | 442 | if (nreadfds + nwritefds == 0) { |
443 | assert(shutting_down); | |
444 | return COMM_SHUTDOWN; | |
445 | } | |
446 | ||
447 | if (msec > MAX_POLL_TIME) | |
448 | msec = MAX_POLL_TIME; | |
449 | ||
450 | if (pending) | |
451 | msec = 0; | |
452 | ||
453 | for (;;) { | |
454 | poll_time.tv_sec = msec / 1000; | |
455 | poll_time.tv_usec = (msec % 1000) * 1000; | |
a0808b48 | 456 | ++ statCounter.syscalls.selects; |
62e76326 | 457 | num = select(maxfd, &readfds, &writefds, NULL, &poll_time); |
a0808b48 | 458 | ++ statCounter.select_loops; |
62e76326 | 459 | |
460 | if (num >= 0 || pending > 0) | |
461 | break; | |
462 | ||
463 | if (ignoreErrno(errno)) | |
464 | break; | |
465 | ||
fa84c01d | 466 | debugs(5, DBG_CRITICAL, "comm_select: select failure: " << xstrerror()); |
62e76326 | 467 | |
468 | examine_select(&readfds, &writefds); | |
469 | ||
470 | return COMM_ERROR; | |
471 | ||
472 | /* NOTREACHED */ | |
473 | } | |
474 | ||
475 | if (num < 0 && !pending) | |
476 | continue; | |
477 | ||
40a77eef | 478 | getCurrentTime(); |
479 | ||
bf8fe701 | 480 | debugs(5, num ? 5 : 8, "comm_select: " << num << "+" << pending << " FDs ready"); |
62e76326 | 481 | |
f30f7998 | 482 | statCounter.select_fds_hist.count(num); |
62e76326 | 483 | |
62e76326 | 484 | if (num == 0 && pending == 0) |
485 | continue; | |
486 | ||
487 | /* Scan return fd masks for ready descriptors */ | |
488 | fdsp = (fd_mask *) & readfds; | |
489 | ||
490 | pfdsp = (fd_mask *) & pendingfds; | |
491 | ||
492 | maxindex = howmany(maxfd, FD_MASK_BITS); | |
493 | ||
a0808b48 | 494 | for (j = 0; j < maxindex; ++j) { |
62e76326 | 495 | if ((tmask = (fdsp[j] | pfdsp[j])) == 0) |
496 | continue; /* no bits here */ | |
497 | ||
a0808b48 | 498 | for (k = 0; k < FD_MASK_BITS; ++k) { |
62e76326 | 499 | if (tmask == 0) |
500 | break; /* no more bits left */ | |
501 | ||
502 | if (!EBIT_TEST(tmask, k)) | |
503 | continue; | |
504 | ||
505 | /* Found a set bit */ | |
506 | fd = (j * FD_MASK_BITS) + k; | |
507 | ||
508 | EBIT_CLR(tmask, k); /* this will be done */ | |
509 | ||
d072e3a1 | 510 | #if DEBUG_FDBITS |
62e76326 | 511 | |
bf8fe701 | 512 | debugs(5, 9, "FD " << fd << " bit set for reading"); |
62e76326 | 513 | |
514 | assert(FD_ISSET(fd, &readfds)); | |
515 | ||
d072e3a1 | 516 | #endif |
62e76326 | 517 | |
65d448bc AJ |
518 | if (fdIsUdpListener(fd)) { |
519 | calludp = 1; | |
62e76326 | 520 | continue; |
521 | } | |
522 | ||
523 | if (fdIsDns(fd)) { | |
524 | calldns = 1; | |
525 | continue; | |
526 | } | |
527 | ||
65d448bc AJ |
528 | if (fdIsTcpListener(fd)) { |
529 | calltcp = 1; | |
62e76326 | 530 | continue; |
531 | } | |
532 | ||
533 | F = &fd_table[fd]; | |
bf8fe701 | 534 | debugs(5, 6, "comm_select: FD " << fd << " ready for reading"); |
62e76326 | 535 | |
536 | if (NULL == (hdl = F->read_handler)) | |
537 | (void) 0; | |
62e76326 | 538 | else { |
539 | F->read_handler = NULL; | |
26ac0430 | 540 | F->flags.read_pending = 0; |
62e76326 | 541 | commUpdateReadBits(fd, NULL); |
542 | hdl(fd, F->read_data); | |
a0808b48 | 543 | ++ statCounter.select_fds; |
62e76326 | 544 | |
65d448bc AJ |
545 | if (commCheckUdpIncoming) |
546 | comm_select_udp_incoming(); | |
62e76326 | 547 | |
65d448bc | 548 | if (commCheckDnsIncoming) |
62e76326 | 549 | comm_select_dns_incoming(); |
550 | ||
65d448bc AJ |
551 | if (commCheckTcpIncoming) |
552 | comm_select_tcp_incoming(); | |
62e76326 | 553 | } |
554 | } | |
555 | } | |
556 | ||
557 | fdsp = (fd_mask *) & writefds; | |
558 | ||
a0808b48 | 559 | for (j = 0; j < maxindex; ++j) { |
62e76326 | 560 | if ((tmask = fdsp[j]) == 0) |
561 | continue; /* no bits here */ | |
562 | ||
a0808b48 | 563 | for (k = 0; k < FD_MASK_BITS; ++k) { |
62e76326 | 564 | if (tmask == 0) |
565 | break; /* no more bits left */ | |
566 | ||
567 | if (!EBIT_TEST(tmask, k)) | |
568 | continue; | |
569 | ||
570 | /* Found a set bit */ | |
571 | fd = (j * FD_MASK_BITS) + k; | |
572 | ||
573 | EBIT_CLR(tmask, k); /* this will be done */ | |
574 | ||
d072e3a1 | 575 | #if DEBUG_FDBITS |
62e76326 | 576 | |
bf8fe701 | 577 | debugs(5, 9, "FD " << fd << " bit set for writing"); |
62e76326 | 578 | |
579 | assert(FD_ISSET(fd, &writefds)); | |
580 | ||
d072e3a1 | 581 | #endif |
62e76326 | 582 | |
65d448bc AJ |
583 | if (fdIsUdpListener(fd)) { |
584 | calludp = 1; | |
62e76326 | 585 | continue; |
586 | } | |
587 | ||
588 | if (fdIsDns(fd)) { | |
589 | calldns = 1; | |
590 | continue; | |
591 | } | |
592 | ||
65d448bc AJ |
593 | if (fdIsTcpListener(fd)) { |
594 | calltcp = 1; | |
62e76326 | 595 | continue; |
596 | } | |
597 | ||
598 | F = &fd_table[fd]; | |
48e7baac | 599 | debugs(5, 6, "comm_select: FD " << fd << " ready for writing"); |
62e76326 | 600 | |
601 | if ((hdl = F->write_handler)) { | |
602 | F->write_handler = NULL; | |
603 | commUpdateWriteBits(fd, NULL); | |
604 | hdl(fd, F->write_data); | |
a0808b48 | 605 | ++ statCounter.select_fds; |
62e76326 | 606 | |
65d448bc AJ |
607 | if (commCheckUdpIncoming) |
608 | comm_select_udp_incoming(); | |
62e76326 | 609 | |
65d448bc | 610 | if (commCheckDnsIncoming) |
62e76326 | 611 | comm_select_dns_incoming(); |
612 | ||
65d448bc AJ |
613 | if (commCheckTcpIncoming) |
614 | comm_select_tcp_incoming(); | |
62e76326 | 615 | } |
616 | } | |
617 | } | |
618 | ||
65d448bc AJ |
619 | if (calludp) |
620 | comm_select_udp_incoming(); | |
62e76326 | 621 | |
622 | if (calldns) | |
623 | comm_select_dns_incoming(); | |
624 | ||
65d448bc AJ |
625 | if (calltcp) |
626 | comm_select_tcp_incoming(); | |
62e76326 | 627 | |
62e76326 | 628 | getCurrentTime(); |
629 | ||
630 | statCounter.select_time += (current_dtime - start); | |
631 | ||
632 | return COMM_OK; | |
3d0ac046 | 633 | } while (timeout > current_dtime); |
4a7a3d56 | 634 | debugs(5, 8, "comm_select: time out: " << squid_curtime); |
62e76326 | 635 | |
60df005c | 636 | return COMM_TIMEOUT; |
6a988308 | 637 | } |
6a988308 | 638 | |
28103807 | 639 | static void |
a46d2c0e | 640 | comm_select_dns_incoming(void) |
641 | { | |
60df005c | 642 | int nfds = 0; |
4d6c8504 | 643 | int fds[3]; |
60df005c | 644 | int nevents; |
645 | dns_io_events = 0; | |
62e76326 | 646 | |
055421ee | 647 | if (DnsSocketA < 0 && DnsSocketB < 0) |
62e76326 | 648 | return; |
649 | ||
a0808b48 FC |
650 | if (DnsSocketA >= 0) { |
651 | fds[nfds] = DnsSocketA; | |
652 | ++nfds; | |
653 | } | |
4d6c8504 | 654 | |
a0808b48 FC |
655 | if (DnsSocketB >= 0) { |
656 | fds[nfds] = DnsSocketB; | |
657 | ++nfds; | |
658 | } | |
62e76326 | 659 | |
60df005c | 660 | nevents = comm_check_incoming_select_handlers(nfds, fds); |
62e76326 | 661 | |
60df005c | 662 | if (nevents < 0) |
62e76326 | 663 | return; |
664 | ||
65d448bc | 665 | incoming_dns_interval += Config.comm_incoming.dns.average - nevents; |
62e76326 | 666 | |
65d448bc AJ |
667 | if (incoming_dns_interval < Config.comm_incoming.dns.min_poll) |
668 | incoming_dns_interval = Config.comm_incoming.dns.min_poll; | |
62e76326 | 669 | |
60df005c | 670 | if (incoming_dns_interval > MAX_INCOMING_INTERVAL) |
62e76326 | 671 | incoming_dns_interval = MAX_INCOMING_INTERVAL; |
672 | ||
60df005c | 673 | if (nevents > INCOMING_DNS_MAX) |
62e76326 | 674 | nevents = INCOMING_DNS_MAX; |
675 | ||
f30f7998 | 676 | statCounter.comm_dns_incoming.count(nevents); |
28103807 | 677 | } |
678 | ||
6a988308 | 679 | void |
d841c88d | 680 | Comm::SelectLoopInit(void) |
a46d2c0e | 681 | { |
60df005c | 682 | zero_tv.tv_sec = 0; |
683 | zero_tv.tv_usec = 0; | |
60df005c | 684 | FD_ZERO(&global_readfds); |
685 | FD_ZERO(&global_writefds); | |
686 | nreadfds = nwritefds = 0; | |
da9b2c49 | 687 | |
d841c88d AJ |
688 | Mgr::RegisterAction("comm_select_incoming", |
689 | "comm_incoming() stats", | |
690 | commIncomingStats, 0, 1); | |
6a988308 | 691 | } |
692 | ||
6a988308 | 693 | /* |
694 | * examine_select - debug routine. | |
695 | * | |
696 | * I spend the day chasing this core dump that occurs when both the client | |
697 | * and the server side of a cache fetch simultaneoulsy abort the | |
698 | * connection. While I haven't really studied the code to figure out how | |
699 | * it happens, the snippet below may prevent the cache from exitting: | |
26ac0430 | 700 | * |
6a988308 | 701 | * Call this from where the select loop fails. |
702 | */ | |
703 | static int | |
a46d2c0e | 704 | examine_select(fd_set * readfds, fd_set * writefds) |
705 | { | |
60df005c | 706 | int fd = 0; |
707 | fd_set read_x; | |
708 | fd_set write_x; | |
62e76326 | 709 | |
60df005c | 710 | struct timeval tv; |
6d527e0a | 711 | AsyncCall::Pointer ch = NULL; |
60df005c | 712 | fde *F = NULL; |
62e76326 | 713 | |
60df005c | 714 | struct stat sb; |
fa84c01d | 715 | debugs(5, DBG_CRITICAL, "examine_select: Examining open file descriptors..."); |
62e76326 | 716 | |
a0808b48 | 717 | for (fd = 0; fd < Squid_MaxFD; ++fd) { |
62e76326 | 718 | FD_ZERO(&read_x); |
719 | FD_ZERO(&write_x); | |
720 | tv.tv_sec = tv.tv_usec = 0; | |
721 | ||
722 | if (FD_ISSET(fd, readfds)) | |
723 | FD_SET(fd, &read_x); | |
724 | else if (FD_ISSET(fd, writefds)) | |
725 | FD_SET(fd, &write_x); | |
726 | else | |
727 | continue; | |
728 | ||
a0808b48 | 729 | ++ statCounter.syscalls.selects; |
62e76326 | 730 | errno = 0; |
731 | ||
732 | if (!fstat(fd, &sb)) { | |
bf8fe701 | 733 | debugs(5, 5, "FD " << fd << " is valid."); |
62e76326 | 734 | continue; |
735 | } | |
736 | ||
737 | F = &fd_table[fd]; | |
fa84c01d FC |
738 | debugs(5, DBG_CRITICAL, "FD " << fd << ": " << xstrerror()); |
739 | debugs(5, DBG_CRITICAL, "WARNING: FD " << fd << " has handlers, but it's invalid."); | |
740 | debugs(5, DBG_CRITICAL, "FD " << fd << " is a " << fdTypeStr[F->type] << " called '" << F->desc << "'"); | |
741 | debugs(5, DBG_CRITICAL, "tmout:" << F->timeoutHandler << " read:" << F->read_handler << " write:" << F->write_handler); | |
62e76326 | 742 | |
6d527e0a | 743 | for (ch = F->closeHandler; ch != NULL; ch = ch->Next()) |
fa84c01d | 744 | debugs(5, DBG_CRITICAL, " close handler: " << ch); |
62e76326 | 745 | |
6d527e0a | 746 | if (F->closeHandler != NULL) { |
62e76326 | 747 | commCallCloseHandlers(fd); |
6d527e0a | 748 | } else if (F->timeoutHandler != NULL) { |
fa84c01d | 749 | debugs(5, DBG_CRITICAL, "examine_select: Calling Timeout Handler"); |
26ac0430 | 750 | ScheduleCallHere(F->timeoutHandler); |
62e76326 | 751 | } |
752 | ||
753 | F->closeHandler = NULL; | |
6d527e0a | 754 | F->timeoutHandler = NULL; |
62e76326 | 755 | F->read_handler = NULL; |
756 | F->write_handler = NULL; | |
757 | FD_CLR(fd, readfds); | |
758 | FD_CLR(fd, writefds); | |
6a988308 | 759 | } |
62e76326 | 760 | |
60df005c | 761 | return 0; |
6a988308 | 762 | } |
6a988308 | 763 | |
6a988308 | 764 | static void |
a46d2c0e | 765 | commIncomingStats(StoreEntry * sentry) |
766 | { | |
65d448bc AJ |
767 | storeAppendPrintf(sentry, "Current incoming_udp_interval: %d\n", |
768 | incoming_udp_interval >> INCOMING_FACTOR); | |
60df005c | 769 | storeAppendPrintf(sentry, "Current incoming_dns_interval: %d\n", |
62e76326 | 770 | incoming_dns_interval >> INCOMING_FACTOR); |
65d448bc AJ |
771 | storeAppendPrintf(sentry, "Current incoming_tcp_interval: %d\n", |
772 | incoming_tcp_interval >> INCOMING_FACTOR); | |
60df005c | 773 | storeAppendPrintf(sentry, "\n"); |
774 | storeAppendPrintf(sentry, "Histogram of events per incoming socket type\n"); | |
65d448bc AJ |
775 | storeAppendPrintf(sentry, "ICP Messages handled per comm_select_udp_incoming() call:\n"); |
776 | statCounter.comm_udp_incoming.dump(sentry, statHistIntDumper); | |
60df005c | 777 | storeAppendPrintf(sentry, "DNS Messages handled per comm_select_dns_incoming() call:\n"); |
82146cc8 | 778 | statCounter.comm_dns_incoming.dump(sentry, statHistIntDumper); |
65d448bc AJ |
779 | storeAppendPrintf(sentry, "HTTP Messages handled per comm_select_tcp_incoming() call:\n"); |
780 | statCounter.comm_tcp_incoming.dump(sentry, statHistIntDumper); | |
6a988308 | 781 | } |
d072e3a1 | 782 | |
783 | void | |
a46d2c0e | 784 | commUpdateReadBits(int fd, PF * handler) |
785 | { | |
60df005c | 786 | if (handler && !FD_ISSET(fd, &global_readfds)) { |
62e76326 | 787 | FD_SET(fd, &global_readfds); |
a0808b48 | 788 | ++nreadfds; |
60df005c | 789 | } else if (!handler && FD_ISSET(fd, &global_readfds)) { |
62e76326 | 790 | FD_CLR(fd, &global_readfds); |
a0808b48 | 791 | --nreadfds; |
588f223c | 792 | } |
d072e3a1 | 793 | } |
794 | ||
795 | void | |
a46d2c0e | 796 | commUpdateWriteBits(int fd, PF * handler) |
797 | { | |
60df005c | 798 | if (handler && !FD_ISSET(fd, &global_writefds)) { |
62e76326 | 799 | FD_SET(fd, &global_writefds); |
a0808b48 | 800 | ++nwritefds; |
60df005c | 801 | } else if (!handler && FD_ISSET(fd, &global_writefds)) { |
62e76326 | 802 | FD_CLR(fd, &global_writefds); |
a0808b48 | 803 | --nwritefds; |
588f223c | 804 | } |
d072e3a1 | 805 | } |
cd748f27 | 806 | |
807 | /* Called by async-io or diskd to speed up the polling */ | |
808 | void | |
d841c88d | 809 | Comm::QuickPollRequired(void) |
a46d2c0e | 810 | { |
cd748f27 | 811 | MAX_POLL_TIME = 10; |
812 | } | |
1b3db6d9 | 813 | |
814 | #endif /* USE_SELECT */ |