ModDevPoll.cc
Go to the documentation of this file.
1 /*
2  * Copyright (C) 1996-2023 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 /*
12  * This is a very simple driver for Solaris /dev/poll.
13  *
14  * The updates are batched, one trip through the comm loop.
15  * (like libevent.) We keep a pointer into the structs so we
16  * can zero out an entry in the poll list if its active.
17  *
18  * Ported by Peter Payne from Squid 2.7.STABLE9 comm_devpoll.c
19  * on August 11, 2010 at 3pm (GMT+0100 Europe/London).
20  */
21 /*
22  * There are several poll types in Squid, ALL of which are compiled and linked
23  * in. Thus conditional compile-time flags are used to prevent the different
24  * modules from creating several versions of the same function simultaneously.
25  */
26 
27 #include "squid.h"
28 
29 #if USE_DEVPOLL
30 
31 #include "base/IoManip.h"
32 #include "comm/Loops.h"
33 #include "fd.h"
34 #include "fde.h"
35 #include "mgr/Registration.h"
36 #include "StatCounters.h"
37 #include "StatHist.h"
38 #include "Store.h"
39 
40 #include <cerrno>
41 #include <climits>
42 #if HAVE_SYS_DEVPOLL_H
43 /* Solaris /dev/poll support, see "man -s 7D poll" */
44 #include <sys/devpoll.h>
45 #endif
46 
47 #define DEBUG_DEVPOLL 0
48 
49 /* TYPEDEFS */
50 typedef short pollfd_events_t; /* type of pollfd.events from sys/poll.h */
51 
52 /* STRUCTURES */
56 };
57 
66 static struct {
67  struct pollfd *pfds;
68  int cur;
69  int size;
71 
72 /* STATIC VARIABLES */
73 static int devpoll_fd;
74 static int max_poll_time = 1000;
76 static struct _devpoll_state *devpoll_state;
77 static struct dvpoll do_poll;
78 static int dpoll_nfds;
80 /* PROTOTYPES */
81 static void commDevPollRegisterWithCacheManager(void);
82 
83 /* PRIVATE FUNCTIONS */
92 static void
94 {
95  int i;
96  if (devpoll_update.cur == -1)
97  return; /* array of changes to make is empty */
98 
99  debugs(
100  5,
101  DEBUG_DEVPOLL ? 0 : 8,
102  (devpoll_update.cur + 1) << " fds queued"
103  );
104 
105  i = write(
106  devpoll_fd, /* open handle to /dev/poll */
107  devpoll_update.pfds, /* pointer to array of struct pollfd */
108  (devpoll_update.cur + 1) * sizeof(struct pollfd) /* bytes to process */
109  );
110  assert(i > 0);
111  assert(static_cast<size_t>(i) == (sizeof(struct pollfd) * (devpoll_update.cur + 1)));
112  devpoll_update.cur = -1; /* reset size of array, no elements remain */
113 }
114 
124 static void
126 {
127  debugs(
128  5,
129  DEBUG_DEVPOLL ? 0 : 8,
130  "FD " << fd << ", events=" << events
131  );
132 
133  /* Is the array already full and in need of flushing? */
134  if (devpoll_update.cur != -1 && (devpoll_update.cur == devpoll_update.size))
136 
137  /* Push new event onto array */
138  ++ devpoll_update.cur;
139  devpoll_update.pfds[devpoll_update.cur].fd = fd;
140  devpoll_update.pfds[devpoll_update.cur].events = events;
141  devpoll_update.pfds[devpoll_update.cur].revents = 0;
142 }
143 
144 static void commIncomingStats(StoreEntry *sentry)
145 {
146  storeAppendPrintf(sentry, "Total number of devpoll loops: %ld\n", statCounter.select_loops);
147  storeAppendPrintf(sentry, "Histogram of returned filedescriptors\n");
149 }
150 
151 static void
153 {
155  "comm_devpoll_incoming",
156  "comm_incoming() stats",
158  0,
159  1
160  );
161 }
162 
163 /* PUBLIC FUNCTIONS */
164 
169 void
171 {
172  /* allocate memory first before attempting to open poll device */
173  /* This tracks the FD devpoll offset+state */
175  Squid_MaxFD, sizeof(struct _devpoll_state)
176  );
177 
178  /* This is the stuff we use to read events. If it's larger than
179  the current RLIMIT_NOFILE, the Solaris kernel returns EINVAL. */
181  do_poll.dp_fds = (struct pollfd *)xcalloc(
182  dpoll_nfds, sizeof(struct pollfd)
183  );
184 
185  /* This is the stuff we use to write requests to change tracking state.
186  It's also limited to the current RLIMIT_NOFILE by the Solaris kernel. */
187  devpoll_update.cur = -1;
189  devpoll_update.pfds = (struct pollfd *)xcalloc(
190  devpoll_update.size, sizeof(struct pollfd)
191  );
192 
193  /* attempt to open /dev/poll device */
194  devpoll_fd = open("/dev/poll", O_RDWR);
195  if (devpoll_fd < 0) {
196  int xerrno = errno;
197  fatalf("comm_select_init: can't open /dev/poll: %s\n", xstrerr(xerrno));
198  }
199 
200  fd_open(devpoll_fd, FD_UNKNOWN, "devpoll ctl");
201 
203 }
204 
219 void
220 Comm::SetSelect(int fd, unsigned int type, PF * handler, void *client_data, time_t timeout)
221 {
222  assert(fd >= 0);
223  debugs(5, 5, "FD " << fd << ", type=" << type <<
224  ", handler=" << handler << ", client_data=" << client_data <<
225  ", timeout=" << timeout);
226 
227  /* POLLIN/POLLOUT are defined in <sys/poll.h> */
228  fde *F = &fd_table[fd];
229  if (!F->flags.open) {
230  /* remove from poll set */
231  comm_update_fd( fd, POLLREMOVE );
232  devpoll_state[fd].state = 0;
233  return;
234  }
235 
236  pollfd_events_t state_old = devpoll_state[fd].state;
237  pollfd_events_t state_new = 0; /* new state (derive from old state) */
238 
239  if ( type & COMM_SELECT_READ ) {
240  if ( handler != NULL ) {
241  // Hack to keep the events flowing if there is data immediately ready
242  if (F->flags.read_pending)
243  state_new |= POLLOUT;
244  /* we want to POLLIN */
245  state_new |= POLLIN;
246  } else {
247  ; /* we want to clear POLLIN because handler is NULL */
248  }
249 
250  F->read_handler = handler;
251  F->read_data = client_data;
252  } else if ( state_old & POLLIN ) {
253  /* we're not changing reading state so take from existing */
254  state_new |= POLLIN;
255  }
256 
257  if ( type & COMM_SELECT_WRITE ) {
258  if ( handler != NULL ) {
259  /* we want to POLLOUT */
260  state_new |= POLLOUT;
261  } else {
262  ; /* we want to clear POLLOUT because handler is NULL */
263  }
264 
265  F->write_handler = handler;
266  F->write_data = client_data;
267  } else if ( state_old & POLLOUT ) {
268  /* we're not changing writing state so take from existing */
269  state_new |= POLLOUT;
270  }
271 
272  if ( pollfd_events_t bits_changed = (state_old ^ state_new) ) {
273  /* something has changed, update /dev/poll of what to listen for */
274 
275  /* did any bits clear? (in which case a poll remove is necessary) */
276  if ( bits_changed & state_old ) {
277  comm_update_fd( fd, POLLREMOVE );
278  /* existing state cleared, so update with all required events */
279  if ( state_new )
280  comm_update_fd( fd, state_new );
281  } else {
282  /* only update with new required event */
283  if ( pollfd_events_t newly_set_only = (bits_changed & state_new) )
284  comm_update_fd( fd, newly_set_only );
285  }
286 
287  devpoll_state[fd].state = state_new;
288  }
289 
290  if (timeout)
291  F->timeout = squid_curtime + timeout;
292 }
293 
308 Comm::DoSelect(int msec)
309 {
310  int num, i;
311  fde *F;
312  PF *hdl;
313 
314  if (msec > max_poll_time)
315  msec = max_poll_time;
316 
317  for (;;) {
318  do_poll.dp_timeout = msec;
319  do_poll.dp_nfds = dpoll_nfds;
320 
321  comm_flush_updates(); /* ensure latest changes are sent to /dev/poll */
322 
323  num = ioctl(devpoll_fd, DP_POLL, &do_poll);
325 
326  if (num >= 0)
327  break; /* no error, skip out of loop */
328 
329  if (ignoreErrno(errno))
330  break; /* error is one we may ignore, skip out of loop */
331 
332  /* error during poll */
333  getCurrentTime();
334  return Comm::COMM_ERROR;
335  }
336 
337  getCurrentTime();
338 
340 
341  if (num == 0)
342  return Comm::TIMEOUT; /* no error */
343 
344  for (i = 0; i < num; ++i) {
345  int fd = (int)do_poll.dp_fds[i].fd;
346  F = &fd_table[fd];
347  debugs(
348  5,
349  DEBUG_DEVPOLL ? 0 : 8,
350  "got FD " << fd
351  << ",events=" << asHex(do_poll.dp_fds[i].revents)
352  << ",monitoring=" << asHex(devpoll_state[fd].state)
353  << ",F->read_handler=" << F->read_handler
354  << ",F->write_handler=" << F->write_handler
355  );
356 
357  /* handle errors */
358  if (do_poll.dp_fds[i].revents & (POLLERR | POLLHUP | POLLNVAL)) {
359  debugs(5, DEBUG_DEVPOLL ? 0 : 8,
360  "ERROR: devpoll event failure: fd " << fd
361  );
362  continue;
363  }
364 
365  /* check if file descriptor has data to read */
366  if (do_poll.dp_fds[i].revents & POLLIN || F->flags.read_pending) {
367  if ( (hdl = F->read_handler) != NULL ) {
368  debugs(
369  5,
370  DEBUG_DEVPOLL ? 0 : 8,
371  "Calling read handler on FD " << fd
372  );
373  F->read_handler = nullptr;
374  hdl(fd, F->read_data);
376  } else {
377  debugs(
378  5,
379  DEBUG_DEVPOLL ? 0 : 8,
380  "no read handler for FD " << fd
381  );
382  // remove interest since no handler exist for this event.
383  SetSelect(fd, COMM_SELECT_READ, nullptr, nullptr, 0);
384  }
385  }
386 
387  /* check if file descriptor is ready to write */
388  if (do_poll.dp_fds[i].revents & POLLOUT) {
389  if ((hdl = F->write_handler) != NULL) {
390  debugs(
391  5,
392  DEBUG_DEVPOLL ? 0 : 8,
393  "Calling write handler on FD " << fd
394  );
395  F->write_handler = nullptr;
396  hdl(fd, F->write_data);
398  } else {
399  debugs(
400  5,
401  DEBUG_DEVPOLL ? 0 : 8,
402  "no write handler for FD " << fd
403  );
404  // remove interest since no handler exist for this event.
405  SetSelect(fd, COMM_SELECT_WRITE, nullptr, nullptr, 0);
406  }
407  }
408  }
409 
410  return Comm::OK;
411 }
412 
413 void
415 {
416  max_poll_time = 10;
417 }
418 
419 #endif /* USE_DEVPOLL */
420 
const char * xstrerr(int error)
Definition: xstrerror.cc:83
void * xcalloc(size_t n, size_t sz)
Definition: xalloc.cc:71
short pollfd_events_t
Definition: ModDevPoll.cc:50
static int max_poll_time
Definition: ModDevPoll.cc:74
static struct _devpoll_state * devpoll_state
Definition: ModDevPoll.cc:76
void storeAppendPrintf(StoreEntry *e, const char *fmt,...)
Definition: store.cc:855
Comm::Flag DoSelect(int)
Do poll and trigger callback functions as appropriate.
Definition: ModDevPoll.cc:308
static uint32 F(uint32 X, uint32 Y, uint32 Z)
Definition: md4.c:46
@ TIMEOUT
Definition: Flag.h:18
@ OK
Definition: Flag.h:16
static void comm_flush_updates(void)
Write batched file descriptor event changes to poll device.
Definition: ModDevPoll.cc:93
struct pollfd * pfds
Definition: ModDevPoll.cc:67
void fd_open(const int fd, unsigned int, const char *description)
Definition: minimal.cc:15
Definition: fde.h:51
static void commIncomingStats(StoreEntry *sentry)
Definition: ModDevPoll.cc:144
static struct @39 devpoll_update
Update list.
int size
Definition: ModDevPoll.cc:69
#define NULL
Definition: types.h:145
time_t getCurrentTime() STUB_RETVAL(0) int tvSubUsec(struct timeval
void dump(StoreEntry *sentry, StatHistBinDumper *bd) const
Definition: StatHist.cc:171
pollfd_events_t state
Definition: ModDevPoll.cc:55
AsHex< Integer > asHex(const Integer n)
a helper to ease AsHex object creation
Definition: IoManip.h:169
static void commDevPollRegisterWithCacheManager(void)
Definition: ModDevPoll.cc:152
#define DEBUG_DEVPOLL
Definition: ModDevPoll.cc:47
void count(double val)
Definition: StatHist.cc:55
#define assert(EX)
Definition: assert.h:17
void fatalf(const char *fmt,...)
Definition: fatal.cc:68
@ COMM_ERROR
Definition: Flag.h:17
#define COMM_SELECT_READ
Definition: defines.h:24
static int dpoll_nfds
Definition: ModDevPoll.cc:78
time_t squid_curtime
Definition: stub_libtime.cc:20
int cur
Definition: ModDevPoll.cc:68
int Squid_MaxFD
void SelectLoopInit(void)
Initialize the module on Squid startup.
Definition: ModDevPoll.cc:170
Flag
Definition: Flag.h:15
int ignoreErrno(int ierrno)
Definition: comm.cc:1422
#define fd_table
Definition: fde.h:189
StatHist select_fds_hist
Definition: StatCounters.h:130
unsigned long int select_loops
Definition: StatCounters.h:119
StatHistBinDumper statHistIntDumper
Definition: StatHist.h:119
void SetSelect(int, unsigned int, PF *, void *, time_t)
Mark an FD to be watched for its IO status.
Definition: ModDevPoll.cc:220
void RegisterAction(char const *action, char const *desc, OBJH *handler, Protected, Atomic, Format)
Definition: Registration.cc:54
static void comm_update_fd(int fd, pollfd_events_t events)
Register change in desired polling state for file descriptor.
Definition: ModDevPoll.cc:125
static struct dvpoll do_poll
Definition: ModDevPoll.cc:77
@ FD_UNKNOWN
Definition: enums.h:19
#define debugs(SECTION, LEVEL, CONTENT)
Definition: Stream.h:192
#define COMM_SELECT_WRITE
Definition: defines.h:25
void QuickPollRequired(void)
Definition: ModDevPoll.cc:414
static int devpoll_fd
Definition: ModDevPoll.cc:73
void PF(int, void *)
Definition: forward.h:18
Current state.
Definition: ModDevPoll.cc:54
int unsigned int
Definition: stub_fd.cc:19
StatCounters statCounter
Definition: StatCounters.cc:12

 

Introduction

Documentation

Support

Miscellaneous