]>
Commit | Line | Data |
---|---|---|
6039b729 | 1 | /* |
4ac4a490 | 2 | * Copyright (C) 1996-2017 The Squid Software Foundation and contributors |
6039b729 | 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. | |
6039b729 | 7 | */ |
8 | ||
bbc27441 AJ |
9 | /* DEBUG: section 05 Socket Functions */ |
10 | ||
6039b729 | 11 | /* |
12 | * The idea for this came from these two websites: | |
13 | * http://www.xmailserver.org/linux-patches/nio-improve.html | |
14 | * http://www.kegel.com/c10k.html | |
15 | * | |
16 | * This is to support the epoll sysctl being added to the linux 2.5 | |
17 | * kernel tree. The new sys_epoll is an event based poller without | |
18 | * most of the fuss of rtsignals. | |
19 | * | |
20 | * -- David Nicklay <dnicklay@web.turner.com> | |
21 | */ | |
22 | ||
23 | /* | |
24 | * XXX Currently not implemented / supported by this module XXX | |
25 | * | |
26 | * - delay pools | |
27 | * - deferred reads | |
28 | * | |
29 | */ | |
30 | ||
f7f3304a | 31 | #include "squid.h" |
d841c88d AJ |
32 | |
33 | #if USE_EPOLL | |
34 | ||
d841c88d | 35 | #include "comm/Loops.h" |
1287b9e1 | 36 | #include "fde.h" |
582c2af2 | 37 | #include "globals.h" |
d841c88d | 38 | #include "mgr/Registration.h" |
582c2af2 | 39 | #include "profiler/Profiler.h" |
348697ca | 40 | #include "SquidTime.h" |
e1656dc4 | 41 | #include "StatCounters.h" |
00a7574e | 42 | #include "StatHist.h" |
d841c88d | 43 | #include "Store.h" |
8a02a7f8 | 44 | |
6039b729 | 45 | #define DEBUG_EPOLL 0 |
46 | ||
1a30fdf5 | 47 | #include <cerrno> |
ad32c661 | 48 | #if HAVE_SYS_EPOLL_H |
6039b729 | 49 | #include <sys/epoll.h> |
ad32c661 | 50 | #endif |
6039b729 | 51 | |
612fc474 | 52 | static int kdpfd = -1; |
6039b729 | 53 | static int max_poll_time = 1000; |
54 | ||
55 | static struct epoll_event *pevents; | |
56 | ||
5acc9f37 | 57 | static void commEPollRegisterWithCacheManager(void); |
6039b729 | 58 | |
6039b729 | 59 | /* XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX */ |
60 | /* Public functions */ | |
61 | ||
6039b729 | 62 | /* |
6039b729 | 63 | * This is a needed exported function which will be called to initialise |
64 | * the network loop code. | |
65 | */ | |
66 | void | |
d841c88d | 67 | Comm::SelectLoopInit(void) |
6039b729 | 68 | { |
6039b729 | 69 | pevents = (struct epoll_event *) xmalloc(SQUID_MAXFD * sizeof(struct epoll_event)); |
70 | ||
71 | if (!pevents) { | |
b69e9ffa AJ |
72 | int xerrno = errno; |
73 | fatalf("comm_select_init: xmalloc() failed: %s\n", xstrerr(xerrno)); | |
6039b729 | 74 | } |
75 | ||
76 | kdpfd = epoll_create(SQUID_MAXFD); | |
77 | ||
78 | if (kdpfd < 0) { | |
b69e9ffa AJ |
79 | int xerrno = errno; |
80 | fatalf("comm_select_init: epoll_create(): %s\n", xstrerr(xerrno)); | |
6039b729 | 81 | } |
8cb183ec FC |
82 | |
83 | commEPollRegisterWithCacheManager(); | |
6039b729 | 84 | } |
85 | ||
751406fe | 86 | static const char* epolltype_atoi(int x) |
87 | { | |
26ac0430 | 88 | switch (x) { |
751406fe | 89 | |
90 | case EPOLL_CTL_ADD: | |
91 | return "EPOLL_CTL_ADD"; | |
92 | ||
93 | case EPOLL_CTL_DEL: | |
94 | return "EPOLL_CTL_DEL"; | |
95 | ||
96 | case EPOLL_CTL_MOD: | |
97 | return "EPOLL_CTL_MOD"; | |
98 | ||
99 | default: | |
100 | return "UNKNOWN_EPOLLCTL_OP"; | |
101 | } | |
102 | } | |
103 | ||
d841c88d | 104 | /** |
6039b729 | 105 | * This is a needed exported function which will be called to register |
106 | * and deregister interest in a pending IO state for a given FD. | |
6039b729 | 107 | */ |
108 | void | |
d841c88d | 109 | Comm::SetSelect(int fd, unsigned int type, PF * handler, void *client_data, time_t timeout) |
6039b729 | 110 | { |
111 | fde *F = &fd_table[fd]; | |
751406fe | 112 | int epoll_ctl_type = 0; |
6039b729 | 113 | |
6039b729 | 114 | assert(fd >= 0); |
48e7baac | 115 | debugs(5, 5, HERE << "FD " << fd << ", type=" << type << |
d841c88d AJ |
116 | ", handler=" << handler << ", client_data=" << client_data << |
117 | ", timeout=" << timeout); | |
6039b729 | 118 | |
612fc474 AJ |
119 | struct epoll_event ev; |
120 | memset(&ev, 0, sizeof(ev)); | |
751406fe | 121 | ev.data.fd = fd; |
6039b729 | 122 | |
a1727eb7 | 123 | if (!F->flags.open) { |
124 | epoll_ctl(kdpfd, EPOLL_CTL_DEL, fd, &ev); | |
125 | return; | |
126 | } | |
127 | ||
751406fe | 128 | // If read is an interest |
6039b729 | 129 | |
130 | if (type & COMM_SELECT_READ) { | |
0a080ce3 | 131 | if (handler) { |
26ac0430 AJ |
132 | // Hack to keep the events flowing if there is data immediately ready |
133 | if (F->flags.read_pending) | |
134 | ev.events |= EPOLLOUT; | |
751406fe | 135 | ev.events |= EPOLLIN; |
26ac0430 | 136 | } |
6039b729 | 137 | |
138 | F->read_handler = handler; | |
139 | ||
140 | F->read_data = client_data; | |
751406fe | 141 | |
142 | // Otherwise, use previously stored value | |
143 | } else if (F->epoll_state & EPOLLIN) { | |
144 | ev.events |= EPOLLIN; | |
6039b729 | 145 | } |
146 | ||
751406fe | 147 | // If write is an interest |
6039b729 | 148 | if (type & COMM_SELECT_WRITE) { |
751406fe | 149 | if (handler) |
150 | ev.events |= EPOLLOUT; | |
6039b729 | 151 | |
152 | F->write_handler = handler; | |
153 | ||
154 | F->write_data = client_data; | |
6039b729 | 155 | |
751406fe | 156 | // Otherwise, use previously stored value |
157 | } else if (F->epoll_state & EPOLLOUT) { | |
158 | ev.events |= EPOLLOUT; | |
159 | } | |
6039b729 | 160 | |
751406fe | 161 | if (ev.events) |
162 | ev.events |= EPOLLHUP | EPOLLERR; | |
6039b729 | 163 | |
751406fe | 164 | if (ev.events != F->epoll_state) { |
165 | if (F->epoll_state) // already monitoring something. | |
166 | epoll_ctl_type = ev.events ? EPOLL_CTL_MOD : EPOLL_CTL_DEL; | |
167 | else | |
168 | epoll_ctl_type = EPOLL_CTL_ADD; | |
6039b729 | 169 | |
751406fe | 170 | F->epoll_state = ev.events; |
6039b729 | 171 | |
751406fe | 172 | if (epoll_ctl(kdpfd, epoll_ctl_type, fd, &ev) < 0) { |
b69e9ffa AJ |
173 | int xerrno = errno; |
174 | debugs(5, DEBUG_EPOLL ? 0 : 8, "epoll_ctl(," << epolltype_atoi(epoll_ctl_type) << | |
175 | ",,): failed on FD " << fd << ": " << xstrerr(xerrno)); | |
6039b729 | 176 | } |
177 | } | |
178 | ||
179 | if (timeout) | |
180 | F->timeout = squid_curtime + timeout; | |
181 | } | |
182 | ||
3a5a4930 | 183 | void |
d841c88d | 184 | Comm::ResetSelect(int fd) |
3a5a4930 | 185 | { |
186 | fde *F = &fd_table[fd]; | |
187 | F->epoll_state = 0; | |
d841c88d | 188 | SetSelect(fd, 0, NULL, NULL, 0); |
3a5a4930 | 189 | } |
190 | ||
610ee341 | 191 | static void commIncomingStats(StoreEntry * sentry); |
192 | ||
5acc9f37 | 193 | static void |
dbeed9ea | 194 | commEPollRegisterWithCacheManager(void) |
610ee341 | 195 | { |
8822ebee | 196 | Mgr::RegisterAction("comm_epoll_incoming", |
d9fc6862 A |
197 | "comm_incoming() stats", |
198 | commIncomingStats, 0, 1); | |
610ee341 | 199 | } |
200 | ||
201 | static void | |
202 | commIncomingStats(StoreEntry * sentry) | |
203 | { | |
204 | StatCounters *f = &statCounter; | |
0a515876 | 205 | storeAppendPrintf(sentry, "Total number of epoll(2) loops: %ld\n", statCounter.select_loops); |
610ee341 | 206 | storeAppendPrintf(sentry, "Histogram of returned filedescriptors\n"); |
96886986 | 207 | f->select_fds_hist.dump(sentry, statHistIntDumper); |
610ee341 | 208 | } |
209 | ||
d841c88d | 210 | /** |
6039b729 | 211 | * Check all connections for new connections and input data that is to be |
212 | * processed. Also check for connections with data queued and whether we can | |
213 | * write it out. | |
6039b729 | 214 | * |
215 | * Called to do the new-style IO, courtesy of of squid (like most of this | |
216 | * new IO code). This routine handles the stuff we've hidden in | |
217 | * comm_setselect and fd_table[] and calls callbacks for IO ready | |
218 | * events. | |
219 | */ | |
c8407295 | 220 | Comm::Flag |
d841c88d | 221 | Comm::DoSelect(int msec) |
6039b729 | 222 | { |
223 | int num, i,fd; | |
224 | fde *F; | |
225 | PF *hdl; | |
226 | ||
227 | struct epoll_event *cevents; | |
6039b729 | 228 | |
a1727eb7 | 229 | PROF_start(comm_check_incoming); |
230 | ||
6039b729 | 231 | if (msec > max_poll_time) |
232 | msec = max_poll_time; | |
233 | ||
234 | for (;;) { | |
235 | num = epoll_wait(kdpfd, pevents, SQUID_MAXFD, msec); | |
098346fd | 236 | ++ statCounter.select_loops; |
6039b729 | 237 | |
238 | if (num >= 0) | |
239 | break; | |
240 | ||
241 | if (ignoreErrno(errno)) | |
242 | break; | |
243 | ||
244 | getCurrentTime(); | |
245 | ||
a1727eb7 | 246 | PROF_stop(comm_check_incoming); |
247 | ||
4ee57cbe | 248 | return Comm::COMM_ERROR; |
6039b729 | 249 | } |
250 | ||
a1727eb7 | 251 | PROF_stop(comm_check_incoming); |
6039b729 | 252 | getCurrentTime(); |
253 | ||
f30f7998 | 254 | statCounter.select_fds_hist.count(num); |
751406fe | 255 | |
6039b729 | 256 | if (num == 0) |
f53969cc | 257 | return Comm::TIMEOUT; /* No error.. */ |
751406fe | 258 | |
259 | PROF_start(comm_handle_ready_fd); | |
6039b729 | 260 | |
cbebe602 | 261 | for (i = 0, cevents = pevents; i < num; ++i, ++cevents) { |
6039b729 | 262 | fd = cevents->data.fd; |
263 | F = &fd_table[fd]; | |
d841c88d | 264 | debugs(5, DEBUG_EPOLL ? 0 : 8, HERE << "got FD " << fd << " events=" << |
26ac0430 AJ |
265 | std::hex << cevents->events << " monitoring=" << F->epoll_state << |
266 | " F->read_handler=" << F->read_handler << " F->write_handler=" << F->write_handler); | |
751406fe | 267 | |
268 | // TODO: add EPOLLPRI?? | |
6039b729 | 269 | |
0a080ce3 | 270 | if (cevents->events & (EPOLLIN|EPOLLHUP|EPOLLERR) || F->flags.read_pending) { |
a1727eb7 | 271 | if ((hdl = F->read_handler) != NULL) { |
d841c88d | 272 | debugs(5, DEBUG_EPOLL ? 0 : 8, HERE << "Calling read handler on FD " << fd); |
751406fe | 273 | PROF_start(comm_write_handler); |
0a080ce3 | 274 | F->flags.read_pending = 0; |
6039b729 | 275 | F->read_handler = NULL; |
276 | hdl(fd, F->read_data); | |
751406fe | 277 | PROF_stop(comm_write_handler); |
098346fd | 278 | ++ statCounter.select_fds; |
751406fe | 279 | } else { |
d841c88d | 280 | debugs(5, DEBUG_EPOLL ? 0 : 8, HERE << "no read handler for FD " << fd); |
751406fe | 281 | // remove interest since no handler exist for this event. |
d841c88d | 282 | SetSelect(fd, COMM_SELECT_READ, NULL, NULL, 0); |
6039b729 | 283 | } |
284 | } | |
285 | ||
751406fe | 286 | if (cevents->events & (EPOLLOUT|EPOLLHUP|EPOLLERR)) { |
a1727eb7 | 287 | if ((hdl = F->write_handler) != NULL) { |
d841c88d | 288 | debugs(5, DEBUG_EPOLL ? 0 : 8, HERE << "Calling write handler on FD " << fd); |
751406fe | 289 | PROF_start(comm_read_handler); |
6039b729 | 290 | F->write_handler = NULL; |
291 | hdl(fd, F->write_data); | |
751406fe | 292 | PROF_stop(comm_read_handler); |
098346fd | 293 | ++ statCounter.select_fds; |
751406fe | 294 | } else { |
d841c88d | 295 | debugs(5, DEBUG_EPOLL ? 0 : 8, HERE << "no write handler for FD " << fd); |
751406fe | 296 | // remove interest since no handler exist for this event. |
d841c88d | 297 | SetSelect(fd, COMM_SELECT_WRITE, NULL, NULL, 0); |
6039b729 | 298 | } |
299 | } | |
300 | } | |
301 | ||
751406fe | 302 | PROF_stop(comm_handle_ready_fd); |
303 | ||
c8407295 | 304 | return Comm::OK; |
6039b729 | 305 | } |
306 | ||
307 | void | |
d841c88d | 308 | Comm::QuickPollRequired(void) |
6039b729 | 309 | { |
768d3d2f | 310 | max_poll_time = 10; |
6039b729 | 311 | } |
312 | ||
8a02a7f8 | 313 | #endif /* USE_EPOLL */ |
f53969cc | 314 |