ModDevPoll.cc
Go to the documentation of this file.
1 /*
2  * Copyright (C) 1996-2018 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  * Last modified 2010-10-08
22  */
23 
24 /*
25  * There are several poll types in Squid, ALL of which are compiled and linked
26  * in. Thus conditional compile-time flags are used to prevent the different
27  * modules from creating several versions of the same function simultaneously.
28  */
29 
30 #include "squid.h"
31 
32 #if USE_DEVPOLL
33 
34 #include "comm/Loops.h"
35 #include "fd.h"
36 #include "fde.h"
37 #include "mgr/Registration.h"
38 #include "profiler/Profiler.h"
39 #include "SquidTime.h"
40 #include "StatCounters.h"
41 #include "StatHist.h"
42 #include "Store.h"
43 
44 #include <cerrno>
45 #include <climits>
46 #if HAVE_SYS_DEVPOLL_H
47 /* Solaris /dev/poll support, see "man -s 7D poll" */
48 #include <sys/devpoll.h>
49 #endif
50 
51 #define DEBUG_DEVPOLL 0
52 
53 // OPEN_MAX is defined in <climits>
54 #define DEVPOLL_UPDATESIZE OPEN_MAX
55 #define DEVPOLL_QUERYSIZE OPEN_MAX
56 
57 /* TYPEDEFS */
58 typedef short pollfd_events_t; /* type of pollfd.events from sys/poll.h */
59 
60 /* STRUCTURES */
64 };
65 
74 static struct {
75  struct pollfd *pfds;
76  int cur;
77  int size;
79 
80 /* STATIC VARIABLES */
81 static int devpoll_fd;
82 static int max_poll_time = 1000;
84 static struct _devpoll_state *devpoll_state;
85 static struct dvpoll do_poll;
86 static int dpoll_nfds;
88 /* PROTOTYPES */
89 static void commDevPollRegisterWithCacheManager(void);
90 
91 /* PRIVATE FUNCTIONS */
100 static void
102 {
103  int i;
104  if (devpoll_update.cur == -1)
105  return; /* array of changes to make is empty */
106 
107  debugs(
108  5,
109  DEBUG_DEVPOLL ? 0 : 8,
110  HERE << (devpoll_update.cur + 1) << " fds queued"
111  );
112 
113  i = write(
114  devpoll_fd, /* open handle to /dev/poll */
115  devpoll_update.pfds, /* pointer to array of struct pollfd */
116  (devpoll_update.cur + 1) * sizeof(struct pollfd) /* bytes to process */
117  );
118  assert(i > 0);
119  assert(static_cast<size_t>(i) == (sizeof(struct pollfd) * (devpoll_update.cur + 1)));
120  devpoll_update.cur = -1; /* reset size of array, no elements remain */
121 }
122 
132 static void
133 comm_update_fd(int fd, int events)
134 {
135  debugs(
136  5,
137  DEBUG_DEVPOLL ? 0 : 8,
138  HERE << "FD " << fd << ", events=" << events
139  );
140 
141  /* Is the array already full and in need of flushing? */
142  if (devpoll_update.cur != -1 && (devpoll_update.cur == devpoll_update.size))
144 
145  /* Push new event onto array */
146  ++ devpoll_update.cur;
147  devpoll_update.pfds[devpoll_update.cur].fd = fd;
148  devpoll_update.pfds[devpoll_update.cur].events = events;
149  devpoll_update.pfds[devpoll_update.cur].revents = 0;
150 }
151 
152 static void commIncomingStats(StoreEntry *sentry)
153 {
154  storeAppendPrintf(sentry, "Total number of devpoll loops: %ld\n", statCounter.select_loops);
155  storeAppendPrintf(sentry, "Histogram of returned filedescriptors\n");
157 }
158 
159 static void
161 {
163  "comm_devpoll_incoming",
164  "comm_incoming() stats",
166  0,
167  1
168  );
169 }
170 
171 /* PUBLIC FUNCTIONS */
172 
177 void
179 {
180  /* allocate memory first before attempting to open poll device */
181  /* This tracks the FD devpoll offset+state */
182  devpoll_state = (struct _devpoll_state *)xcalloc(
183  SQUID_MAXFD, sizeof(struct _devpoll_state)
184  );
185 
186  /* And this is the stuff we use to read events */
187  do_poll.dp_fds = (struct pollfd *)xcalloc(
188  DEVPOLL_QUERYSIZE, sizeof(struct pollfd)
189  );
191 
192  devpoll_update.pfds = (struct pollfd *)xcalloc(
193  DEVPOLL_UPDATESIZE, sizeof(struct pollfd)
194  );
195  devpoll_update.cur = -1;
197 
198  /* attempt to open /dev/poll device */
199  devpoll_fd = open("/dev/poll", O_RDWR);
200  if (devpoll_fd < 0) {
201  int xerrno = errno;
202  fatalf("comm_select_init: can't open /dev/poll: %s\n", xstrerr(xerrno));
203  }
204 
205  fd_open(devpoll_fd, FD_UNKNOWN, "devpoll ctl");
206 
208 }
209 
224 void
225 Comm::SetSelect(int fd, unsigned int type, PF * handler, void *client_data, time_t timeout)
226 {
227  assert(fd >= 0);
228  debugs(5, 5, HERE << "FD " << fd << ", type=" << type <<
229  ", handler=" << handler << ", client_data=" << client_data <<
230  ", timeout=" << timeout);
231 
232  /* POLLIN/POLLOUT are defined in <sys/poll.h> */
233  fde *F = &fd_table[fd];
234  if (!F->flags.open) {
235  /* remove from poll set */
236  comm_update_fd( fd, POLLREMOVE );
237  devpoll_state[fd].state = 0;
238  return;
239  }
240 
241  pollfd_events_t state_old = devpoll_state[fd].state;
242  pollfd_events_t state_new = 0; /* new state (derive from old state) */
243 
244  if ( type & COMM_SELECT_READ ) {
245  if ( handler != NULL ) {
246  // Hack to keep the events flowing if there is data immediately ready
247  if (F->flags.read_pending)
248  state_new |= POLLOUT;
249  /* we want to POLLIN */
250  state_new |= POLLIN;
251  } else {
252  ; /* we want to clear POLLIN because handler is NULL */
253  }
254 
255  F->read_handler = handler;
256  F->read_data = client_data;
257  } else if ( state_old & POLLIN ) {
258  /* we're not changing reading state so take from existing */
259  state_new |= POLLIN;
260  }
261 
262  if ( type & COMM_SELECT_WRITE ) {
263  if ( handler != NULL ) {
264  /* we want to POLLOUT */
265  state_new |= POLLOUT;
266  } else {
267  ; /* we want to clear POLLOUT because handler is NULL */
268  }
269 
270  F->write_handler = handler;
271  F->write_data = client_data;
272  } else if ( state_old & POLLOUT ) {
273  /* we're not changing writing state so take from existing */
274  state_new |= POLLOUT;
275  }
276 
277  if ( pollfd_events_t bits_changed = (state_old ^ state_new) ) {
278  /* something has changed, update /dev/poll of what to listen for */
279 
280  /* did any bits clear? (in which case a poll remove is necessary) */
281  if ( bits_changed & state_old ) {
282  comm_update_fd( fd, POLLREMOVE );
283  /* existing state cleared, so update with all required events */
284  if ( state_new )
285  comm_update_fd( fd, state_new );
286  } else {
287  /* only update with new required event */
288  if ( pollfd_events_t newly_set_only = (bits_changed & state_new) )
289  comm_update_fd( fd, newly_set_only );
290  }
291 
292  devpoll_state[fd].state = state_new;
293  }
294 
295  if (timeout)
296  F->timeout = squid_curtime + timeout;
297 }
298 
313 Comm::DoSelect(int msec)
314 {
315  int num, i;
316  fde *F;
317  PF *hdl;
318 
319  PROF_start(comm_check_incoming);
320 
321  if (msec > max_poll_time)
322  msec = max_poll_time;
323 
324  for (;;) {
325  do_poll.dp_timeout = msec;
326  do_poll.dp_nfds = dpoll_nfds;
327 
328  comm_flush_updates(); /* ensure latest changes are sent to /dev/poll */
329 
330  num = ioctl(devpoll_fd, DP_POLL, &do_poll);
332 
333  if (num >= 0)
334  break; /* no error, skip out of loop */
335 
336  if (ignoreErrno(errno))
337  break; /* error is one we may ignore, skip out of loop */
338 
339  /* error during poll */
340  getCurrentTime();
341  PROF_stop(comm_check_incoming);
342  return Comm::COMM_ERROR;
343  }
344 
345  PROF_stop(comm_check_incoming);
346  getCurrentTime();
347 
349 
350  if (num == 0)
351  return Comm::TIMEOUT; /* no error */
352 
353  PROF_start(comm_handle_ready_fd);
354 
355  for (i = 0; i < num; ++i) {
356  int fd = (int)do_poll.dp_fds[i].fd;
357  F = &fd_table[fd];
358  debugs(
359  5,
360  DEBUG_DEVPOLL ? 0 : 8,
361  HERE << "got FD " << fd
362  << ",events=" << std::hex << do_poll.dp_fds[i].revents
363  << ",monitoring=" << devpoll_state[fd].state
364  << ",F->read_handler=" << F->read_handler
365  << ",F->write_handler=" << F->write_handler
366  );
367 
368  /* handle errors */
369  if (do_poll.dp_fds[i].revents & (POLLERR | POLLHUP | POLLNVAL)) {
370  debugs(
371  5,
372  DEBUG_DEVPOLL ? 0 : 8,
373  HERE << "devpoll event error: fd " << fd
374  );
375  continue;
376  }
377 
378  /* check if file descriptor has data to read */
379  if (do_poll.dp_fds[i].revents & POLLIN || F->flags.read_pending) {
380  if ( (hdl = F->read_handler) != NULL ) {
381  debugs(
382  5,
383  DEBUG_DEVPOLL ? 0 : 8,
384  HERE << "Calling read handler on FD " << fd
385  );
386  PROF_start(comm_read_handler);
387  F->flags.read_pending = 0;
388  F->read_handler = NULL;
389  hdl(fd, F->read_data);
390  PROF_stop(comm_read_handler);
392  } else {
393  debugs(
394  5,
395  DEBUG_DEVPOLL ? 0 : 8,
396  HERE << "no read handler for FD " << fd
397  );
398  // remove interest since no handler exist for this event.
400  }
401  }
402 
403  /* check if file descriptor is ready to write */
404  if (do_poll.dp_fds[i].revents & POLLOUT) {
405  if ((hdl = F->write_handler) != NULL) {
406  debugs(
407  5,
408  DEBUG_DEVPOLL ? 0 : 8,
409  HERE << "Calling write handler on FD " << fd
410  );
411  PROF_start(comm_write_handler);
412  F->write_handler = NULL;
413  hdl(fd, F->write_data);
414  PROF_stop(comm_write_handler);
416  } else {
417  debugs(
418  5,
419  DEBUG_DEVPOLL ? 0 : 8,
420  HERE << "no write handler for FD " << fd
421  );
422  // remove interest since no handler exist for this event.
424  }
425  }
426  }
427 
428  PROF_stop(comm_handle_ready_fd);
429  return Comm::OK;
430 }
431 
432 void
434 {
435  max_poll_time = 10;
436 }
437 
438 #endif /* USE_DEVPOLL */
439 
void count(double val)
Definition: StatHist.cc:57
#define fd_table
Definition: fde.h:157
#define COMM_SELECT_READ
Definition: defines.h:36
StatCounters statCounter
Definition: StatCounters.cc:12
#define assert(EX)
Definition: assert.h:17
#define DEVPOLL_QUERYSIZE
Definition: ModDevPoll.cc:55
void SelectLoopInit(void)
Initialize the module on Squid startup.
Definition: ModDevPoll.cc:178
struct fde::_fde_flags flags
int type
Definition: errorpage.cc:78
#define xcalloc
Definition: membanger.c:57
short pollfd_events_t
Definition: ModDevPoll.cc:58
int i
Definition: membanger.c:49
static void comm_flush_updates(void)
Write batched file descriptor event changes to poll device.
Definition: ModDevPoll.cc:101
void fd_open(int fd, unsigned int type, const char *desc)
Definition: fd.cc:186
Definition: Flag.h:16
PF * write_handler
Definition: fde.h:128
time_t squid_curtime
Definition: stub_time.cc:17
static struct dvpoll do_poll
Definition: ModDevPoll.cc:85
void fatalf(const char *fmt,...)
Definition: fatal.cc:68
const char * xstrerr(int error)
Definition: xstrerror.cc:83
Comm::Flag DoSelect(int)
Do poll and trigger callback functions as appropriate.
Definition: ModDevPoll.cc:313
#define debugs(SECTION, LEVEL, CONTENT)
Definition: Debug.h:124
PF * read_handler
Definition: fde.h:126
void SetSelect(int, unsigned int, PF *, void *, time_t)
Mark an FD to be watched for its IO status.
Definition: ModDevPoll.cc:225
void RegisterAction(char const *action, char const *desc, OBJH *handler, int pw_req_flag, int atomic)
Definition: Registration.cc:16
#define COMM_SELECT_WRITE
Definition: defines.h:37
#define DEBUG_DEVPOLL
Definition: ModDevPoll.cc:51
void * write_data
Definition: fde.h:129
StatHist select_fds_hist
Definition: StatCounters.h:127
void PF(int, void *)
Definition: forward.h:18
void * read_data
Definition: fde.h:127
unsigned long int select_loops
Definition: StatCounters.h:118
static void comm_update_fd(int fd, int events)
Register change in desired polling state for file descriptor.
Definition: ModDevPoll.cc:133
int cur
Definition: ModDevPoll.cc:76
static void handler(int signo)
Definition: purge.cc:860
static struct _devpoll_state * devpoll_state
Definition: ModDevPoll.cc:84
std::ostream & HERE(std::ostream &s)
Definition: Debug.h:153
static void commDevPollRegisterWithCacheManager(void)
Definition: ModDevPoll.cc:160
static int max_poll_time
Definition: ModDevPoll.cc:82
Flag
Definition: Flag.h:15
static int dpoll_nfds
Definition: ModDevPoll.cc:86
bool SIGHDLR int STUB void int
Definition: stub_tools.cc:68
int ignoreErrno(int ierrno)
Definition: comm.cc:1477
time_t getCurrentTime(void)
Get current time.
bool read_pending
Definition: fde.h:106
static struct @43 devpoll_update
Update list.
#define PROF_start(probename)
Definition: Profiler.h:62
bool open
Definition: fde.h:96
pollfd_events_t state
Definition: ModDevPoll.cc:63
Definition: fde.h:49
struct pollfd * pfds
Definition: ModDevPoll.cc:75
time_t timeout
Definition: fde.h:131
static uint32 F(uint32 X, uint32 Y, uint32 Z)
Definition: md4.c:46
#define PROF_stop(probename)
Definition: Profiler.h:63
void dump(StoreEntry *sentry, StatHistBinDumper *bd) const
Definition: StatHist.cc:173
#define DEVPOLL_UPDATESIZE
Definition: ModDevPoll.cc:54
StatHistBinDumper statHistIntDumper
Definition: StatHist.h:119
static int devpoll_fd
Definition: ModDevPoll.cc:81
void storeAppendPrintf(StoreEntry *e, const char *fmt,...)
Definition: store.cc:875
#define NULL
Definition: types.h:166
int size
Definition: ModDevPoll.cc:77
void QuickPollRequired(void)
Definition: ModDevPoll.cc:433
Current state.
Definition: ModDevPoll.cc:62
static void commIncomingStats(StoreEntry *sentry)
Definition: ModDevPoll.cc:152

 

Introduction

Documentation

Support

Miscellaneous

Web Site Translations

Mirrors