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