]> git.ipfire.org Git - thirdparty/squid.git/blame - src/comm/ModDevPoll.cc
Source Format Enforcement (#1234)
[thirdparty/squid.git] / src / comm / ModDevPoll.cc
CommitLineData
a1ad2f9b 1/*
b8ae064d 2 * Copyright (C) 1996-2023 The Squid Software Foundation and contributors
a1ad2f9b 3 *
bbc27441
AJ
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.
a1ad2f9b
AJ
7 */
8
bbc27441
AJ
9/* DEBUG: section 05 Socket Functions */
10
a1ad2f9b
AJ
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
a1ad2f9b
AJ
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
f7f3304a 30#include "squid.h"
d841c88d 31
a1ad2f9b
AJ
32#if USE_DEVPOLL
33
d841c88d 34#include "comm/Loops.h"
a1d4c183 35#include "fd.h"
a1ad2f9b 36#include "fde.h"
8af645e7 37#include "mgr/Registration.h"
e1656dc4 38#include "StatCounters.h"
00a7574e 39#include "StatHist.h"
d841c88d 40#include "Store.h"
a1ad2f9b 41
074d6a40
AJ
42#include <cerrno>
43#include <climits>
a1ad2f9b
AJ
44#if HAVE_SYS_DEVPOLL_H
45/* Solaris /dev/poll support, see "man -s 7D poll" */
46#include <sys/devpoll.h>
47#endif
48
49#define DEBUG_DEVPOLL 0
50
074d6a40 51// OPEN_MAX is defined in <climits>
f53969cc
SM
52#define DEVPOLL_UPDATESIZE OPEN_MAX
53#define DEVPOLL_QUERYSIZE OPEN_MAX
a1ad2f9b
AJ
54
55/* TYPEDEFS */
56typedef short pollfd_events_t; /* type of pollfd.events from sys/poll.h */
57
58/* STRUCTURES */
59/** \brief Current state */
60struct _devpoll_state {
61 pollfd_events_t state; /**< current known state of file handle */
62};
63
64/** \brief Update list
65 *
66 * This structure contains an array of settings to send to the /dev/poll
67 * interface. Rather than send changes to /dev/poll one at a time they
68 * are pushed onto this array (updating cur to indicate how many of the
69 * pfds structure elements have been set) until it is full before it
70 * is written out the API.
71 */
14302ffa 72static struct {
a1ad2f9b
AJ
73 struct pollfd *pfds; /**< ptr to array of struct pollfd config elements */
74 int cur; /**< index of last written element of array, or -1 if none */
75 int size; /**< maximum number of elements in array */
76} devpoll_update;
77
a1ad2f9b
AJ
78/* STATIC VARIABLES */
79static int devpoll_fd; /**< handle to /dev/poll device */
80static int max_poll_time = 1000; /**< maximum milliseconds to spend in poll */
81
82static struct _devpoll_state *devpoll_state; /**< array of socket states */
83static struct dvpoll do_poll; /**< data struct for storing poll results */
84static int dpoll_nfds; /**< maximum number of poll results */
85
86/* PROTOTYPES */
87static void commDevPollRegisterWithCacheManager(void);
88
a1ad2f9b
AJ
89/* PRIVATE FUNCTIONS */
90/** \brief Write batched file descriptor event changes to poll device
91 *
92 * Writes out the static array of file descriptor event changes to the
93 * poll device. This is done only when necessary (i.e. just before
94 * the poll device is queried during the select call, and whenever
95 * the number of changes to store in the array exceeds the size of the
96 * array).
97 */
98static void
99comm_flush_updates(void)
100{
101 int i;
102 if (devpoll_update.cur == -1)
103 return; /* array of changes to make is empty */
104
105 debugs(
106 5,
107 DEBUG_DEVPOLL ? 0 : 8,
bf95c10a 108 (devpoll_update.cur + 1) << " fds queued"
a1ad2f9b
AJ
109 );
110
111 i = write(
112 devpoll_fd, /* open handle to /dev/poll */
113 devpoll_update.pfds, /* pointer to array of struct pollfd */
114 (devpoll_update.cur + 1) * sizeof(struct pollfd) /* bytes to process */
115 );
116 assert(i > 0);
8b37be18 117 assert(static_cast<size_t>(i) == (sizeof(struct pollfd) * (devpoll_update.cur + 1)));
a1ad2f9b
AJ
118 devpoll_update.cur = -1; /* reset size of array, no elements remain */
119}
120
121/** \brief Register change in desired polling state for file descriptor
122 *
123 * Prevents unnecessary calls to the /dev/poll API by queueing changes
124 * in the devpoll_update array. If the array fills up the comm_flush_updates
125 * function is called.
126 *
127 * @param fd file descriptor to register change with
128 * @param events events to register (usually POLLIN, POLLOUT, or POLLREMOVE)
129 */
130static void
131comm_update_fd(int fd, int events)
132{
133 debugs(
134 5,
135 DEBUG_DEVPOLL ? 0 : 8,
bf95c10a 136 "FD " << fd << ", events=" << events
a1ad2f9b
AJ
137 );
138
139 /* Is the array already full and in need of flushing? */
140 if (devpoll_update.cur != -1 && (devpoll_update.cur == devpoll_update.size))
141 comm_flush_updates();
142
143 /* Push new event onto array */
098346fd 144 ++ devpoll_update.cur;
a1ad2f9b
AJ
145 devpoll_update.pfds[devpoll_update.cur].fd = fd;
146 devpoll_update.pfds[devpoll_update.cur].events = events;
147 devpoll_update.pfds[devpoll_update.cur].revents = 0;
148}
149
a1ad2f9b
AJ
150static void commIncomingStats(StoreEntry *sentry)
151{
a1ad2f9b
AJ
152 storeAppendPrintf(sentry, "Total number of devpoll loops: %ld\n", statCounter.select_loops);
153 storeAppendPrintf(sentry, "Histogram of returned filedescriptors\n");
82146cc8 154 statCounter.select_fds_hist.dump(sentry, statHistIntDumper);
a1ad2f9b
AJ
155}
156
a1ad2f9b
AJ
157static void
158commDevPollRegisterWithCacheManager(void)
159{
8af645e7 160 Mgr::RegisterAction(
a1ad2f9b
AJ
161 "comm_devpoll_incoming",
162 "comm_incoming() stats",
163 commIncomingStats,
164 0,
165 1
166 );
167}
168
a1ad2f9b
AJ
169/* PUBLIC FUNCTIONS */
170
171/** \brief Initialise /dev/poll support
172 *
173 * Allocates memory, opens /dev/poll device handle.
174 */
175void
d841c88d 176Comm::SelectLoopInit(void)
a1ad2f9b
AJ
177{
178 /* allocate memory first before attempting to open poll device */
179 /* This tracks the FD devpoll offset+state */
180 devpoll_state = (struct _devpoll_state *)xcalloc(
181 SQUID_MAXFD, sizeof(struct _devpoll_state)
182 );
183
184 /* And this is the stuff we use to read events */
185 do_poll.dp_fds = (struct pollfd *)xcalloc(
186 DEVPOLL_QUERYSIZE, sizeof(struct pollfd)
187 );
188 dpoll_nfds = DEVPOLL_QUERYSIZE;
189
190 devpoll_update.pfds = (struct pollfd *)xcalloc(
191 DEVPOLL_UPDATESIZE, sizeof(struct pollfd)
192 );
193 devpoll_update.cur = -1;
194 devpoll_update.size = DEVPOLL_UPDATESIZE;
195
196 /* attempt to open /dev/poll device */
197 devpoll_fd = open("/dev/poll", O_RDWR);
b69e9ffa
AJ
198 if (devpoll_fd < 0) {
199 int xerrno = errno;
200 fatalf("comm_select_init: can't open /dev/poll: %s\n", xstrerr(xerrno));
201 }
a1ad2f9b
AJ
202
203 fd_open(devpoll_fd, FD_UNKNOWN, "devpoll ctl");
204
205 commDevPollRegisterWithCacheManager();
206}
207
208/** \brief Set polling state of file descriptor and callback functions
209 *
210 * Sets requested polling state for given file handle along with
211 * desired callback function in the event the request event triggers.
212 *
213 * Note that setting a polling state with a NULL callback function will
214 * clear the polling for that event on that file descriptor.
215 *
216 * @param fd file descriptor to change
217 * @param type may be COMM_SELECT_READ (input) or COMM_SELECT_WRITE (output)
218 * @param handler callback function, or NULL to stop type of polling
219 * @param client_data pointer to be provided to call back function
220 * @param timeout if non-zero then timeout relative to now
221 */
222void
d841c88d 223Comm::SetSelect(int fd, unsigned int type, PF * handler, void *client_data, time_t timeout)
a1ad2f9b
AJ
224{
225 assert(fd >= 0);
bf95c10a 226 debugs(5, 5, "FD " << fd << ", type=" << type <<
48e7baac
AJ
227 ", handler=" << handler << ", client_data=" << client_data <<
228 ", timeout=" << timeout);
a1ad2f9b
AJ
229
230 /* POLLIN/POLLOUT are defined in <sys/poll.h> */
231 fde *F = &fd_table[fd];
232 if (!F->flags.open) {
233 /* remove from poll set */
234 comm_update_fd( fd, POLLREMOVE );
235 devpoll_state[fd].state = 0;
236 return;
237 }
238
239 pollfd_events_t state_old = devpoll_state[fd].state;
240 pollfd_events_t state_new = 0; /* new state (derive from old state) */
241
242 if ( type & COMM_SELECT_READ ) {
243 if ( handler != NULL ) {
ef0416ec
AR
244 // Hack to keep the events flowing if there is data immediately ready
245 if (F->flags.read_pending)
246 state_new |= POLLOUT;
a1ad2f9b
AJ
247 /* we want to POLLIN */
248 state_new |= POLLIN;
249 } else {
250 ; /* we want to clear POLLIN because handler is NULL */
251 }
252
253 F->read_handler = handler;
254 F->read_data = client_data;
255 } else if ( state_old & POLLIN ) {
256 /* we're not changing reading state so take from existing */
257 state_new |= POLLIN;
258 }
259
260 if ( type & COMM_SELECT_WRITE ) {
261 if ( handler != NULL ) {
262 /* we want to POLLOUT */
263 state_new |= POLLOUT;
264 } else {
265 ; /* we want to clear POLLOUT because handler is NULL */
266 }
267
268 F->write_handler = handler;
269 F->write_data = client_data;
270 } else if ( state_old & POLLOUT ) {
271 /* we're not changing writing state so take from existing */
272 state_new |= POLLOUT;
273 }
274
275 if ( pollfd_events_t bits_changed = (state_old ^ state_new) ) {
276 /* something has changed, update /dev/poll of what to listen for */
277
278 /* did any bits clear? (in which case a poll remove is necessary) */
279 if ( bits_changed & state_old ) {
280 comm_update_fd( fd, POLLREMOVE );
281 /* existing state cleared, so update with all required events */
282 if ( state_new )
283 comm_update_fd( fd, state_new );
284 } else {
285 /* only update with new required event */
286 if ( pollfd_events_t newly_set_only = (bits_changed & state_new) )
287 comm_update_fd( fd, newly_set_only );
288 }
289
290 devpoll_state[fd].state = state_new;
291 }
292
293 if (timeout)
294 F->timeout = squid_curtime + timeout;
295}
296
a1ad2f9b
AJ
297/** \brief Do poll and trigger callback functions as appropriate
298 *
299 * Check all connections for new connections and input data that is to be
300 * processed. Also check for connections with data queued and whether we can
301 * write it out.
302 *
303 * Called to do the new-style IO, courtesy of of squid (like most of this
304 * new IO code). This routine handles the stuff we've hidden in
305 * comm_setselect and fd_table[] and calls callbacks for IO ready
306 * events.
307 *
308 * @param msec milliseconds to poll for (limited by max_poll_time)
309 */
c8407295 310Comm::Flag
d841c88d 311Comm::DoSelect(int msec)
a1ad2f9b
AJ
312{
313 int num, i;
314 fde *F;
315 PF *hdl;
316
a1ad2f9b
AJ
317 if (msec > max_poll_time)
318 msec = max_poll_time;
319
320 for (;;) {
321 do_poll.dp_timeout = msec;
322 do_poll.dp_nfds = dpoll_nfds;
323
324 comm_flush_updates(); /* ensure latest changes are sent to /dev/poll */
325
326 num = ioctl(devpoll_fd, DP_POLL, &do_poll);
098346fd 327 ++ statCounter.select_loops;
a1ad2f9b
AJ
328
329 if (num >= 0)
330 break; /* no error, skip out of loop */
331
332 if (ignoreErrno(errno))
333 break; /* error is one we may ignore, skip out of loop */
334
335 /* error during poll */
336 getCurrentTime();
4ee57cbe 337 return Comm::COMM_ERROR;
a1ad2f9b
AJ
338 }
339
a1ad2f9b
AJ
340 getCurrentTime();
341
f30f7998 342 statCounter.select_fds_hist.count(num);
a1ad2f9b
AJ
343
344 if (num == 0)
c8407295 345 return Comm::TIMEOUT; /* no error */
a1ad2f9b 346
cbebe602 347 for (i = 0; i < num; ++i) {
a1ad2f9b
AJ
348 int fd = (int)do_poll.dp_fds[i].fd;
349 F = &fd_table[fd];
350 debugs(
351 5,
352 DEBUG_DEVPOLL ? 0 : 8,
bf95c10a 353 "got FD " << fd
a1ad2f9b
AJ
354 << ",events=" << std::hex << do_poll.dp_fds[i].revents
355 << ",monitoring=" << devpoll_state[fd].state
356 << ",F->read_handler=" << F->read_handler
357 << ",F->write_handler=" << F->write_handler
358 );
359
360 /* handle errors */
361 if (do_poll.dp_fds[i].revents & (POLLERR | POLLHUP | POLLNVAL)) {
d816f28d 362 debugs(5, DEBUG_DEVPOLL ? 0 : 8,
bf95c10a 363 "ERROR: devpoll event failure: fd " << fd
364 );
a1ad2f9b
AJ
365 continue;
366 }
367
368 /* check if file descriptor has data to read */
369 if (do_poll.dp_fds[i].revents & POLLIN || F->flags.read_pending) {
370 if ( (hdl = F->read_handler) != NULL ) {
371 debugs(
372 5,
373 DEBUG_DEVPOLL ? 0 : 8,
bf95c10a 374 "Calling read handler on FD " << fd
a1ad2f9b 375 );
a1ad2f9b
AJ
376 F->read_handler = NULL;
377 hdl(fd, F->read_data);
098346fd 378 ++ statCounter.select_fds;
a1ad2f9b
AJ
379 } else {
380 debugs(
381 5,
382 DEBUG_DEVPOLL ? 0 : 8,
bf95c10a 383 "no read handler for FD " << fd
a1ad2f9b
AJ
384 );
385 // remove interest since no handler exist for this event.
d841c88d 386 SetSelect(fd, COMM_SELECT_READ, NULL, NULL, 0);
a1ad2f9b
AJ
387 }
388 }
389
390 /* check if file descriptor is ready to write */
391 if (do_poll.dp_fds[i].revents & POLLOUT) {
392 if ((hdl = F->write_handler) != NULL) {
393 debugs(
394 5,
395 DEBUG_DEVPOLL ? 0 : 8,
bf95c10a 396 "Calling write handler on FD " << fd
a1ad2f9b 397 );
a1ad2f9b
AJ
398 F->write_handler = NULL;
399 hdl(fd, F->write_data);
098346fd 400 ++ statCounter.select_fds;
a1ad2f9b
AJ
401 } else {
402 debugs(
403 5,
404 DEBUG_DEVPOLL ? 0 : 8,
bf95c10a 405 "no write handler for FD " << fd
a1ad2f9b
AJ
406 );
407 // remove interest since no handler exist for this event.
d841c88d 408 SetSelect(fd, COMM_SELECT_WRITE, NULL, NULL, 0);
a1ad2f9b
AJ
409 }
410 }
411 }
412
c8407295 413 return Comm::OK;
a1ad2f9b
AJ
414}
415
a1ad2f9b 416void
d841c88d 417Comm::QuickPollRequired(void)
a1ad2f9b
AJ
418{
419 max_poll_time = 10;
420}
421
422#endif /* USE_DEVPOLL */
f53969cc 423