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