Squid Web Cache master
Loading...
Searching...
No Matches
ModDevPoll.cc
Go to the documentation of this file.
1/*
2 * Copyright (C) 1996-2025 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 "compat/unistd.h"
34#include "fd.h"
35#include "fde.h"
36#include "mgr/Registration.h"
37#include "StatCounters.h"
38#include "StatHist.h"
39#include "Store.h"
40
41#include <cerrno>
42#include <climits>
43#if HAVE_SYS_DEVPOLL_H
44/* Solaris /dev/poll support, see "man -s 7D poll" */
45#include <sys/devpoll.h>
46#endif
47
48#define DEBUG_DEVPOLL 0
49
50/* TYPEDEFS */
51typedef short pollfd_events_t; /* type of pollfd.events from sys/poll.h */
52
53/* STRUCTURES */
58
67static struct {
68 struct pollfd *pfds;
69 int cur;
70 int size;
72
73/* STATIC VARIABLES */
74static int devpoll_fd;
75static int max_poll_time = 1000;
78static struct dvpoll do_poll;
79static int dpoll_nfds;
81/* PROTOTYPES */
83
84/* PRIVATE FUNCTIONS */
93static void
95{
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 const auto i = xwrite(
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
124static 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
144static 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
151static void
153{
155 "comm_devpoll_incoming",
156 "comm_incoming() stats",
158 0,
159 1
160 );
161}
162
163/* PUBLIC FUNCTIONS */
164
169void
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 = xopen("/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
219void
220Comm::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
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 */
334 return Comm::COMM_ERROR;
335 }
336
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
413void
415{
416 max_poll_time = 10;
417}
418
419#endif /* USE_DEVPOLL */
420
AsHex< Integer > asHex(const Integer n)
a helper to ease AsHex object creation
Definition IoManip.h:169
static int max_poll_time
Definition ModDevPoll.cc:75
static void comm_update_fd(int fd, pollfd_events_t events)
Register change in desired polling state for file descriptor.
int size
Definition ModDevPoll.cc:70
static struct @32 devpoll_update
Update list.
static void comm_flush_updates(void)
Write batched file descriptor event changes to poll device.
Definition ModDevPoll.cc:94
static void commDevPollRegisterWithCacheManager(void)
static struct _devpoll_state * devpoll_state
Definition ModDevPoll.cc:77
static int dpoll_nfds
Definition ModDevPoll.cc:79
static struct dvpoll do_poll
Definition ModDevPoll.cc:78
static int devpoll_fd
Definition ModDevPoll.cc:74
int cur
Definition ModDevPoll.cc:69
short pollfd_events_t
Definition ModDevPoll.cc:51
struct pollfd * pfds
Definition ModDevPoll.cc:68
#define DEBUG_DEVPOLL
Definition ModDevPoll.cc:48
static OBJH commIncomingStats
Definition ModPoll.cc:56
time_t squid_curtime
StatCounters statCounter
StatHistBinDumper statHistIntDumper
Definition StatHist.h:119
#define assert(EX)
Definition assert.h:17
StatHist select_fds_hist
unsigned long int select_loops
void count(double val)
Definition StatHist.cc:55
void dump(StoreEntry *sentry, StatHistBinDumper *bd) const
Definition StatHist.cc:171
Definition fde.h:52
PF * read_handler
Definition fde.h:149
void * write_data
Definition fde.h:152
struct fde::_fde_flags flags
time_t timeout
Definition fde.h:154
void * read_data
Definition fde.h:150
PF * write_handler
Definition fde.h:151
void PF(int, void *)
Definition forward.h:18
void fd_open(const int fd, unsigned int, const char *description)
Definition minimal.cc:15
int ignoreErrno(int ierrno)
Definition comm.cc:1407
#define debugs(SECTION, LEVEL, CONTENT)
Definition Stream.h:192
#define COMM_SELECT_READ
Definition defines.h:24
#define COMM_SELECT_WRITE
Definition defines.h:25
@ FD_UNKNOWN
Definition enums.h:19
void fatalf(const char *fmt,...)
Definition fatal.cc:68
#define fd_table
Definition fde.h:189
int Squid_MaxFD
void QuickPollRequired(void)
Flag
Definition Flag.h:15
@ OK
Definition Flag.h:16
@ TIMEOUT
Definition Flag.h:18
@ COMM_ERROR
Definition Flag.h:17
Comm::Flag DoSelect(int)
Do poll and trigger callback functions as appropriate.
void SelectLoopInit(void)
Initialize the module on Squid startup.
void SetSelect(int, unsigned int, PF *, void *, time_t)
Mark an FD to be watched for its IO status.
void RegisterAction(char const *action, char const *desc, OBJH *handler, Protected, Atomic, Format)
void storeAppendPrintf(StoreEntry *e, const char *fmt,...)
Definition store.cc:855
Current state.
Definition ModDevPoll.cc:55
pollfd_events_t state
Definition ModDevPoll.cc:56
bool read_pending
buffering readMethod_ has data to give (regardless of socket state)
Definition fde.h:127
bool open
Definition fde.h:118
int unsigned int
Definition stub_fd.cc:19
time_t getCurrentTime() STUB_RETVAL(0) int tvSubUsec(struct timeval
#define NULL
Definition types.h:145
int xwrite(int fd, const void *buf, size_t bufSize)
POSIX write(2) equivalent.
Definition unistd.h:67
int xopen(const char *filename, int oflag, int pmode=0)
POSIX open(2) equivalent.
Definition unistd.h:55
void * xcalloc(size_t n, size_t sz)
Definition xalloc.cc:71
const char * xstrerr(int error)
Definition xstrerror.cc:83