]> git.ipfire.org Git - thirdparty/squid.git/blob - src/comm/ModEpoll.cc
3e53cce0a00c5e2d887bec336562ab65d506e676
[thirdparty/squid.git] / src / comm / ModEpoll.cc
1 /*
2 * $Id$
3 *
4 * DEBUG: section 05 Socket Functions
5 *
6 * SQUID Web Proxy Cache http://www.squid-cache.org/
7 * ----------------------------------------------------------
8 *
9 * Squid is the result of efforts by numerous individuals from
10 * the Internet community; see the CONTRIBUTORS file for full
11 * details. Many organizations have provided support for Squid's
12 * development; see the SPONSORS file for full details. Squid is
13 * Copyrighted (C) 2001 by the Regents of the University of
14 * California; see the COPYRIGHT file for full details. Squid
15 * incorporates software developed and/or copyrighted by other
16 * sources; see the CREDITS file for full details.
17 *
18 * This program is free software; you can redistribute it and/or modify
19 * it under the terms of the GNU General Public License as published by
20 * the Free Software Foundation; either version 2 of the License, or
21 * (at your option) any later version.
22 *
23 * This program is distributed in the hope that it will be useful,
24 * but WITHOUT ANY WARRANTY; without even the implied warranty of
25 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
26 * GNU General Public License for more details.
27 *
28 * You should have received a copy of the GNU General Public License
29 * along with this program; if not, write to the Free Software
30 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
31 *
32 */
33
34 /*
35 * The idea for this came from these two websites:
36 * http://www.xmailserver.org/linux-patches/nio-improve.html
37 * http://www.kegel.com/c10k.html
38 *
39 * This is to support the epoll sysctl being added to the linux 2.5
40 * kernel tree. The new sys_epoll is an event based poller without
41 * most of the fuss of rtsignals.
42 *
43 * -- David Nicklay <dnicklay@web.turner.com>
44 */
45
46 /*
47 * XXX Currently not implemented / supported by this module XXX
48 *
49 * - delay pools
50 * - deferred reads
51 *
52 */
53
54 #include "config.h"
55
56 #if USE_EPOLL
57
58 #include "squid.h"
59 #include "comm/Loops.h"
60 #include "fde.h"
61 #include "mgr/Registration.h"
62 #include "SquidTime.h"
63 #include "StatCounters.h"
64 #include "StatHist.h"
65 #include "Store.h"
66
67 #define DEBUG_EPOLL 0
68
69 #if HAVE_SYS_EPOLL_H
70 #include <sys/epoll.h>
71 #endif
72
73 static int kdpfd;
74 static int max_poll_time = 1000;
75
76 static struct epoll_event *pevents;
77
78 static void commEPollRegisterWithCacheManager(void);
79
80
81 /* XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX */
82 /* Public functions */
83
84
85 /*
86 * This is a needed exported function which will be called to initialise
87 * the network loop code.
88 */
89 void
90 Comm::SelectLoopInit(void)
91 {
92 pevents = (struct epoll_event *) xmalloc(SQUID_MAXFD * sizeof(struct epoll_event));
93
94 if (!pevents) {
95 fatalf("comm_select_init: xmalloc() failed: %s\n",xstrerror());
96 }
97
98 kdpfd = epoll_create(SQUID_MAXFD);
99
100 if (kdpfd < 0) {
101 fatalf("comm_select_init: epoll_create(): %s\n",xstrerror());
102 }
103
104 commEPollRegisterWithCacheManager();
105 }
106
107 static const char* epolltype_atoi(int x)
108 {
109 switch (x) {
110
111 case EPOLL_CTL_ADD:
112 return "EPOLL_CTL_ADD";
113
114 case EPOLL_CTL_DEL:
115 return "EPOLL_CTL_DEL";
116
117 case EPOLL_CTL_MOD:
118 return "EPOLL_CTL_MOD";
119
120 default:
121 return "UNKNOWN_EPOLLCTL_OP";
122 }
123 }
124
125 /**
126 * This is a needed exported function which will be called to register
127 * and deregister interest in a pending IO state for a given FD.
128 */
129 void
130 Comm::SetSelect(int fd, unsigned int type, PF * handler, void *client_data, time_t timeout)
131 {
132 fde *F = &fd_table[fd];
133 int epoll_ctl_type = 0;
134
135 struct epoll_event ev;
136 assert(fd >= 0);
137 debugs(5, 5, HERE << "FD " << fd << ", type=" << type <<
138 ", handler=" << handler << ", client_data=" << client_data <<
139 ", timeout=" << timeout);
140
141 if (RUNNING_ON_VALGRIND) {
142 /* Keep valgrind happy.. complains about uninitialized bytes otherwise */
143 memset(&ev, 0, sizeof(ev));
144 }
145 ev.events = 0;
146 ev.data.fd = fd;
147
148 if (!F->flags.open) {
149 epoll_ctl(kdpfd, EPOLL_CTL_DEL, fd, &ev);
150 return;
151 }
152
153 // If read is an interest
154
155 if (type & COMM_SELECT_READ) {
156 if (handler) {
157 // Hack to keep the events flowing if there is data immediately ready
158 if (F->flags.read_pending)
159 ev.events |= EPOLLOUT;
160 ev.events |= EPOLLIN;
161 }
162
163 F->read_handler = handler;
164
165 F->read_data = client_data;
166
167 // Otherwise, use previously stored value
168 } else if (F->epoll_state & EPOLLIN) {
169 ev.events |= EPOLLIN;
170 }
171
172 // If write is an interest
173 if (type & COMM_SELECT_WRITE) {
174 if (handler)
175 ev.events |= EPOLLOUT;
176
177 F->write_handler = handler;
178
179 F->write_data = client_data;
180
181 // Otherwise, use previously stored value
182 } else if (F->epoll_state & EPOLLOUT) {
183 ev.events |= EPOLLOUT;
184 }
185
186 if (ev.events)
187 ev.events |= EPOLLHUP | EPOLLERR;
188
189 if (ev.events != F->epoll_state) {
190 if (F->epoll_state) // already monitoring something.
191 epoll_ctl_type = ev.events ? EPOLL_CTL_MOD : EPOLL_CTL_DEL;
192 else
193 epoll_ctl_type = EPOLL_CTL_ADD;
194
195 F->epoll_state = ev.events;
196
197 if (epoll_ctl(kdpfd, epoll_ctl_type, fd, &ev) < 0) {
198 debugs(5, DEBUG_EPOLL ? 0 : 8, HERE << "epoll_ctl(," << epolltype_atoi(epoll_ctl_type) <<
199 ",,): failed on FD " << fd << ": " << xstrerror());
200 }
201 }
202
203 if (timeout)
204 F->timeout = squid_curtime + timeout;
205 }
206
207 void
208 Comm::ResetSelect(int fd)
209 {
210 fde *F = &fd_table[fd];
211 F->epoll_state = 0;
212 SetSelect(fd, 0, NULL, NULL, 0);
213 }
214
215
216 static void commIncomingStats(StoreEntry * sentry);
217
218 static void
219 commEPollRegisterWithCacheManager(void)
220 {
221 Mgr::RegisterAction("comm_epoll_incoming",
222 "comm_incoming() stats",
223 commIncomingStats, 0, 1);
224 }
225
226 static void
227 commIncomingStats(StoreEntry * sentry)
228 {
229 StatCounters *f = &statCounter;
230 storeAppendPrintf(sentry, "Total number of epoll(2) loops: %ld\n", statCounter.select_loops);
231 storeAppendPrintf(sentry, "Histogram of returned filedescriptors\n");
232 f->select_fds_hist.dump(sentry, statHistIntDumper);
233 }
234
235 /**
236 * Check all connections for new connections and input data that is to be
237 * processed. Also check for connections with data queued and whether we can
238 * write it out.
239 *
240 * Called to do the new-style IO, courtesy of of squid (like most of this
241 * new IO code). This routine handles the stuff we've hidden in
242 * comm_setselect and fd_table[] and calls callbacks for IO ready
243 * events.
244 */
245 comm_err_t
246 Comm::DoSelect(int msec)
247 {
248 int num, i,fd;
249 fde *F;
250 PF *hdl;
251
252 struct epoll_event *cevents;
253
254 PROF_start(comm_check_incoming);
255
256 if (msec > max_poll_time)
257 msec = max_poll_time;
258
259 for (;;) {
260 num = epoll_wait(kdpfd, pevents, SQUID_MAXFD, msec);
261 ++statCounter.select_loops;
262
263 if (num >= 0)
264 break;
265
266 if (ignoreErrno(errno))
267 break;
268
269 getCurrentTime();
270
271 PROF_stop(comm_check_incoming);
272
273 return COMM_ERROR;
274 }
275
276 PROF_stop(comm_check_incoming);
277 getCurrentTime();
278
279 statCounter.select_fds_hist.count(num);
280
281 if (num == 0)
282 return COMM_TIMEOUT; /* No error.. */
283
284 PROF_start(comm_handle_ready_fd);
285
286 for (i = 0, cevents = pevents; i < num; i++, cevents++) {
287 fd = cevents->data.fd;
288 F = &fd_table[fd];
289 debugs(5, DEBUG_EPOLL ? 0 : 8, HERE << "got FD " << fd << " events=" <<
290 std::hex << cevents->events << " monitoring=" << F->epoll_state <<
291 " F->read_handler=" << F->read_handler << " F->write_handler=" << F->write_handler);
292
293 // TODO: add EPOLLPRI??
294
295 if (cevents->events & (EPOLLIN|EPOLLHUP|EPOLLERR) || F->flags.read_pending) {
296 if ((hdl = F->read_handler) != NULL) {
297 debugs(5, DEBUG_EPOLL ? 0 : 8, HERE << "Calling read handler on FD " << fd);
298 PROF_start(comm_write_handler);
299 F->flags.read_pending = 0;
300 F->read_handler = NULL;
301 hdl(fd, F->read_data);
302 PROF_stop(comm_write_handler);
303 statCounter.select_fds++;
304 } else {
305 debugs(5, DEBUG_EPOLL ? 0 : 8, HERE << "no read handler for FD " << fd);
306 // remove interest since no handler exist for this event.
307 SetSelect(fd, COMM_SELECT_READ, NULL, NULL, 0);
308 }
309 }
310
311 if (cevents->events & (EPOLLOUT|EPOLLHUP|EPOLLERR)) {
312 if ((hdl = F->write_handler) != NULL) {
313 debugs(5, DEBUG_EPOLL ? 0 : 8, HERE << "Calling write handler on FD " << fd);
314 PROF_start(comm_read_handler);
315 F->write_handler = NULL;
316 hdl(fd, F->write_data);
317 PROF_stop(comm_read_handler);
318 statCounter.select_fds++;
319 } else {
320 debugs(5, DEBUG_EPOLL ? 0 : 8, HERE << "no write handler for FD " << fd);
321 // remove interest since no handler exist for this event.
322 SetSelect(fd, COMM_SELECT_WRITE, NULL, NULL, 0);
323 }
324 }
325 }
326
327 PROF_stop(comm_handle_ready_fd);
328
329 return COMM_OK;
330 }
331
332 void
333 Comm::QuickPollRequired(void)
334 {
335 max_poll_time = 10;
336 }
337
338 #endif /* USE_EPOLL */