ModPoll.cc
Go to the documentation of this file.
1 /*
2  * Copyright (C) 1996-2022 The Squid Software Foundation and contributors
3  *
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.
7  */
8 
9 /* DEBUG: section 05 Socket Functions */
10 
11 #include "squid.h"
12 
13 #if USE_POLL
14 #include "anyp/PortCfg.h"
15 #include "comm/Connection.h"
16 #include "comm/Loops.h"
17 #include "fd.h"
18 #include "fde.h"
19 #include "globals.h"
20 #include "ICP.h"
21 #include "mgr/Registration.h"
22 #include "SquidConfig.h"
23 #include "StatCounters.h"
24 #include "Store.h"
25 
26 #include <cerrno>
27 #if HAVE_POLL_H
28 #include <poll.h>
29 #endif
30 
31 /* Needed for poll() on Linux at least */
32 #if USE_POLL
33 #ifndef POLLRDNORM
34 #define POLLRDNORM POLLIN
35 #endif
36 #ifndef POLLWRNORM
37 #define POLLWRNORM POLLOUT
38 #endif
39 #endif
40 
41 static int MAX_POLL_TIME = 1000; /* see also Comm::QuickPollRequired() */
42 
43 #ifndef howmany
44 #define howmany(x, y) (((x)+((y)-1))/(y))
45 #endif
46 #ifndef NBBY
47 #define NBBY 8
48 #endif
49 #define FD_MASK_BYTES sizeof(fd_mask)
50 #define FD_MASK_BITS (FD_MASK_BYTES*NBBY)
51 
52 /* STATIC */
53 static int fdIsTcpListen(int fd);
54 static int fdIsUdpListen(int fd);
55 static int fdIsDns(int fd);
57 static int comm_check_incoming_poll_handlers(int nfds, int *fds);
58 static void comm_poll_dns_incoming(void);
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  *
69  * The variables 'incoming_icp_interval' and 'incoming_http_interval'
70  * determine how many normal I/O events to process before checking
71  * incoming sockets again. Note we store the incoming_interval
72  * multiplied by a factor of (2^INCOMING_FACTOR) to have some
73  * pseudo-floating point precision.
74  *
75  * The variable 'udp_io_events' and 'tcp_io_events' counts how many normal
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  *
87  * incoming_interval = incoming_interval + target_average - number_of_events_processed
88  *
89  * There are separate incoming_interval counters for TCP-based, UDP-based, and DNS events
90  *
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_poll_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)
111 static int udp_io_events = 0;
112 static int dns_io_events = 0;
113 static int tcp_io_events = 0;
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))
120 
121 void
122 Comm::SetSelect(int fd, unsigned int type, PF * handler, void *client_data, time_t timeout)
123 {
124  fde *F = &fd_table[fd];
125  assert(fd >= 0);
126  assert(F->flags.open || (!handler && !client_data && !timeout));
127  debugs(5, 5, "FD " << fd << ", type=" << type <<
128  ", handler=" << handler << ", client_data=" << client_data <<
129  ", timeout=" << timeout);
130 
131  if (type & COMM_SELECT_READ) {
132  F->read_handler = handler;
133  F->read_data = client_data;
134  }
135 
136  if (type & COMM_SELECT_WRITE) {
137  F->write_handler = handler;
138  F->write_data = client_data;
139  }
140 
141  if (timeout)
142  F->timeout = squid_curtime + timeout;
143 }
144 
145 static int
147 {
148  if (icpIncomingConn != nullptr && icpIncomingConn->fd == fd)
149  return 1;
150 
151  if (icpOutgoingConn != nullptr && icpOutgoingConn->fd == fd)
152  return 1;
153 
154  return 0;
155 }
156 
157 static int
158 fdIsDns(int fd)
159 {
160  if (fd == DnsSocketA)
161  return 1;
162 
163  if (fd == DnsSocketB)
164  return 1;
165 
166  return 0;
167 }
168 
169 static int
171 {
172  for (AnyP::PortCfgPointer s = HttpPortList; s != nullptr; s = s->next) {
173  if (s->listenConn != nullptr && s->listenConn->fd == fd)
174  return 1;
175  }
176 
177  return 0;
178 }
179 
180 static int
182 {
183  int i;
184  int fd;
185  PF *hdl = nullptr;
186  int npfds;
187 
188  struct pollfd pfds[3 + MAXTCPLISTENPORTS];
190 
191  for (i = npfds = 0; i < nfds; ++i) {
192  int events;
193  fd = fds[i];
194  events = 0;
195 
196  if (fd_table[fd].read_handler)
197  events |= POLLRDNORM;
198 
199  if (fd_table[fd].write_handler)
200  events |= POLLWRNORM;
201 
202  if (events) {
203  pfds[npfds].fd = fd;
204  pfds[npfds].events = events;
205  pfds[npfds].revents = 0;
206  ++npfds;
207  }
208  }
209 
210  if (!nfds)
211  return -1;
212 
213  getCurrentTime();
215 
216  if (poll(pfds, npfds, 0) < 1)
218 
219  for (i = 0; i < npfds; ++i) {
220  int revents;
221 
222  if (((revents = pfds[i].revents) == 0) || ((fd = pfds[i].fd) == -1))
223  continue;
224 
225  if (revents & (POLLRDNORM | POLLIN | POLLHUP | POLLERR)) {
226  if ((hdl = fd_table[fd].read_handler)) {
227  fd_table[fd].read_handler = nullptr;
228  hdl(fd, fd_table[fd].read_data);
229  } else if (pfds[i].events & POLLRDNORM)
230  debugs(5, DBG_IMPORTANT, "comm_poll_incoming: FD " << fd << " NULL read handler");
231  }
232 
233  if (revents & (POLLWRNORM | POLLOUT | POLLHUP | POLLERR)) {
234  if ((hdl = fd_table[fd].write_handler)) {
235  fd_table[fd].write_handler = nullptr;
236  hdl(fd, fd_table[fd].write_data);
237  } else if (pfds[i].events & POLLWRNORM)
238  debugs(5, DBG_IMPORTANT, "comm_poll_incoming: FD " << fd << " NULL write_handler");
239  }
240  }
241 
243 }
244 
245 static void
247 {
248  int nfds = 0;
249  int fds[2];
250  int nevents;
251  udp_io_events = 0;
252 
254  fds[nfds] = icpIncomingConn->fd;
255  ++nfds;
256  }
257 
259  fds[nfds] = icpOutgoingConn->fd;
260  ++nfds;
261  }
262 
263  if (nfds == 0)
264  return;
265 
266  nevents = comm_check_incoming_poll_handlers(nfds, fds);
267 
268  incoming_udp_interval += Config.comm_incoming.udp.average - nevents;
269 
272 
275 
276  if (nevents > INCOMING_UDP_MAX)
277  nevents = INCOMING_UDP_MAX;
278 
280 }
281 
282 static void
284 {
285  int nfds = 0;
286  int fds[MAXTCPLISTENPORTS];
287  int j;
288  int nevents;
289  tcp_io_events = 0;
290 
291  // XXX: only poll sockets that won't be deferred. But how do we identify them?
292 
293  for (j = 0; j < NHttpSockets; ++j) {
294  if (HttpSockets[j] < 0)
295  continue;
296 
297  fds[nfds] = HttpSockets[j];
298  ++nfds;
299  }
300 
301  nevents = comm_check_incoming_poll_handlers(nfds, fds);
303  + Config.comm_incoming.tcp.average - nevents;
304 
307 
310 
311  if (nevents > INCOMING_TCP_MAX)
312  nevents = INCOMING_TCP_MAX;
313 
315 }
316 
317 /* poll all sockets; call handlers for those that are ready. */
319 Comm::DoSelect(int msec)
320 {
321  struct pollfd pfds[SQUID_MAXFD];
322 
323  PF *hdl = nullptr;
324  int fd;
325  int maxfd;
326  unsigned long nfds;
327  unsigned long npending;
328  int num;
329  int calldns = 0, calludp = 0, calltcp = 0;
330  double timeout = current_dtime + (msec / 1000.0);
331 
332  do {
333  double start;
334  getCurrentTime();
335  start = current_dtime;
336 
339 
342 
345 
346  calldns = calludp = calltcp = 0;
347 
348  nfds = 0;
349 
350  npending = 0;
351 
352  maxfd = Biggest_FD + 1;
353 
354  for (int i = 0; i < maxfd; ++i) {
355  int events;
356  events = 0;
357  /* Check each open socket for a handler. */
358 
359  if (fd_table[i].read_handler)
360  events |= POLLRDNORM;
361 
362  if (fd_table[i].write_handler)
363  events |= POLLWRNORM;
364 
365  if (events) {
366  pfds[nfds].fd = i;
367  pfds[nfds].events = events;
368  pfds[nfds].revents = 0;
369  ++nfds;
370 
371  if ((events & POLLRDNORM) && fd_table[i].flags.read_pending)
372  ++npending;
373  }
374  }
375 
376  if (npending)
377  msec = 0;
378 
379  if (msec > MAX_POLL_TIME)
380  msec = MAX_POLL_TIME;
381 
382  /* nothing to do
383  *
384  * Note that this will only ever trigger when there are no log files
385  * and stdout/err/in are all closed too.
386  */
387  if (nfds == 0 && npending == 0) {
388  if (shutting_down)
389  return Comm::SHUTDOWN;
390  else
391  return Comm::IDLE;
392  }
393 
394  for (;;) {
396  num = poll(pfds, nfds, msec);
397  int xerrno = errno;
399 
400  if (num >= 0 || npending > 0)
401  break;
402 
403  if (ignoreErrno(xerrno))
404  continue;
405 
406  debugs(5, DBG_CRITICAL, MYNAME << "poll failure: " << xstrerr(xerrno));
407 
408  assert(xerrno != EINVAL);
409 
410  return Comm::COMM_ERROR;
411 
412  /* NOTREACHED */
413  }
414 
415  getCurrentTime();
416 
417  debugs(5, num ? 5 : 8, "comm_poll: " << num << "+" << npending << " FDs ready");
419 
420  if (num == 0 && npending == 0)
421  continue;
422 
423  /* scan each socket but the accept socket. Poll this
424  * more frequently to minimize losses due to the 5 connect
425  * limit in SunOS */
426 
427  for (size_t loopIndex = 0; loopIndex < nfds; ++loopIndex) {
428  fde *F;
429  int revents = pfds[loopIndex].revents;
430  fd = pfds[loopIndex].fd;
431 
432  if (fd == -1)
433  continue;
434 
435  if (fd_table[fd].flags.read_pending)
436  revents |= POLLIN;
437 
438  if (revents == 0)
439  continue;
440 
441  if (fdIsUdpListen(fd)) {
442  calludp = 1;
443  continue;
444  }
445 
446  if (fdIsDns(fd)) {
447  calldns = 1;
448  continue;
449  }
450 
451  if (fdIsTcpListen(fd)) {
452  calltcp = 1;
453  continue;
454  }
455 
456  F = &fd_table[fd];
457 
458  if (revents & (POLLRDNORM | POLLIN | POLLHUP | POLLERR)) {
459  debugs(5, 6, "comm_poll: FD " << fd << " ready for reading");
460 
461  if ((hdl = F->read_handler)) {
462  F->read_handler = nullptr;
463  hdl(fd, F->read_data);
465 
468 
471 
474  }
475  }
476 
477  if (revents & (POLLWRNORM | POLLOUT | POLLHUP | POLLERR)) {
478  debugs(5, 6, "comm_poll: FD " << fd << " ready for writing");
479 
480  if ((hdl = F->write_handler)) {
481  F->write_handler = nullptr;
482  hdl(fd, F->write_data);
484 
487 
490 
493  }
494  }
495 
496  if (revents & POLLNVAL) {
498  debugs(5, DBG_CRITICAL, "WARNING: FD " << fd << " has handlers, but it's invalid.");
499  debugs(5, DBG_CRITICAL, "FD " << fd << " is a " << fdTypeStr[F->type]);
500  debugs(5, DBG_CRITICAL, "--> " << F->desc);
501  debugs(5, DBG_CRITICAL, "tmout:" << F->timeoutHandler << "read:" <<
502  F->read_handler << " write:" << F->write_handler);
503 
504  for (ch = F->closeHandler; ch != nullptr; ch = ch->Next())
505  debugs(5, DBG_CRITICAL, " close handler: " << ch);
506 
507  if (F->closeHandler != nullptr) {
509  } else if (F->timeoutHandler != nullptr) {
510  debugs(5, DBG_CRITICAL, "comm_poll: Calling Timeout Handler");
511  ScheduleCallHere(F->timeoutHandler);
512  }
513 
514  F->closeHandler = nullptr;
515  F->timeoutHandler = nullptr;
516  F->read_handler = nullptr;
517  F->write_handler = nullptr;
518 
519  if (F->flags.open)
520  fd_close(fd);
521  }
522  }
523 
524  if (calludp)
526 
527  if (calldns)
529 
530  if (calltcp)
532 
533  getCurrentTime();
534 
536 
537  return Comm::OK;
538  } while (timeout > current_dtime);
539 
540  debugs(5, 8, "comm_poll: time out: " << squid_curtime << ".");
541 
542  return Comm::TIMEOUT;
543 }
544 
545 static void
547 {
548  int nfds = 0;
549  int fds[2];
550  int nevents;
551  dns_io_events = 0;
552 
553  if (DnsSocketA < 0 && DnsSocketB < 0)
554  return;
555 
556  if (DnsSocketA >= 0) {
557  fds[nfds] = DnsSocketA;
558  ++nfds;
559  }
560 
561  if (DnsSocketB >= 0) {
562  fds[nfds] = DnsSocketB;
563  ++nfds;
564  }
565 
566  nevents = comm_check_incoming_poll_handlers(nfds, fds);
567 
568  if (nevents < 0)
569  return;
570 
571  incoming_dns_interval += Config.comm_incoming.dns.average - nevents;
572 
575 
578 
579  if (nevents > INCOMING_DNS_MAX)
580  nevents = INCOMING_DNS_MAX;
581 
583 }
584 
585 static void
587 {
588  Mgr::RegisterAction("comm_poll_incoming",
589  "comm_incoming() stats",
590  commIncomingStats, 0, 1);
591 }
592 
593 void
595 {
597 }
598 
599 static void
601 {
602  storeAppendPrintf(sentry, "Current incoming_udp_interval: %d\n",
604  storeAppendPrintf(sentry, "Current incoming_dns_interval: %d\n",
606  storeAppendPrintf(sentry, "Current incoming_tcp_interval: %d\n",
608  storeAppendPrintf(sentry, "\n");
609  storeAppendPrintf(sentry, "Histogram of events per incoming socket type\n");
610  storeAppendPrintf(sentry, "ICP Messages handled per comm_poll_udp_incoming() call:\n");
612  storeAppendPrintf(sentry, "DNS Messages handled per comm_poll_dns_incoming() call:\n");
614  storeAppendPrintf(sentry, "HTTP Messages handled per comm_poll_tcp_incoming() call:\n");
616 }
617 
618 /* Called by async-io or diskd to speed up the polling */
619 void
621 {
622  MAX_POLL_TIME = 10;
623 }
624 
625 #endif /* USE_POLL */
626 
#define ScheduleCallHere(call)
Definition: AsyncCall.h:164
#define INCOMING_TCP_MAX
Definition: Loops.h:69
#define INCOMING_DNS_MAX
Definition: Loops.h:59
#define INCOMING_UDP_MAX
Definition: Loops.h:50
struct pollfd * pfds
Definition: ModDevPoll.cc:73
static int incoming_udp_interval
Definition: ModPoll.cc:114
#define commCheckTcpIncoming
Definition: ModPoll.cc:119
static int incoming_tcp_interval
Definition: ModPoll.cc:116
static int fdIsDns(int fd)
Definition: ModPoll.cc:158
static void commPollRegisterWithCacheManager(void)
Definition: ModPoll.cc:586
static int incoming_dns_interval
Definition: ModPoll.cc:115
static int dns_io_events
I/O events passed since last DNS socket poll.
Definition: ModPoll.cc:112
static int tcp_io_events
I/O events passed since last TCP listening socket poll.
Definition: ModPoll.cc:113
static int comm_check_incoming_poll_handlers(int nfds, int *fds)
Definition: ModPoll.cc:181
static int MAX_POLL_TIME
Definition: ModPoll.cc:41
#define commCheckDnsIncoming
Definition: ModPoll.cc:118
#define commCheckUdpIncoming
Definition: ModPoll.cc:117
static int fdIsTcpListen(int fd)
Definition: ModPoll.cc:170
#define POLLRDNORM
Definition: ModPoll.cc:34
#define POLLWRNORM
Definition: ModPoll.cc:37
static void comm_poll_dns_incoming(void)
Definition: ModPoll.cc:546
#define INCOMING_FACTOR
Definition: ModPoll.cc:109
static void comm_poll_tcp_incoming(void)
Definition: ModPoll.cc:283
static OBJH commIncomingStats
Definition: ModPoll.cc:56
static int udp_io_events
I/O events passed since last UDP receiver socket poll.
Definition: ModPoll.cc:111
#define MAX_INCOMING_INTERVAL
Definition: ModPoll.cc:110
static int fdIsUdpListen(int fd)
Definition: ModPoll.cc:146
static void comm_poll_udp_incoming(void)
Definition: ModPoll.cc:246
time_t squid_curtime
Definition: stub_libtime.cc:20
int NHttpSockets
Definition: PortCfg.cc:25
int HttpSockets[MAXTCPLISTENPORTS]
Definition: PortCfg.cc:26
AnyP::PortCfgPointer HttpPortList
list of Squid http(s)_port configured
Definition: PortCfg.cc:22
#define MAXTCPLISTENPORTS
Definition: PortCfg.h:87
class SquidConfig Config
Definition: SquidConfig.cc:12
StatCounters statCounter
Definition: StatCounters.cc:12
StatHistBinDumper statHistIntDumper
Definition: StatHist.h:119
#define assert(EX)
Definition: assert.h:19
AsyncCall::Pointer & Next()
Definition: AsyncCall.h:65
struct SquidConfig::@116::@124 tcp
struct SquidConfig::@116 comm_incoming
struct SquidConfig::@116::@124 dns
struct SquidConfig::@116::@124 udp
double select_time
Definition: StatCounters.h:120
StatHist select_fds_hist
Definition: StatCounters.h:127
struct StatCounters::@135 syscalls
StatHist comm_tcp_incoming
Definition: StatCounters.h:126
unsigned long int select_loops
Definition: StatCounters.h:118
StatHist comm_udp_incoming
Definition: StatCounters.h:124
StatHist comm_dns_incoming
Definition: StatCounters.h:125
void count(double val)
Definition: StatHist.cc:55
void dump(StoreEntry *sentry, StatHistBinDumper *bd) const
Definition: StatHist.cc:171
Definition: fde.h:52
void PF(int, void *)
Definition: forward.h:18
void fd_close(const int fd)
Definition: minimal.cc:20
void commCallCloseHandlers(int fd)
Definition: comm.cc:727
int ignoreErrno(int ierrno)
Definition: comm.cc:1412
#define MYNAME
Definition: Stream.h:238
#define DBG_IMPORTANT
Definition: Stream.h:41
#define debugs(SECTION, LEVEL, CONTENT)
Definition: Stream.h:196
#define DBG_CRITICAL
Definition: Stream.h:40
#define COMM_SELECT_READ
Definition: defines.h:24
#define COMM_SELECT_WRITE
Definition: defines.h:25
int type
Definition: errorpage.cc:152
const char * fdTypeStr[]
Definition: fd.cc:39
#define fd_table
Definition: fde.h:189
int DnsSocketB
int DnsSocketA
int shutting_down
int Biggest_FD
int incoming_sockets_accepted
Comm::ConnectionPointer icpOutgoingConn
Definition: icp_v2.cc:100
Comm::ConnectionPointer icpIncomingConn
Definition: icp_v2.cc:98
static uint32 F(uint32 X, uint32 Y, uint32 Z)
Definition: md4.c:46
void OBJH(StoreEntry *)
Definition: forward.h:44
void QuickPollRequired(void)
Definition: ModDevPoll.cc:417
bool IsConnOpen(const Comm::ConnectionPointer &conn)
Definition: Connection.cc:27
Flag
Definition: Flag.h:15
@ SHUTDOWN
Definition: Flag.h:20
@ OK
Definition: Flag.h:16
@ IDLE
Definition: Flag.h:21
@ TIMEOUT
Definition: Flag.h:19
@ COMM_ERROR
Definition: Flag.h:17
Comm::Flag DoSelect(int)
Do poll and trigger callback functions as appropriate.
Definition: ModDevPoll.cc:311
void SelectLoopInit(void)
Initialize the module on Squid startup.
Definition: ModDevPoll.cc:176
void SetSelect(int, unsigned int, PF *, void *, time_t)
Mark an FD to be watched for its IO status.
Definition: ModDevPoll.cc:223
void RegisterAction(char const *action, char const *desc, OBJH *handler, int pw_req_flag, int atomic)
Definition: Registration.cc:16
static void handler(int signo)
Definition: purge.cc:854
void storeAppendPrintf(StoreEntry *e, const char *fmt,...)
Definition: store.cc:830
double current_dtime
the current UNIX time in seconds (with microsecond precision)
Definition: stub_libtime.cc:19
time_t getCurrentTime() STUB_RETVAL(0) int tvSubUsec(struct timeval
int nfds
Definition: tcp-banger2.c:129
int maxfd
Definition: tcp-banger2.c:130
const char * xstrerr(int error)
Definition: xstrerror.cc:83

 

Introduction

Documentation

Support

Miscellaneous

Web Site Translations

Mirrors