]>
Commit | Line | Data |
---|---|---|
6a988308 | 1 | /* |
bbc27441 | 2 | * Copyright (C) 1996-2014 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 | |
73 | * multipled by a factor of (2^INCOMING_FACTOR) to have some | |
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); |
127 | assert(F->flags.open); | |
48e7baac AJ |
128 | debugs(5, 5, HERE << "FD " << fd << ", type=" << type << |
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 | ||
3a5a4930 | 148 | void |
d841c88d | 149 | Comm::ResetSelect(int fd) |
3a5a4930 | 150 | { |
151 | } | |
1b3db6d9 | 152 | |
6a988308 | 153 | static int |
65d448bc | 154 | fdIsUdpListener(int fd) |
6a988308 | 155 | { |
1b76e6c1 | 156 | if (icpIncomingConn != NULL && fd == icpIncomingConn->fd) |
62e76326 | 157 | return 1; |
158 | ||
1b76e6c1 | 159 | if (icpOutgoingConn != NULL && fd == icpOutgoingConn->fd) |
62e76326 | 160 | return 1; |
161 | ||
60df005c | 162 | return 0; |
6a988308 | 163 | } |
164 | ||
ef523f99 | 165 | static int |
60df005c | 166 | fdIsDns(int fd) |
ef523f99 | 167 | { |
4d6c8504 AJ |
168 | if (fd == DnsSocketA) |
169 | return 1; | |
170 | ||
171 | if (fd == DnsSocketB) | |
62e76326 | 172 | return 1; |
173 | ||
60df005c | 174 | return 0; |
ef523f99 | 175 | } |
176 | ||
6a988308 | 177 | static int |
65d448bc | 178 | fdIsTcpListener(int fd) |
6a988308 | 179 | { |
d00790b2 | 180 | for (AnyP::PortCfgPointer s = HttpPortList; s != NULL; s = s->next) { |
65d448bc | 181 | if (s->listenConn != NULL && s->listenConn->fd == fd) |
62e76326 | 182 | return 1; |
6a988308 | 183 | } |
62e76326 | 184 | |
60df005c | 185 | return 0; |
6a988308 | 186 | } |
187 | ||
6a988308 | 188 | static int |
60df005c | 189 | comm_check_incoming_select_handlers(int nfds, int *fds) |
6a988308 | 190 | { |
60df005c | 191 | int i; |
192 | int fd; | |
60df005c | 193 | int maxfd = 0; |
194 | PF *hdl = NULL; | |
195 | fd_set read_mask; | |
196 | fd_set write_mask; | |
197 | FD_ZERO(&read_mask); | |
198 | FD_ZERO(&write_mask); | |
d193a436 | 199 | incoming_sockets_accepted = 0; |
62e76326 | 200 | |
a0808b48 | 201 | for (i = 0; i < nfds; ++i) { |
62e76326 | 202 | fd = fds[i]; |
203 | ||
204 | if (fd_table[fd].read_handler) { | |
205 | FD_SET(fd, &read_mask); | |
206 | ||
207 | if (fd > maxfd) | |
208 | maxfd = fd; | |
209 | } | |
210 | ||
211 | if (fd_table[fd].write_handler) { | |
212 | FD_SET(fd, &write_mask); | |
213 | ||
214 | if (fd > maxfd) | |
215 | maxfd = fd; | |
216 | } | |
6a988308 | 217 | } |
62e76326 | 218 | |
60df005c | 219 | if (maxfd++ == 0) |
62e76326 | 220 | return -1; |
221 | ||
60df005c | 222 | getCurrentTime(); |
62e76326 | 223 | |
a0808b48 | 224 | ++ statCounter.syscalls.selects; |
62e76326 | 225 | |
60df005c | 226 | if (select(maxfd, &read_mask, &write_mask, NULL, &zero_tv) < 1) |
62e76326 | 227 | return incoming_sockets_accepted; |
228 | ||
cbebe602 | 229 | for (i = 0; i < nfds; ++i) { |
62e76326 | 230 | fd = fds[i]; |
231 | ||
232 | if (FD_ISSET(fd, &read_mask)) { | |
233 | if ((hdl = fd_table[fd].read_handler) != NULL) { | |
234 | fd_table[fd].read_handler = NULL; | |
235 | commUpdateReadBits(fd, NULL); | |
236 | hdl(fd, fd_table[fd].read_data); | |
237 | } else { | |
e0236918 | 238 | debugs(5, DBG_IMPORTANT, "comm_select_incoming: FD " << fd << " NULL read handler"); |
62e76326 | 239 | } |
240 | } | |
241 | ||
242 | if (FD_ISSET(fd, &write_mask)) { | |
243 | if ((hdl = fd_table[fd].write_handler) != NULL) { | |
244 | fd_table[fd].write_handler = NULL; | |
245 | commUpdateWriteBits(fd, NULL); | |
246 | hdl(fd, fd_table[fd].write_data); | |
247 | } else { | |
e0236918 | 248 | debugs(5, DBG_IMPORTANT, "comm_select_incoming: FD " << fd << " NULL write handler"); |
62e76326 | 249 | } |
250 | } | |
6a988308 | 251 | } |
62e76326 | 252 | |
d193a436 | 253 | return incoming_sockets_accepted; |
6a988308 | 254 | } |
255 | ||
256 | static void | |
65d448bc | 257 | comm_select_udp_incoming(void) |
6a988308 | 258 | { |
60df005c | 259 | int nfds = 0; |
260 | int fds[2]; | |
261 | int nevents; | |
65d448bc | 262 | udp_io_events = 0; |
62e76326 | 263 | |
a0808b48 FC |
264 | if (Comm::IsConnOpen(icpIncomingConn)) { |
265 | fds[nfds] = icpIncomingConn->fd; | |
266 | ++nfds; | |
267 | } | |
62e76326 | 268 | |
a0808b48 FC |
269 | if (Comm::IsConnOpen(icpOutgoingConn) && icpIncomingConn != icpOutgoingConn) { |
270 | fds[nfds] = icpOutgoingConn->fd; | |
271 | ++nfds; | |
272 | } | |
62e76326 | 273 | |
60df005c | 274 | if (nfds == 0) |
62e76326 | 275 | return; |
276 | ||
60df005c | 277 | nevents = comm_check_incoming_select_handlers(nfds, fds); |
62e76326 | 278 | |
65d448bc | 279 | incoming_udp_interval += Config.comm_incoming.udp.average - nevents; |
62e76326 | 280 | |
65d448bc AJ |
281 | if (incoming_udp_interval < 0) |
282 | incoming_udp_interval = 0; | |
62e76326 | 283 | |
65d448bc AJ |
284 | if (incoming_udp_interval > MAX_INCOMING_INTERVAL) |
285 | incoming_udp_interval = MAX_INCOMING_INTERVAL; | |
62e76326 | 286 | |
65d448bc AJ |
287 | if (nevents > INCOMING_UDP_MAX) |
288 | nevents = INCOMING_UDP_MAX; | |
62e76326 | 289 | |
65d448bc | 290 | statCounter.comm_udp_incoming.count(nevents); |
6a988308 | 291 | } |
292 | ||
ef523f99 | 293 | static void |
65d448bc | 294 | comm_select_tcp_incoming(void) |
ef523f99 | 295 | { |
60df005c | 296 | int nfds = 0; |
65d448bc | 297 | int fds[MAXTCPLISTENPORTS]; |
60df005c | 298 | int nevents; |
65d448bc | 299 | tcp_io_events = 0; |
62e76326 | 300 | |
65d448bc | 301 | // XXX: only poll sockets that won't be deferred. But how do we identify them? |
62e76326 | 302 | |
d00790b2 | 303 | for (AnyP::PortCfgPointer s = HttpPortList; s != NULL; s = s->next) { |
a0808b48 FC |
304 | if (Comm::IsConnOpen(s->listenConn)) { |
305 | fds[nfds] = s->listenConn->fd; | |
306 | ++nfds; | |
307 | } | |
6a988308 | 308 | } |
62e76326 | 309 | |
60df005c | 310 | nevents = comm_check_incoming_select_handlers(nfds, fds); |
65d448bc | 311 | incoming_tcp_interval += Config.comm_incoming.tcp.average - nevents; |
62e76326 | 312 | |
65d448bc AJ |
313 | if (incoming_tcp_interval < 0) |
314 | incoming_tcp_interval = 0; | |
62e76326 | 315 | |
65d448bc AJ |
316 | if (incoming_tcp_interval > MAX_INCOMING_INTERVAL) |
317 | incoming_tcp_interval = MAX_INCOMING_INTERVAL; | |
62e76326 | 318 | |
65d448bc AJ |
319 | if (nevents > INCOMING_TCP_MAX) |
320 | nevents = INCOMING_TCP_MAX; | |
62e76326 | 321 | |
65d448bc | 322 | statCounter.comm_tcp_incoming.count(nevents); |
6a988308 | 323 | } |
324 | ||
654570c2 | 325 | #define DEBUG_FDBITS 0 |
6a988308 | 326 | /* Select on all sockets; call handlers for those that are ready. */ |
c8407295 | 327 | Comm::Flag |
d841c88d | 328 | Comm::DoSelect(int msec) |
6a988308 | 329 | { |
60df005c | 330 | fd_set readfds; |
79d4ccdf | 331 | fd_set pendingfds; |
60df005c | 332 | fd_set writefds; |
62e76326 | 333 | |
60df005c | 334 | PF *hdl = NULL; |
335 | int fd; | |
336 | int maxfd; | |
337 | int num; | |
79d4ccdf | 338 | int pending; |
65d448bc | 339 | int calldns = 0, calludp = 0, calltcp = 0; |
60df005c | 340 | int maxindex; |
ac458aed | 341 | unsigned int k; |
60df005c | 342 | int j; |
f4e030f1 | 343 | #if DEBUG_FDBITS |
62e76326 | 344 | |
60df005c | 345 | int i; |
f4e030f1 | 346 | #endif |
62e76326 | 347 | |
60df005c | 348 | fd_mask *fdsp; |
79d4ccdf | 349 | fd_mask *pfdsp; |
60df005c | 350 | fd_mask tmask; |
62e76326 | 351 | |
60df005c | 352 | struct timeval poll_time; |
353 | double timeout = current_dtime + (msec / 1000.0); | |
354 | fde *F; | |
62e76326 | 355 | |
60df005c | 356 | do { |
62e76326 | 357 | double start; |
358 | getCurrentTime(); | |
359 | start = current_dtime; | |
62e76326 | 360 | |
65d448bc AJ |
361 | if (commCheckUdpIncoming) |
362 | comm_select_udp_incoming(); | |
62e76326 | 363 | |
65d448bc | 364 | if (commCheckDnsIncoming) |
62e76326 | 365 | comm_select_dns_incoming(); |
366 | ||
65d448bc AJ |
367 | if (commCheckTcpIncoming) |
368 | comm_select_tcp_incoming(); | |
62e76326 | 369 | |
65d448bc | 370 | calldns = calludp = calltcp = 0; |
62e76326 | 371 | |
372 | maxfd = Biggest_FD + 1; | |
373 | ||
41d00cd3 | 374 | memcpy(&readfds, &global_readfds, |
e34763f4 | 375 | howmany(maxfd, FD_MASK_BITS) * FD_MASK_BYTES); |
62e76326 | 376 | |
41d00cd3 | 377 | memcpy(&writefds, &global_writefds, |
e34763f4 | 378 | howmany(maxfd, FD_MASK_BITS) * FD_MASK_BYTES); |
62e76326 | 379 | |
380 | /* remove stalled FDs, and deal with pending descriptors */ | |
381 | pending = 0; | |
382 | ||
383 | FD_ZERO(&pendingfds); | |
384 | ||
385 | maxindex = howmany(maxfd, FD_MASK_BITS); | |
386 | ||
387 | fdsp = (fd_mask *) & readfds; | |
388 | ||
a0808b48 | 389 | for (j = 0; j < maxindex; ++j) { |
62e76326 | 390 | if ((tmask = fdsp[j]) == 0) |
f53969cc | 391 | continue; /* no bits here */ |
62e76326 | 392 | |
a0808b48 | 393 | for (k = 0; k < FD_MASK_BITS; ++k) { |
62e76326 | 394 | if (!EBIT_TEST(tmask, k)) |
395 | continue; | |
396 | ||
397 | /* Found a set bit */ | |
398 | fd = (j * FD_MASK_BITS) + k; | |
399 | ||
62e76326 | 400 | if (FD_ISSET(fd, &readfds) && fd_table[fd].flags.read_pending) { |
401 | FD_SET(fd, &pendingfds); | |
a0808b48 | 402 | ++pending; |
62e76326 | 403 | } |
404 | } | |
405 | } | |
406 | ||
d072e3a1 | 407 | #if DEBUG_FDBITS |
a0808b48 | 408 | for (i = 0; i < maxfd; ++i) { |
62e76326 | 409 | /* Check each open socket for a handler. */ |
62e76326 | 410 | |
a46d2c0e | 411 | if (fd_table[i].read_handler) { |
62e76326 | 412 | assert(FD_ISSET(i, &readfds)); |
413 | } | |
414 | ||
415 | if (fd_table[i].write_handler) { | |
416 | assert(FD_ISSET(i, &writefds)); | |
417 | } | |
418 | } | |
419 | ||
d072e3a1 | 420 | #endif |
62e76326 | 421 | if (nreadfds + nwritefds == 0) { |
422 | assert(shutting_down); | |
23ff0bee | 423 | return Comm::SHUTDOWN; |
62e76326 | 424 | } |
425 | ||
426 | if (msec > MAX_POLL_TIME) | |
427 | msec = MAX_POLL_TIME; | |
428 | ||
429 | if (pending) | |
430 | msec = 0; | |
431 | ||
432 | for (;;) { | |
433 | poll_time.tv_sec = msec / 1000; | |
434 | poll_time.tv_usec = (msec % 1000) * 1000; | |
a0808b48 | 435 | ++ statCounter.syscalls.selects; |
62e76326 | 436 | num = select(maxfd, &readfds, &writefds, NULL, &poll_time); |
a0808b48 | 437 | ++ statCounter.select_loops; |
62e76326 | 438 | |
439 | if (num >= 0 || pending > 0) | |
440 | break; | |
441 | ||
442 | if (ignoreErrno(errno)) | |
443 | break; | |
444 | ||
fa84c01d | 445 | debugs(5, DBG_CRITICAL, "comm_select: select failure: " << xstrerror()); |
62e76326 | 446 | |
447 | examine_select(&readfds, &writefds); | |
448 | ||
4ee57cbe | 449 | return Comm::COMM_ERROR; |
62e76326 | 450 | |
451 | /* NOTREACHED */ | |
452 | } | |
453 | ||
454 | if (num < 0 && !pending) | |
455 | continue; | |
456 | ||
40a77eef | 457 | getCurrentTime(); |
458 | ||
bf8fe701 | 459 | debugs(5, num ? 5 : 8, "comm_select: " << num << "+" << pending << " FDs ready"); |
62e76326 | 460 | |
f30f7998 | 461 | statCounter.select_fds_hist.count(num); |
62e76326 | 462 | |
62e76326 | 463 | if (num == 0 && pending == 0) |
464 | continue; | |
465 | ||
466 | /* Scan return fd masks for ready descriptors */ | |
467 | fdsp = (fd_mask *) & readfds; | |
468 | ||
469 | pfdsp = (fd_mask *) & pendingfds; | |
470 | ||
471 | maxindex = howmany(maxfd, FD_MASK_BITS); | |
472 | ||
a0808b48 | 473 | for (j = 0; j < maxindex; ++j) { |
62e76326 | 474 | if ((tmask = (fdsp[j] | pfdsp[j])) == 0) |
f53969cc | 475 | continue; /* no bits here */ |
62e76326 | 476 | |
a0808b48 | 477 | for (k = 0; k < FD_MASK_BITS; ++k) { |
62e76326 | 478 | if (tmask == 0) |
f53969cc | 479 | break; /* no more bits left */ |
62e76326 | 480 | |
481 | if (!EBIT_TEST(tmask, k)) | |
482 | continue; | |
483 | ||
484 | /* Found a set bit */ | |
485 | fd = (j * FD_MASK_BITS) + k; | |
486 | ||
f53969cc | 487 | EBIT_CLR(tmask, k); /* this will be done */ |
62e76326 | 488 | |
d072e3a1 | 489 | #if DEBUG_FDBITS |
62e76326 | 490 | |
bf8fe701 | 491 | debugs(5, 9, "FD " << fd << " bit set for reading"); |
62e76326 | 492 | |
493 | assert(FD_ISSET(fd, &readfds)); | |
494 | ||
d072e3a1 | 495 | #endif |
62e76326 | 496 | |
65d448bc AJ |
497 | if (fdIsUdpListener(fd)) { |
498 | calludp = 1; | |
62e76326 | 499 | continue; |
500 | } | |
501 | ||
502 | if (fdIsDns(fd)) { | |
503 | calldns = 1; | |
504 | continue; | |
505 | } | |
506 | ||
65d448bc AJ |
507 | if (fdIsTcpListener(fd)) { |
508 | calltcp = 1; | |
62e76326 | 509 | continue; |
510 | } | |
511 | ||
512 | F = &fd_table[fd]; | |
bf8fe701 | 513 | debugs(5, 6, "comm_select: FD " << fd << " ready for reading"); |
62e76326 | 514 | |
515 | if (NULL == (hdl = F->read_handler)) | |
516 | (void) 0; | |
62e76326 | 517 | else { |
518 | F->read_handler = NULL; | |
26ac0430 | 519 | F->flags.read_pending = 0; |
62e76326 | 520 | commUpdateReadBits(fd, NULL); |
521 | hdl(fd, F->read_data); | |
a0808b48 | 522 | ++ statCounter.select_fds; |
62e76326 | 523 | |
65d448bc AJ |
524 | if (commCheckUdpIncoming) |
525 | comm_select_udp_incoming(); | |
62e76326 | 526 | |
65d448bc | 527 | if (commCheckDnsIncoming) |
62e76326 | 528 | comm_select_dns_incoming(); |
529 | ||
65d448bc AJ |
530 | if (commCheckTcpIncoming) |
531 | comm_select_tcp_incoming(); | |
62e76326 | 532 | } |
533 | } | |
534 | } | |
535 | ||
536 | fdsp = (fd_mask *) & writefds; | |
537 | ||
a0808b48 | 538 | for (j = 0; j < maxindex; ++j) { |
62e76326 | 539 | if ((tmask = fdsp[j]) == 0) |
f53969cc | 540 | continue; /* no bits here */ |
62e76326 | 541 | |
a0808b48 | 542 | for (k = 0; k < FD_MASK_BITS; ++k) { |
62e76326 | 543 | if (tmask == 0) |
f53969cc | 544 | break; /* no more bits left */ |
62e76326 | 545 | |
546 | if (!EBIT_TEST(tmask, k)) | |
547 | continue; | |
548 | ||
549 | /* Found a set bit */ | |
550 | fd = (j * FD_MASK_BITS) + k; | |
551 | ||
f53969cc | 552 | EBIT_CLR(tmask, k); /* this will be done */ |
62e76326 | 553 | |
d072e3a1 | 554 | #if DEBUG_FDBITS |
62e76326 | 555 | |
bf8fe701 | 556 | debugs(5, 9, "FD " << fd << " bit set for writing"); |
62e76326 | 557 | |
558 | assert(FD_ISSET(fd, &writefds)); | |
559 | ||
d072e3a1 | 560 | #endif |
62e76326 | 561 | |
65d448bc AJ |
562 | if (fdIsUdpListener(fd)) { |
563 | calludp = 1; | |
62e76326 | 564 | continue; |
565 | } | |
566 | ||
567 | if (fdIsDns(fd)) { | |
568 | calldns = 1; | |
569 | continue; | |
570 | } | |
571 | ||
65d448bc AJ |
572 | if (fdIsTcpListener(fd)) { |
573 | calltcp = 1; | |
62e76326 | 574 | continue; |
575 | } | |
576 | ||
577 | F = &fd_table[fd]; | |
48e7baac | 578 | debugs(5, 6, "comm_select: FD " << fd << " ready for writing"); |
62e76326 | 579 | |
580 | if ((hdl = F->write_handler)) { | |
581 | F->write_handler = NULL; | |
582 | commUpdateWriteBits(fd, NULL); | |
583 | hdl(fd, F->write_data); | |
a0808b48 | 584 | ++ statCounter.select_fds; |
62e76326 | 585 | |
65d448bc AJ |
586 | if (commCheckUdpIncoming) |
587 | comm_select_udp_incoming(); | |
62e76326 | 588 | |
65d448bc | 589 | if (commCheckDnsIncoming) |
62e76326 | 590 | comm_select_dns_incoming(); |
591 | ||
65d448bc AJ |
592 | if (commCheckTcpIncoming) |
593 | comm_select_tcp_incoming(); | |
62e76326 | 594 | } |
595 | } | |
596 | } | |
597 | ||
65d448bc AJ |
598 | if (calludp) |
599 | comm_select_udp_incoming(); | |
62e76326 | 600 | |
601 | if (calldns) | |
602 | comm_select_dns_incoming(); | |
603 | ||
65d448bc AJ |
604 | if (calltcp) |
605 | comm_select_tcp_incoming(); | |
62e76326 | 606 | |
62e76326 | 607 | getCurrentTime(); |
608 | ||
609 | statCounter.select_time += (current_dtime - start); | |
610 | ||
c8407295 | 611 | return Comm::OK; |
3d0ac046 | 612 | } while (timeout > current_dtime); |
4a7a3d56 | 613 | debugs(5, 8, "comm_select: time out: " << squid_curtime); |
62e76326 | 614 | |
c8407295 | 615 | return Comm::TIMEOUT; |
6a988308 | 616 | } |
6a988308 | 617 | |
28103807 | 618 | static void |
a46d2c0e | 619 | comm_select_dns_incoming(void) |
620 | { | |
60df005c | 621 | int nfds = 0; |
4d6c8504 | 622 | int fds[3]; |
60df005c | 623 | int nevents; |
624 | dns_io_events = 0; | |
62e76326 | 625 | |
055421ee | 626 | if (DnsSocketA < 0 && DnsSocketB < 0) |
62e76326 | 627 | return; |
628 | ||
a0808b48 FC |
629 | if (DnsSocketA >= 0) { |
630 | fds[nfds] = DnsSocketA; | |
631 | ++nfds; | |
632 | } | |
4d6c8504 | 633 | |
a0808b48 FC |
634 | if (DnsSocketB >= 0) { |
635 | fds[nfds] = DnsSocketB; | |
636 | ++nfds; | |
637 | } | |
62e76326 | 638 | |
60df005c | 639 | nevents = comm_check_incoming_select_handlers(nfds, fds); |
62e76326 | 640 | |
60df005c | 641 | if (nevents < 0) |
62e76326 | 642 | return; |
643 | ||
65d448bc | 644 | incoming_dns_interval += Config.comm_incoming.dns.average - nevents; |
62e76326 | 645 | |
65d448bc AJ |
646 | if (incoming_dns_interval < Config.comm_incoming.dns.min_poll) |
647 | incoming_dns_interval = Config.comm_incoming.dns.min_poll; | |
62e76326 | 648 | |
60df005c | 649 | if (incoming_dns_interval > MAX_INCOMING_INTERVAL) |
62e76326 | 650 | incoming_dns_interval = MAX_INCOMING_INTERVAL; |
651 | ||
60df005c | 652 | if (nevents > INCOMING_DNS_MAX) |
62e76326 | 653 | nevents = INCOMING_DNS_MAX; |
654 | ||
f30f7998 | 655 | statCounter.comm_dns_incoming.count(nevents); |
28103807 | 656 | } |
657 | ||
6a988308 | 658 | void |
d841c88d | 659 | Comm::SelectLoopInit(void) |
a46d2c0e | 660 | { |
60df005c | 661 | zero_tv.tv_sec = 0; |
662 | zero_tv.tv_usec = 0; | |
60df005c | 663 | FD_ZERO(&global_readfds); |
664 | FD_ZERO(&global_writefds); | |
665 | nreadfds = nwritefds = 0; | |
da9b2c49 | 666 | |
d841c88d AJ |
667 | Mgr::RegisterAction("comm_select_incoming", |
668 | "comm_incoming() stats", | |
669 | commIncomingStats, 0, 1); | |
6a988308 | 670 | } |
671 | ||
6a988308 | 672 | /* |
673 | * examine_select - debug routine. | |
674 | * | |
675 | * I spend the day chasing this core dump that occurs when both the client | |
676 | * and the server side of a cache fetch simultaneoulsy abort the | |
677 | * connection. While I haven't really studied the code to figure out how | |
678 | * it happens, the snippet below may prevent the cache from exitting: | |
26ac0430 | 679 | * |
6a988308 | 680 | * Call this from where the select loop fails. |
681 | */ | |
682 | static int | |
a46d2c0e | 683 | examine_select(fd_set * readfds, fd_set * writefds) |
684 | { | |
60df005c | 685 | int fd = 0; |
686 | fd_set read_x; | |
687 | fd_set write_x; | |
62e76326 | 688 | |
60df005c | 689 | struct timeval tv; |
6d527e0a | 690 | AsyncCall::Pointer ch = NULL; |
60df005c | 691 | fde *F = NULL; |
62e76326 | 692 | |
60df005c | 693 | struct stat sb; |
fa84c01d | 694 | debugs(5, DBG_CRITICAL, "examine_select: Examining open file descriptors..."); |
62e76326 | 695 | |
a0808b48 | 696 | for (fd = 0; fd < Squid_MaxFD; ++fd) { |
62e76326 | 697 | FD_ZERO(&read_x); |
698 | FD_ZERO(&write_x); | |
699 | tv.tv_sec = tv.tv_usec = 0; | |
700 | ||
701 | if (FD_ISSET(fd, readfds)) | |
702 | FD_SET(fd, &read_x); | |
703 | else if (FD_ISSET(fd, writefds)) | |
704 | FD_SET(fd, &write_x); | |
705 | else | |
706 | continue; | |
707 | ||
a0808b48 | 708 | ++ statCounter.syscalls.selects; |
62e76326 | 709 | errno = 0; |
710 | ||
711 | if (!fstat(fd, &sb)) { | |
bf8fe701 | 712 | debugs(5, 5, "FD " << fd << " is valid."); |
62e76326 | 713 | continue; |
714 | } | |
715 | ||
716 | F = &fd_table[fd]; | |
fa84c01d FC |
717 | debugs(5, DBG_CRITICAL, "FD " << fd << ": " << xstrerror()); |
718 | debugs(5, DBG_CRITICAL, "WARNING: FD " << fd << " has handlers, but it's invalid."); | |
719 | debugs(5, DBG_CRITICAL, "FD " << fd << " is a " << fdTypeStr[F->type] << " called '" << F->desc << "'"); | |
720 | debugs(5, DBG_CRITICAL, "tmout:" << F->timeoutHandler << " read:" << F->read_handler << " write:" << F->write_handler); | |
62e76326 | 721 | |
6d527e0a | 722 | for (ch = F->closeHandler; ch != NULL; ch = ch->Next()) |
fa84c01d | 723 | debugs(5, DBG_CRITICAL, " close handler: " << ch); |
62e76326 | 724 | |
6d527e0a | 725 | if (F->closeHandler != NULL) { |
62e76326 | 726 | commCallCloseHandlers(fd); |
6d527e0a | 727 | } else if (F->timeoutHandler != NULL) { |
fa84c01d | 728 | debugs(5, DBG_CRITICAL, "examine_select: Calling Timeout Handler"); |
26ac0430 | 729 | ScheduleCallHere(F->timeoutHandler); |
62e76326 | 730 | } |
731 | ||
732 | F->closeHandler = NULL; | |
6d527e0a | 733 | F->timeoutHandler = NULL; |
62e76326 | 734 | F->read_handler = NULL; |
735 | F->write_handler = NULL; | |
736 | FD_CLR(fd, readfds); | |
737 | FD_CLR(fd, writefds); | |
6a988308 | 738 | } |
62e76326 | 739 | |
60df005c | 740 | return 0; |
6a988308 | 741 | } |
6a988308 | 742 | |
6a988308 | 743 | static void |
a46d2c0e | 744 | commIncomingStats(StoreEntry * sentry) |
745 | { | |
65d448bc AJ |
746 | storeAppendPrintf(sentry, "Current incoming_udp_interval: %d\n", |
747 | incoming_udp_interval >> INCOMING_FACTOR); | |
60df005c | 748 | storeAppendPrintf(sentry, "Current incoming_dns_interval: %d\n", |
62e76326 | 749 | incoming_dns_interval >> INCOMING_FACTOR); |
65d448bc AJ |
750 | storeAppendPrintf(sentry, "Current incoming_tcp_interval: %d\n", |
751 | incoming_tcp_interval >> INCOMING_FACTOR); | |
60df005c | 752 | storeAppendPrintf(sentry, "\n"); |
753 | storeAppendPrintf(sentry, "Histogram of events per incoming socket type\n"); | |
65d448bc AJ |
754 | storeAppendPrintf(sentry, "ICP Messages handled per comm_select_udp_incoming() call:\n"); |
755 | statCounter.comm_udp_incoming.dump(sentry, statHistIntDumper); | |
60df005c | 756 | storeAppendPrintf(sentry, "DNS Messages handled per comm_select_dns_incoming() call:\n"); |
82146cc8 | 757 | statCounter.comm_dns_incoming.dump(sentry, statHistIntDumper); |
65d448bc AJ |
758 | storeAppendPrintf(sentry, "HTTP Messages handled per comm_select_tcp_incoming() call:\n"); |
759 | statCounter.comm_tcp_incoming.dump(sentry, statHistIntDumper); | |
6a988308 | 760 | } |
d072e3a1 | 761 | |
762 | void | |
a46d2c0e | 763 | commUpdateReadBits(int fd, PF * handler) |
764 | { | |
60df005c | 765 | if (handler && !FD_ISSET(fd, &global_readfds)) { |
62e76326 | 766 | FD_SET(fd, &global_readfds); |
a0808b48 | 767 | ++nreadfds; |
60df005c | 768 | } else if (!handler && FD_ISSET(fd, &global_readfds)) { |
62e76326 | 769 | FD_CLR(fd, &global_readfds); |
a0808b48 | 770 | --nreadfds; |
588f223c | 771 | } |
d072e3a1 | 772 | } |
773 | ||
774 | void | |
a46d2c0e | 775 | commUpdateWriteBits(int fd, PF * handler) |
776 | { | |
60df005c | 777 | if (handler && !FD_ISSET(fd, &global_writefds)) { |
62e76326 | 778 | FD_SET(fd, &global_writefds); |
a0808b48 | 779 | ++nwritefds; |
60df005c | 780 | } else if (!handler && FD_ISSET(fd, &global_writefds)) { |
62e76326 | 781 | FD_CLR(fd, &global_writefds); |
a0808b48 | 782 | --nwritefds; |
588f223c | 783 | } |
d072e3a1 | 784 | } |
cd748f27 | 785 | |
786 | /* Called by async-io or diskd to speed up the polling */ | |
787 | void | |
d841c88d | 788 | Comm::QuickPollRequired(void) |
a46d2c0e | 789 | { |
cd748f27 | 790 | MAX_POLL_TIME = 10; |
791 | } | |
1b3db6d9 | 792 | |
793 | #endif /* USE_SELECT */ | |
f53969cc | 794 |