]> git.ipfire.org Git - thirdparty/squid.git/blob - src/comm/ModDevPoll.cc
SourceFormat Enforcement
[thirdparty/squid.git] / src / comm / ModDevPoll.cc
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 */
61 /** \brief Current state */
62 struct _devpoll_state {
63 pollfd_events_t state; /**< current known state of file handle */
64 };
65
66 /** \brief Update list
67 *
68 * This structure contains an array of settings to send to the /dev/poll
69 * interface. Rather than send changes to /dev/poll one at a time they
70 * are pushed onto this array (updating cur to indicate how many of the
71 * pfds structure elements have been set) until it is full before it
72 * is written out the API.
73 */
74 static struct {
75 struct pollfd *pfds; /**< ptr to array of struct pollfd config elements */
76 int cur; /**< index of last written element of array, or -1 if none */
77 int size; /**< maximum number of elements in array */
78 } devpoll_update;
79
80 /* STATIC VARIABLES */
81 static int devpoll_fd; /**< handle to /dev/poll device */
82 static int max_poll_time = 1000; /**< maximum milliseconds to spend in poll */
83
84 static struct _devpoll_state *devpoll_state; /**< array of socket states */
85 static struct dvpoll do_poll; /**< data struct for storing poll results */
86 static int dpoll_nfds; /**< maximum number of poll results */
87
88 /* PROTOTYPES */
89 static void commDevPollRegisterWithCacheManager(void);
90
91 /* PRIVATE FUNCTIONS */
92 /** \brief Write batched file descriptor event changes to poll device
93 *
94 * Writes out the static array of file descriptor event changes to the
95 * poll device. This is done only when necessary (i.e. just before
96 * the poll device is queried during the select call, and whenever
97 * the number of changes to store in the array exceeds the size of the
98 * array).
99 */
100 static void
101 comm_flush_updates(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
123 /** \brief Register change in desired polling state for file descriptor
124 *
125 * Prevents unnecessary calls to the /dev/poll API by queueing changes
126 * in the devpoll_update array. If the array fills up the comm_flush_updates
127 * function is called.
128 *
129 * @param fd file descriptor to register change with
130 * @param events events to register (usually POLLIN, POLLOUT, or POLLREMOVE)
131 */
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))
143 comm_flush_updates();
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");
156 statCounter.select_fds_hist.dump(sentry, statHistIntDumper);
157 }
158
159 static void
160 commDevPollRegisterWithCacheManager(void)
161 {
162 Mgr::RegisterAction(
163 "comm_devpoll_incoming",
164 "comm_incoming() stats",
165 commIncomingStats,
166 0,
167 1
168 );
169 }
170
171 /* PUBLIC FUNCTIONS */
172
173 /** \brief Initialise /dev/poll support
174 *
175 * Allocates memory, opens /dev/poll device handle.
176 */
177 void
178 Comm::SelectLoopInit(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 );
190 dpoll_nfds = DEVPOLL_QUERYSIZE;
191
192 devpoll_update.pfds = (struct pollfd *)xcalloc(
193 DEVPOLL_UPDATESIZE, sizeof(struct pollfd)
194 );
195 devpoll_update.cur = -1;
196 devpoll_update.size = DEVPOLL_UPDATESIZE;
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
207 commDevPollRegisterWithCacheManager();
208 }
209
210 /** \brief Set polling state of file descriptor and callback functions
211 *
212 * Sets requested polling state for given file handle along with
213 * desired callback function in the event the request event triggers.
214 *
215 * Note that setting a polling state with a NULL callback function will
216 * clear the polling for that event on that file descriptor.
217 *
218 * @param fd file descriptor to change
219 * @param type may be COMM_SELECT_READ (input) or COMM_SELECT_WRITE (output)
220 * @param handler callback function, or NULL to stop type of polling
221 * @param client_data pointer to be provided to call back function
222 * @param timeout if non-zero then timeout relative to now
223 */
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
299 /** \brief Clear polling of file handle (both read and write)
300 *
301 * @param fd file descriptor to clear polling on
302 */
303 void
304 Comm::ResetSelect(int fd)
305 {
306 SetSelect(fd, COMM_SELECT_WRITE, NULL, NULL, 0);
307 SetSelect(fd, COMM_SELECT_READ, NULL, NULL, 0);
308 }
309
310 /** \brief Do poll and trigger callback functions as appropriate
311 *
312 * Check all connections for new connections and input data that is to be
313 * processed. Also check for connections with data queued and whether we can
314 * write it out.
315 *
316 * Called to do the new-style IO, courtesy of of squid (like most of this
317 * new IO code). This routine handles the stuff we've hidden in
318 * comm_setselect and fd_table[] and calls callbacks for IO ready
319 * events.
320 *
321 * @param msec milliseconds to poll for (limited by max_poll_time)
322 */
323 Comm::Flag
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);
342 ++ statCounter.select_loops;
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
359 statCounter.select_fds_hist.count(num);
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);
402 ++ statCounter.select_fds;
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.
410 SetSelect(fd, COMM_SELECT_READ, NULL, NULL, 0);
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);
426 ++ statCounter.select_fds;
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.
434 SetSelect(fd, COMM_SELECT_WRITE, NULL, NULL, 0);
435 }
436 }
437 }
438
439 PROF_stop(comm_handle_ready_fd);
440 return Comm::OK;
441 }
442
443 void
444 Comm::QuickPollRequired(void)
445 {
446 max_poll_time = 10;
447 }
448
449 #endif /* USE_DEVPOLL */
450