ModDevPoll.cc
Go to the documentation of this file.
1 /*
2  * Copyright (C) 1996-2017 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 
303 void
305 {
308 }
309 
324 Comm::DoSelect(int msec)
325 {
326  int num, i;
327  fde *F;
328  PF *hdl;
329 
330  PROF_start(comm_check_incoming);
331 
332  if (msec > max_poll_time)
333  msec = max_poll_time;
334 
335  for (;;) {
336  do_poll.dp_timeout = msec;
337  do_poll.dp_nfds = dpoll_nfds;
338 
339  comm_flush_updates(); /* ensure latest changes are sent to /dev/poll */
340 
341  num = ioctl(devpoll_fd, DP_POLL, &do_poll);
343 
344  if (num >= 0)
345  break; /* no error, skip out of loop */
346 
347  if (ignoreErrno(errno))
348  break; /* error is one we may ignore, skip out of loop */
349 
350  /* error during poll */
351  getCurrentTime();
352  PROF_stop(comm_check_incoming);
353  return Comm::COMM_ERROR;
354  }
355 
356  PROF_stop(comm_check_incoming);
357  getCurrentTime();
358 
360 
361  if (num == 0)
362  return Comm::TIMEOUT; /* no error */
363 
364  PROF_start(comm_handle_ready_fd);
365 
366  for (i = 0; i < num; ++i) {
367  int fd = (int)do_poll.dp_fds[i].fd;
368  F = &fd_table[fd];
369  debugs(
370  5,
371  DEBUG_DEVPOLL ? 0 : 8,
372  HERE << "got FD " << fd
373  << ",events=" << std::hex << do_poll.dp_fds[i].revents
374  << ",monitoring=" << devpoll_state[fd].state
375  << ",F->read_handler=" << F->read_handler
376  << ",F->write_handler=" << F->write_handler
377  );
378 
379  /* handle errors */
380  if (do_poll.dp_fds[i].revents & (POLLERR | POLLHUP | POLLNVAL)) {
381  debugs(
382  5,
383  DEBUG_DEVPOLL ? 0 : 8,
384  HERE << "devpoll event error: fd " << fd
385  );
386  continue;
387  }
388 
389  /* check if file descriptor has data to read */
390  if (do_poll.dp_fds[i].revents & POLLIN || F->flags.read_pending) {
391  if ( (hdl = F->read_handler) != NULL ) {
392  debugs(
393  5,
394  DEBUG_DEVPOLL ? 0 : 8,
395  HERE << "Calling read handler on FD " << fd
396  );
397  PROF_start(comm_read_handler);
398  F->flags.read_pending = 0;
399  F->read_handler = NULL;
400  hdl(fd, F->read_data);
401  PROF_stop(comm_read_handler);
403  } else {
404  debugs(
405  5,
406  DEBUG_DEVPOLL ? 0 : 8,
407  HERE << "no read handler for FD " << fd
408  );
409  // remove interest since no handler exist for this event.
411  }
412  }
413 
414  /* check if file descriptor is ready to write */
415  if (do_poll.dp_fds[i].revents & POLLOUT) {
416  if ((hdl = F->write_handler) != NULL) {
417  debugs(
418  5,
419  DEBUG_DEVPOLL ? 0 : 8,
420  HERE << "Calling write handler on FD " << fd
421  );
422  PROF_start(comm_write_handler);
423  F->write_handler = NULL;
424  hdl(fd, F->write_data);
425  PROF_stop(comm_write_handler);
427  } else {
428  debugs(
429  5,
430  DEBUG_DEVPOLL ? 0 : 8,
431  HERE << "no write handler for FD " << fd
432  );
433  // remove interest since no handler exist for this event.
435  }
436  }
437  }
438 
439  PROF_stop(comm_handle_ready_fd);
440  return Comm::OK;
441 }
442 
443 void
445 {
446  max_poll_time = 10;
447 }
448 
449 #endif /* USE_DEVPOLL */
450 
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
void ResetSelect(int)
reset/undo/unregister the watch for an FD which was set by Comm::SetSelect()
Definition: ModDevPoll.cc:304
#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:79
#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:187
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:79
const char * xstrerr(int error)
Definition: xstrerror.cc:83
Comm::Flag DoSelect(int)
Do poll and trigger callback functions as appropriate.
Definition: ModDevPoll.cc:324
#define debugs(SECTION, LEVEL, CONTENT)
Definition: Debug.h:123
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:147
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:123
static int devpoll_fd
Definition: ModDevPoll.cc:81
void storeAppendPrintf(StoreEntry *e, const char *fmt,...)
Definition: store.cc:904
#define NULL
Definition: types.h:166
int size
Definition: ModDevPoll.cc:77
void QuickPollRequired(void)
Definition: ModDevPoll.cc:444
Current state.
Definition: ModDevPoll.cc:62
static void commIncomingStats(StoreEntry *sentry)
Definition: ModDevPoll.cc:152

 

Introduction

Documentation

Support

Miscellaneous

Web Site Translations

Mirrors