]> git.ipfire.org Git - thirdparty/squid.git/blame - src/comm/ModDevPoll.cc
SourceFormat Enforcement
[thirdparty/squid.git] / src / comm / ModDevPoll.cc
CommitLineData
a1ad2f9b 1/*
bbc27441 2 * Copyright (C) 1996-2014 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"
582c2af2 38#include "profiler/Profiler.h"
a1ad2f9b 39#include "SquidTime.h"
e1656dc4 40#include "StatCounters.h"
00a7574e 41#include "StatHist.h"
d841c88d 42#include "Store.h"
a1ad2f9b 43
074d6a40
AJ
44#include <cerrno>
45#include <climits>
a1ad2f9b
AJ
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
074d6a40 53// OPEN_MAX is defined in <climits>
f53969cc
SM
54#define DEVPOLL_UPDATESIZE OPEN_MAX
55#define DEVPOLL_QUERYSIZE OPEN_MAX
a1ad2f9b
AJ
56
57/* TYPEDEFS */
58typedef short pollfd_events_t; /* type of pollfd.events from sys/poll.h */
59
60/* STRUCTURES */
61/** \brief Current state */
62struct _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 */
14302ffa 74static struct {
a1ad2f9b
AJ
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
a1ad2f9b
AJ
80/* STATIC VARIABLES */
81static int devpoll_fd; /**< handle to /dev/poll device */
82static int max_poll_time = 1000; /**< maximum milliseconds to spend in poll */
83
84static struct _devpoll_state *devpoll_state; /**< array of socket states */
85static struct dvpoll do_poll; /**< data struct for storing poll results */
86static int dpoll_nfds; /**< maximum number of poll results */
87
88/* PROTOTYPES */
89static void commDevPollRegisterWithCacheManager(void);
90
a1ad2f9b
AJ
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 */
100static void
101comm_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);
8b37be18 119 assert(static_cast<size_t>(i) == (sizeof(struct pollfd) * (devpoll_update.cur + 1)));
a1ad2f9b
AJ
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 */
132static void
133comm_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 */
098346fd 146 ++ devpoll_update.cur;
a1ad2f9b
AJ
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
a1ad2f9b
AJ
152static void commIncomingStats(StoreEntry *sentry)
153{
a1ad2f9b
AJ
154 storeAppendPrintf(sentry, "Total number of devpoll loops: %ld\n", statCounter.select_loops);
155 storeAppendPrintf(sentry, "Histogram of returned filedescriptors\n");
82146cc8 156 statCounter.select_fds_hist.dump(sentry, statHistIntDumper);
a1ad2f9b
AJ
157}
158
a1ad2f9b
AJ
159static void
160commDevPollRegisterWithCacheManager(void)
161{
8af645e7 162 Mgr::RegisterAction(
a1ad2f9b
AJ
163 "comm_devpoll_incoming",
164 "comm_incoming() stats",
165 commIncomingStats,
166 0,
167 1
168 );
169}
170
a1ad2f9b
AJ
171/* PUBLIC FUNCTIONS */
172
173/** \brief Initialise /dev/poll support
174 *
175 * Allocates memory, opens /dev/poll device handle.
176 */
177void
d841c88d 178Comm::SelectLoopInit(void)
a1ad2f9b
AJ
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 fatalf("comm_select_init: can't open /dev/poll: %s\n", xstrerror());
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);
48e7baac
AJ
226 debugs(5, 5, HERE << "FD " << fd << ", type=" << type <<
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 ) {
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
a1ad2f9b
AJ
294/** \brief Clear polling of file handle (both read and write)
295 *
296 * @param fd file descriptor to clear polling on
297 */
298void
d841c88d 299Comm::ResetSelect(int fd)
a1ad2f9b 300{
d841c88d
AJ
301 SetSelect(fd, COMM_SELECT_WRITE, NULL, NULL, 0);
302 SetSelect(fd, COMM_SELECT_READ, NULL, NULL, 0);
a1ad2f9b
AJ
303}
304
a1ad2f9b
AJ
305/** \brief Do poll and trigger callback functions as appropriate
306 *
307 * Check all connections for new connections and input data that is to be
308 * processed. Also check for connections with data queued and whether we can
309 * write it out.
310 *
311 * Called to do the new-style IO, courtesy of of squid (like most of this
312 * new IO code). This routine handles the stuff we've hidden in
313 * comm_setselect and fd_table[] and calls callbacks for IO ready
314 * events.
315 *
316 * @param msec milliseconds to poll for (limited by max_poll_time)
317 */
c8407295 318Comm::Flag
d841c88d 319Comm::DoSelect(int msec)
a1ad2f9b
AJ
320{
321 int num, i;
322 fde *F;
323 PF *hdl;
324
325 PROF_start(comm_check_incoming);
326
327 if (msec > max_poll_time)
328 msec = max_poll_time;
329
330 for (;;) {
331 do_poll.dp_timeout = msec;
332 do_poll.dp_nfds = dpoll_nfds;
333
334 comm_flush_updates(); /* ensure latest changes are sent to /dev/poll */
335
336 num = ioctl(devpoll_fd, DP_POLL, &do_poll);
098346fd 337 ++ statCounter.select_loops;
a1ad2f9b
AJ
338
339 if (num >= 0)
340 break; /* no error, skip out of loop */
341
342 if (ignoreErrno(errno))
343 break; /* error is one we may ignore, skip out of loop */
344
345 /* error during poll */
346 getCurrentTime();
347 PROF_stop(comm_check_incoming);
4ee57cbe 348 return Comm::COMM_ERROR;
a1ad2f9b
AJ
349 }
350
351 PROF_stop(comm_check_incoming);
352 getCurrentTime();
353
f30f7998 354 statCounter.select_fds_hist.count(num);
a1ad2f9b
AJ
355
356 if (num == 0)
c8407295 357 return Comm::TIMEOUT; /* no error */
a1ad2f9b
AJ
358
359 PROF_start(comm_handle_ready_fd);
360
cbebe602 361 for (i = 0; i < num; ++i) {
a1ad2f9b
AJ
362 int fd = (int)do_poll.dp_fds[i].fd;
363 F = &fd_table[fd];
364 debugs(
365 5,
366 DEBUG_DEVPOLL ? 0 : 8,
367 HERE << "got FD " << fd
368 << ",events=" << std::hex << do_poll.dp_fds[i].revents
369 << ",monitoring=" << devpoll_state[fd].state
370 << ",F->read_handler=" << F->read_handler
371 << ",F->write_handler=" << F->write_handler
372 );
373
374 /* handle errors */
375 if (do_poll.dp_fds[i].revents & (POLLERR | POLLHUP | POLLNVAL)) {
376 debugs(
377 5,
378 DEBUG_DEVPOLL ? 0 : 8,
379 HERE << "devpoll event error: fd " << fd
380 );
381 continue;
382 }
383
384 /* check if file descriptor has data to read */
385 if (do_poll.dp_fds[i].revents & POLLIN || F->flags.read_pending) {
386 if ( (hdl = F->read_handler) != NULL ) {
387 debugs(
388 5,
389 DEBUG_DEVPOLL ? 0 : 8,
390 HERE << "Calling read handler on FD " << fd
391 );
392 PROF_start(comm_read_handler);
393 F->flags.read_pending = 0;
394 F->read_handler = NULL;
395 hdl(fd, F->read_data);
396 PROF_stop(comm_read_handler);
098346fd 397 ++ statCounter.select_fds;
a1ad2f9b
AJ
398 } else {
399 debugs(
400 5,
401 DEBUG_DEVPOLL ? 0 : 8,
402 HERE << "no read handler for FD " << fd
403 );
404 // remove interest since no handler exist for this event.
d841c88d 405 SetSelect(fd, COMM_SELECT_READ, NULL, NULL, 0);
a1ad2f9b
AJ
406 }
407 }
408
409 /* check if file descriptor is ready to write */
410 if (do_poll.dp_fds[i].revents & POLLOUT) {
411 if ((hdl = F->write_handler) != NULL) {
412 debugs(
413 5,
414 DEBUG_DEVPOLL ? 0 : 8,
415 HERE << "Calling write handler on FD " << fd
416 );
417 PROF_start(comm_write_handler);
418 F->write_handler = NULL;
419 hdl(fd, F->write_data);
420 PROF_stop(comm_write_handler);
098346fd 421 ++ statCounter.select_fds;
a1ad2f9b
AJ
422 } else {
423 debugs(
424 5,
425 DEBUG_DEVPOLL ? 0 : 8,
426 HERE << "no write handler for FD " << fd
427 );
428 // remove interest since no handler exist for this event.
d841c88d 429 SetSelect(fd, COMM_SELECT_WRITE, NULL, NULL, 0);
a1ad2f9b
AJ
430 }
431 }
432 }
433
434 PROF_stop(comm_handle_ready_fd);
c8407295 435 return Comm::OK;
a1ad2f9b
AJ
436}
437
a1ad2f9b 438void
d841c88d 439Comm::QuickPollRequired(void)
a1ad2f9b
AJ
440{
441 max_poll_time = 10;
442}
443
444#endif /* USE_DEVPOLL */
f53969cc 445