]>
Commit | Line | Data |
---|---|---|
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 */ | |
56 | typedef short pollfd_events_t; /* type of pollfd.events from sys/poll.h */ | |
57 | ||
58 | /* STRUCTURES */ | |
59 | /** \brief Current state */ | |
60 | struct _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 | 72 | static 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 */ |
79 | static int devpoll_fd; /**< handle to /dev/poll device */ | |
80 | static int max_poll_time = 1000; /**< maximum milliseconds to spend in poll */ | |
81 | ||
82 | static struct _devpoll_state *devpoll_state; /**< array of socket states */ | |
83 | static struct dvpoll do_poll; /**< data struct for storing poll results */ | |
84 | static int dpoll_nfds; /**< maximum number of poll results */ | |
85 | ||
86 | /* PROTOTYPES */ | |
87 | static 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 | */ | |
98 | static void | |
99 | comm_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 | */ | |
130 | static void | |
131 | comm_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 |
150 | static 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 |
157 | static void |
158 | commDevPollRegisterWithCacheManager(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 | */ | |
175 | void | |
d841c88d | 176 | Comm::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 | */ | |
222 | void | |
d841c88d | 223 | Comm::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 | 310 | Comm::Flag |
d841c88d | 311 | Comm::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 | 416 | void |
d841c88d | 417 | Comm::QuickPollRequired(void) |
a1ad2f9b AJ |
418 | { |
419 | max_poll_time = 10; | |
420 | } | |
421 | ||
422 | #endif /* USE_DEVPOLL */ | |
f53969cc | 423 |