3 * $Id: comm_poll.cc,v 1.26 2008/02/12 23:02:13 rousskov Exp $
5 * DEBUG: section 5 Socket Functions
7 * SQUID Web Proxy Cache http://www.squid-cache.org/
8 * ----------------------------------------------------------
10 * Squid is the result of efforts by numerous individuals from
11 * the Internet community; see the CONTRIBUTORS file for full
12 * details. Many organizations have provided support for Squid's
13 * development; see the SPONSORS file for full details. Squid is
14 * Copyrighted (C) 2001 by the Regents of the University of
15 * California; see the COPYRIGHT file for full details. Squid
16 * incorporates software developed and/or copyrighted by other
17 * sources; see the CREDITS file for full details.
19 * This program is free software; you can redistribute it and/or modify
20 * it under the terms of the GNU General Public License as published by
21 * the Free Software Foundation; either version 2 of the License, or
22 * (at your option) any later version.
24 * This program is distributed in the hope that it will be useful,
25 * but WITHOUT ANY WARRANTY; without even the implied warranty of
26 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
27 * GNU General Public License for more details.
29 * You should have received a copy of the GNU General Public License
30 * along with this program; if not, write to the Free Software
31 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
36 #include "comm_poll.h"
37 #include "CacheManager.h"
38 #include "SquidTime.h"
44 static int MAX_POLL_TIME
= 1000; /* see also comm_quick_poll_required() */
47 #define howmany(x, y) (((x)+((y)-1))/(y))
52 #define FD_MASK_BYTES sizeof(fd_mask)
53 #define FD_MASK_BITS (FD_MASK_BYTES*NBBY)
56 static int fdIsHttp(int fd
);
57 static int fdIsIcp(int fd
);
58 static int fdIsDns(int fd
);
59 static OBJH commIncomingStats
;
60 static int comm_check_incoming_poll_handlers(int nfds
, int *fds
);
61 static void comm_poll_dns_incoming(void);
64 * Automatic tuning for incoming requests:
66 * INCOMING sockets are the ICP and HTTP ports. We need to check these
67 * fairly regularly, but how often? When the load increases, we
68 * want to check the incoming sockets more often. If we have a lot
69 * of incoming ICP, then we need to check these sockets more than
70 * if we just have HTTP.
72 * The variables 'incoming_icp_interval' and 'incoming_http_interval'
73 * determine how many normal I/O events to process before checking
74 * incoming sockets again. Note we store the incoming_interval
75 * multipled by a factor of (2^INCOMING_FACTOR) to have some
76 * pseudo-floating point precision.
78 * The variable 'icp_io_events' and 'http_io_events' counts how many normal
79 * I/O events have been processed since the last check on the incoming
80 * sockets. When io_events > incoming_interval, its time to check incoming
83 * Every time we check incoming sockets, we count how many new messages
84 * or connections were processed. This is used to adjust the
85 * incoming_interval for the next iteration. The new incoming_interval
86 * is calculated as the current incoming_interval plus what we would
87 * like to see as an average number of events minus the number of
88 * events just processed.
90 * incoming_interval = incoming_interval + target_average - number_of_events_processed
92 * There are separate incoming_interval counters for both HTTP and ICP events
94 * You can see the current values of the incoming_interval's, as well as
95 * a histogram of 'incoming_events' by asking the cache manager
96 * for 'comm_incoming', e.g.:
98 * % ./client mgr:comm_poll_incoming
102 * - We have MAX_INCOMING_INTEGER as a magic upper limit on
103 * incoming_interval for both types of sockets. At the
104 * largest value the cache will effectively be idling.
106 * - The higher the INCOMING_FACTOR, the slower the algorithm will
107 * respond to load spikes/increases/decreases in demand. A value
108 * between 3 and 8 is recommended.
111 #define MAX_INCOMING_INTEGER 256
112 #define INCOMING_FACTOR 5
113 #define MAX_INCOMING_INTERVAL (MAX_INCOMING_INTEGER << INCOMING_FACTOR)
114 static int icp_io_events
= 0;
115 static int dns_io_events
= 0;
116 static int http_io_events
= 0;
117 static int incoming_icp_interval
= 16 << INCOMING_FACTOR
;
118 static int incoming_dns_interval
= 16 << INCOMING_FACTOR
;
119 static int incoming_http_interval
= 16 << INCOMING_FACTOR
;
120 #define commCheckICPIncoming (++icp_io_events > (incoming_icp_interval>> INCOMING_FACTOR))
121 #define commCheckDNSIncoming (++dns_io_events > (incoming_dns_interval>> INCOMING_FACTOR))
122 #define commCheckHTTPIncoming (++http_io_events > (incoming_http_interval>> INCOMING_FACTOR))
126 commSetSelect(int fd
, unsigned int type
, PF
* handler
, void *client_data
,
129 fde
*F
= &fd_table
[fd
];
131 assert(F
->flags
.open
);
132 debugs(5, 5, "commSetSelect: FD " << fd
<< " type " << type
);
134 if (type
& COMM_SELECT_READ
) {
135 F
->read_handler
= handler
;
136 F
->read_data
= client_data
;
139 if (type
& COMM_SELECT_WRITE
) {
140 F
->write_handler
= handler
;
141 F
->write_data
= client_data
;
145 F
->timeout
= squid_curtime
+ timeout
;
149 commResetSelect(int fd
)
156 if (fd
== theInIcpConnection
)
159 if (fd
== theOutIcpConnection
)
179 for (j
= 0; j
< NHttpSockets
; j
++) {
180 if (fd
== HttpSockets
[j
])
188 comm_check_incoming_poll_handlers(int nfds
, int *fds
)
195 struct pollfd pfds
[3 + MAXHTTPPORTS
];
196 PROF_start(comm_check_incoming
);
197 incoming_sockets_accepted
= 0;
199 for (i
= npfds
= 0; i
< nfds
; i
++) {
204 if (fd_table
[fd
].read_handler
)
205 events
|= POLLRDNORM
;
207 if (fd_table
[fd
].write_handler
)
208 events
|= POLLWRNORM
;
212 pfds
[npfds
].events
= events
;
213 pfds
[npfds
].revents
= 0;
219 PROF_stop(comm_check_incoming
);
224 statCounter
.syscalls
.selects
++;
226 if (poll(pfds
, npfds
, 0) < 1) {
227 PROF_stop(comm_check_incoming
);
228 return incoming_sockets_accepted
;
231 for (i
= 0; i
< npfds
; i
++) {
234 if (((revents
= pfds
[i
].revents
) == 0) || ((fd
= pfds
[i
].fd
) == -1))
237 if (revents
& (POLLRDNORM
| POLLIN
| POLLHUP
| POLLERR
)) {
238 if ((hdl
= fd_table
[fd
].read_handler
)) {
239 fd_table
[fd
].read_handler
= NULL
;
240 hdl(fd
, fd_table
[fd
].read_data
);
241 } else if (pfds
[i
].events
& POLLRDNORM
)
242 debugs(5, 1, "comm_poll_incoming: FD " << fd
<< " NULL read handler");
245 if (revents
& (POLLWRNORM
| POLLOUT
| POLLHUP
| POLLERR
)) {
246 if ((hdl
= fd_table
[fd
].write_handler
)) {
247 fd_table
[fd
].write_handler
= NULL
;
248 hdl(fd
, fd_table
[fd
].write_data
);
249 } else if (pfds
[i
].events
& POLLWRNORM
)
250 debugs(5, 1, "comm_poll_incoming: FD " << fd
<< " NULL write_handler");
254 PROF_stop(comm_check_incoming
);
255 return incoming_sockets_accepted
;
259 comm_poll_icp_incoming(void)
266 if (theInIcpConnection
>= 0)
267 fds
[nfds
++] = theInIcpConnection
;
269 if (theInIcpConnection
!= theOutIcpConnection
)
270 if (theOutIcpConnection
>= 0)
271 fds
[nfds
++] = theOutIcpConnection
;
276 nevents
= comm_check_incoming_poll_handlers(nfds
, fds
);
278 incoming_icp_interval
+= Config
.comm_incoming
.icp_average
- nevents
;
280 if (incoming_icp_interval
< Config
.comm_incoming
.icp_min_poll
)
281 incoming_icp_interval
= Config
.comm_incoming
.icp_min_poll
;
283 if (incoming_icp_interval
> MAX_INCOMING_INTERVAL
)
284 incoming_icp_interval
= MAX_INCOMING_INTERVAL
;
286 if (nevents
> INCOMING_ICP_MAX
)
287 nevents
= INCOMING_ICP_MAX
;
289 statHistCount(&statCounter
.comm_icp_incoming
, nevents
);
293 comm_poll_http_incoming(void)
296 int fds
[MAXHTTPPORTS
];
301 /* only poll sockets that won't be deferred */
303 for (j
= 0; j
< NHttpSockets
; j
++) {
304 if (HttpSockets
[j
] < 0)
307 fds
[nfds
++] = HttpSockets
[j
];
310 nevents
= comm_check_incoming_poll_handlers(nfds
, fds
);
311 incoming_http_interval
= incoming_http_interval
312 + Config
.comm_incoming
.http_average
- nevents
;
314 if (incoming_http_interval
< Config
.comm_incoming
.http_min_poll
)
315 incoming_http_interval
= Config
.comm_incoming
.http_min_poll
;
317 if (incoming_http_interval
> MAX_INCOMING_INTERVAL
)
318 incoming_http_interval
= MAX_INCOMING_INTERVAL
;
320 if (nevents
> INCOMING_HTTP_MAX
)
321 nevents
= INCOMING_HTTP_MAX
;
323 statHistCount(&statCounter
.comm_http_incoming
, nevents
);
326 /* poll all sockets; call handlers for those that are ready. */
328 comm_select(int msec
)
331 struct pollfd pfds
[SQUID_MAXFD
];
337 unsigned long npending
;
339 int callicp
= 0, callhttp
= 0;
341 double timeout
= current_dtime
+ (msec
/ 1000.0);
346 start
= current_dtime
;
348 if (commCheckICPIncoming
)
349 comm_poll_icp_incoming();
351 if (commCheckDNSIncoming
)
352 comm_poll_dns_incoming();
354 if (commCheckHTTPIncoming
)
355 comm_poll_http_incoming();
357 PROF_start(comm_poll_prep_pfds
);
359 callicp
= calldns
= callhttp
= 0;
365 maxfd
= Biggest_FD
+ 1;
367 for (int i
= 0; i
< maxfd
; i
++) {
370 /* Check each open socket for a handler. */
372 if (fd_table
[i
].read_handler
)
373 events
|= POLLRDNORM
;
375 if (fd_table
[i
].write_handler
)
376 events
|= POLLWRNORM
;
380 pfds
[nfds
].events
= events
;
381 pfds
[nfds
].revents
= 0;
384 if ((events
& POLLRDNORM
) && fd_table
[i
].flags
.read_pending
)
389 PROF_stop(comm_poll_prep_pfds
);
394 if (msec
> MAX_POLL_TIME
)
395 msec
= MAX_POLL_TIME
;
399 * Note that this will only ever trigger when there are no log files
400 * and stdout/err/in are all closed too.
402 if (nfds
== 0 && !npending
) {
404 return COMM_SHUTDOWN
;
410 PROF_start(comm_poll_normal
);
411 statCounter
.syscalls
.selects
++;
412 num
= poll(pfds
, nfds
, msec
);
413 statCounter
.select_loops
++;
414 PROF_stop(comm_poll_normal
);
416 if (num
>= 0 || npending
>= 0)
419 if (ignoreErrno(errno
))
422 debugs(5, 0, "comm_poll: poll failure: " << xstrerror());
424 assert(errno
!= EINVAL
);
433 debugs(5, num
? 5 : 8, "comm_poll: " << num
<< "+" << npending
<< " FDs ready");
434 statHistCount(&statCounter
.select_fds_hist
, num
);
436 if (num
== 0 && npending
== 0)
439 /* scan each socket but the accept socket. Poll this
440 * more frequently to minimize losses due to the 5 connect
442 PROF_start(comm_handle_ready_fd
);
444 for (size_t loopIndex
= 0; loopIndex
< nfds
; loopIndex
++) {
446 int revents
= pfds
[loopIndex
].revents
;
447 fd
= pfds
[loopIndex
].fd
;
452 if (fd_table
[fd
].flags
.read_pending
)
475 if (revents
& (POLLRDNORM
| POLLIN
| POLLHUP
| POLLERR
)) {
476 debugs(5, 6, "comm_poll: FD " << fd
<< " ready for reading");
478 if (NULL
== (hdl
= F
->read_handler
))
481 PROF_start(comm_read_handler
);
482 F
->read_handler
= NULL
;
483 F
->flags
.read_pending
= 0;
484 hdl(fd
, F
->read_data
);
485 PROF_stop(comm_read_handler
);
486 statCounter
.select_fds
++;
488 if (commCheckICPIncoming
)
489 comm_poll_icp_incoming();
491 if (commCheckDNSIncoming
)
492 comm_poll_dns_incoming();
494 if (commCheckHTTPIncoming
)
495 comm_poll_http_incoming();
499 if (revents
& (POLLWRNORM
| POLLOUT
| POLLHUP
| POLLERR
)) {
500 debugs(5, 5, "comm_poll: FD " << fd
<< " ready for writing");
502 if ((hdl
= F
->write_handler
)) {
503 PROF_start(comm_write_handler
);
504 F
->write_handler
= NULL
;
505 hdl(fd
, F
->write_data
);
506 PROF_stop(comm_write_handler
);
507 statCounter
.select_fds
++;
509 if (commCheckICPIncoming
)
510 comm_poll_icp_incoming();
512 if (commCheckDNSIncoming
)
513 comm_poll_dns_incoming();
515 if (commCheckHTTPIncoming
)
516 comm_poll_http_incoming();
520 if (revents
& POLLNVAL
) {
521 AsyncCall::Pointer ch
;
522 debugs(5, 0, "WARNING: FD " << fd
<< " has handlers, but it's invalid.");
523 debugs(5, 0, "FD " << fd
<< " is a " << fdTypeStr
[F
->type
]);
524 debugs(5, 0, "--> " << F
->desc
);
525 debugs(5, 0, "tmout:" << F
->timeoutHandler
<< "read:" <<
526 F
->read_handler
<< " write:" << F
->write_handler
);
528 for (ch
= F
->closeHandler
; ch
!= NULL
; ch
= ch
->Next())
529 debugs(5, 0, " close handler: " << ch
);
531 if (F
->closeHandler
!= NULL
) {
532 commCallCloseHandlers(fd
);
533 } else if (F
->timeoutHandler
!= NULL
) {
534 debugs(5, 0, "comm_poll: Calling Timeout Handler");
535 ScheduleCallHere(F
->timeoutHandler
);
538 F
->closeHandler
= NULL
;
539 F
->timeoutHandler
= NULL
;
540 F
->read_handler
= NULL
;
541 F
->write_handler
= NULL
;
548 PROF_stop(comm_handle_ready_fd
);
551 comm_poll_icp_incoming();
554 comm_poll_dns_incoming();
557 comm_poll_http_incoming();
561 statCounter
.select_time
+= (current_dtime
- start
);
564 } while (timeout
> current_dtime
);
566 debugs(5, 8, "comm_poll: time out: " << squid_curtime
<< ".");
573 comm_poll_dns_incoming(void)
583 fds
[nfds
++] = DnsSocket
;
585 nevents
= comm_check_incoming_poll_handlers(nfds
, fds
);
590 incoming_dns_interval
+= Config
.comm_incoming
.dns_average
- nevents
;
592 if (incoming_dns_interval
< Config
.comm_incoming
.dns_min_poll
)
593 incoming_dns_interval
= Config
.comm_incoming
.dns_min_poll
;
595 if (incoming_dns_interval
> MAX_INCOMING_INTERVAL
)
596 incoming_dns_interval
= MAX_INCOMING_INTERVAL
;
598 if (nevents
> INCOMING_DNS_MAX
)
599 nevents
= INCOMING_DNS_MAX
;
601 statHistCount(&statCounter
.comm_dns_incoming
, nevents
);
605 comm_select_init(void)
609 commPollRegisterWithCacheManager(CacheManager
& manager
)
611 manager
.registerAction("comm_poll_incoming",
612 "comm_incoming() stats",
613 commIncomingStats
, 0, 1);
618 commIncomingStats(StoreEntry
* sentry
)
620 StatCounters
*f
= &statCounter
;
621 storeAppendPrintf(sentry
, "Current incoming_icp_interval: %d\n",
622 incoming_icp_interval
>> INCOMING_FACTOR
);
623 storeAppendPrintf(sentry
, "Current incoming_dns_interval: %d\n",
624 incoming_dns_interval
>> INCOMING_FACTOR
);
625 storeAppendPrintf(sentry
, "Current incoming_http_interval: %d\n",
626 incoming_http_interval
>> INCOMING_FACTOR
);
627 storeAppendPrintf(sentry
, "\n");
628 storeAppendPrintf(sentry
, "Histogram of events per incoming socket type\n");
629 storeAppendPrintf(sentry
, "ICP Messages handled per comm_poll_icp_incoming() call:\n");
630 statHistDump(&f
->comm_icp_incoming
, sentry
, statHistIntDumper
);
631 storeAppendPrintf(sentry
, "DNS Messages handled per comm_poll_dns_incoming() call:\n");
632 statHistDump(&f
->comm_dns_incoming
, sentry
, statHistIntDumper
);
633 storeAppendPrintf(sentry
, "HTTP Messages handled per comm_poll_http_incoming() call:\n");
634 statHistDump(&f
->comm_http_incoming
, sentry
, statHistIntDumper
);
637 /* Called by async-io or diskd to speed up the polling */
639 comm_quick_poll_required(void)
644 #endif /* USE_POLL */