]>
git.ipfire.org Git - thirdparty/squid.git/blob - src/comm/ModEpoll.cc
2 * Copyright (C) 1996-2020 The Squid Software Foundation and contributors
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.
9 /* DEBUG: section 05 Socket Functions */
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
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.
20 * -- David Nicklay <dnicklay@web.turner.com>
24 * XXX Currently not implemented / supported by this module XXX
35 #include "base/CodeContext.h"
36 #include "comm/Loops.h"
39 #include "mgr/Registration.h"
40 #include "profiler/Profiler.h"
41 #include "SquidTime.h"
42 #include "StatCounters.h"
50 #include <sys/epoll.h>
53 static int kdpfd
= -1;
54 static int max_poll_time
= 1000;
56 static struct epoll_event
*pevents
;
58 static void commEPollRegisterWithCacheManager(void);
60 /* XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX */
61 /* Public functions */
64 * This is a needed exported function which will be called to initialise
65 * the network loop code.
68 Comm::SelectLoopInit(void)
70 pevents
= (struct epoll_event
*) xmalloc(SQUID_MAXFD
* sizeof(struct epoll_event
));
74 fatalf("comm_select_init: xmalloc() failed: %s\n", xstrerr(xerrno
));
77 kdpfd
= epoll_create(SQUID_MAXFD
);
81 fatalf("comm_select_init: epoll_create(): %s\n", xstrerr(xerrno
));
84 commEPollRegisterWithCacheManager();
87 static const char* epolltype_atoi(int x
)
92 return "EPOLL_CTL_ADD";
95 return "EPOLL_CTL_DEL";
98 return "EPOLL_CTL_MOD";
101 return "UNKNOWN_EPOLLCTL_OP";
106 * This is a needed exported function which will be called to register
107 * and deregister interest in a pending IO state for a given FD.
110 Comm::SetSelect(int fd
, unsigned int type
, PF
* handler
, void *client_data
, time_t timeout
)
112 fde
*F
= &fd_table
[fd
];
113 int epoll_ctl_type
= 0;
116 debugs(5, 5, HERE
<< "FD " << fd
<< ", type=" << type
<<
117 ", handler=" << handler
<< ", client_data=" << client_data
<<
118 ", timeout=" << timeout
);
120 struct epoll_event ev
;
121 memset(&ev
, 0, sizeof(ev
));
124 if (!F
->flags
.open
) {
125 epoll_ctl(kdpfd
, EPOLL_CTL_DEL
, fd
, &ev
);
129 // If read is an interest
131 if (type
& COMM_SELECT_READ
) {
133 // Hack to keep the events flowing if there is data immediately ready
134 if (F
->flags
.read_pending
)
135 ev
.events
|= EPOLLOUT
;
136 ev
.events
|= EPOLLIN
;
139 F
->read_handler
= handler
;
141 F
->read_data
= client_data
;
143 // Otherwise, use previously stored value
144 } else if (F
->epoll_state
& EPOLLIN
) {
145 ev
.events
|= EPOLLIN
;
148 // If write is an interest
149 if (type
& COMM_SELECT_WRITE
) {
151 ev
.events
|= EPOLLOUT
;
153 F
->write_handler
= handler
;
155 F
->write_data
= client_data
;
157 // Otherwise, use previously stored value
158 } else if (F
->epoll_state
& EPOLLOUT
) {
159 ev
.events
|= EPOLLOUT
;
163 ev
.events
|= EPOLLHUP
| EPOLLERR
;
165 if (ev
.events
!= F
->epoll_state
) {
166 if (F
->epoll_state
) // already monitoring something.
167 epoll_ctl_type
= ev
.events
? EPOLL_CTL_MOD
: EPOLL_CTL_DEL
;
169 epoll_ctl_type
= EPOLL_CTL_ADD
;
171 F
->epoll_state
= ev
.events
;
173 if (epoll_ctl(kdpfd
, epoll_ctl_type
, fd
, &ev
) < 0) {
175 debugs(5, DEBUG_EPOLL
? 0 : 8, "epoll_ctl(," << epolltype_atoi(epoll_ctl_type
) <<
176 ",,): failed on FD " << fd
<< ": " << xstrerr(xerrno
));
181 F
->timeout
= squid_curtime
+ timeout
;
183 if (timeout
|| handler
) // all non-cleanup requests
184 F
->codeContext
= CodeContext::Current(); // TODO: Avoid clearing if set?
185 else if (!ev
.events
) // full cleanup: no more FD-associated work expected
186 F
->codeContext
= nullptr;
187 // else: direction-specific/timeout cleanup requests preserve F->codeContext
190 static void commIncomingStats(StoreEntry
* sentry
);
193 commEPollRegisterWithCacheManager(void)
195 Mgr::RegisterAction("comm_epoll_incoming",
196 "comm_incoming() stats",
197 commIncomingStats
, 0, 1);
201 commIncomingStats(StoreEntry
* sentry
)
203 StatCounters
*f
= &statCounter
;
204 storeAppendPrintf(sentry
, "Total number of epoll(2) loops: %ld\n", statCounter
.select_loops
);
205 storeAppendPrintf(sentry
, "Histogram of returned filedescriptors\n");
206 f
->select_fds_hist
.dump(sentry
, statHistIntDumper
);
210 * Check all connections for new connections and input data that is to be
211 * processed. Also check for connections with data queued and whether we can
214 * Called to do the new-style IO, courtesy of of squid (like most of this
215 * new IO code). This routine handles the stuff we've hidden in
216 * comm_setselect and fd_table[] and calls callbacks for IO ready
220 Comm::DoSelect(int msec
)
226 struct epoll_event
*cevents
;
228 PROF_start(comm_check_incoming
);
230 if (msec
> max_poll_time
)
231 msec
= max_poll_time
;
234 num
= epoll_wait(kdpfd
, pevents
, SQUID_MAXFD
, msec
);
235 ++ statCounter
.select_loops
;
240 if (ignoreErrno(errno
))
245 PROF_stop(comm_check_incoming
);
247 return Comm::COMM_ERROR
;
250 PROF_stop(comm_check_incoming
);
253 statCounter
.select_fds_hist
.count(num
);
256 return Comm::TIMEOUT
; /* No error.. */
258 PROF_start(comm_handle_ready_fd
);
260 for (i
= 0, cevents
= pevents
; i
< num
; ++i
, ++cevents
) {
261 fd
= cevents
->data
.fd
;
263 CodeContext::Reset(F
->codeContext
);
264 debugs(5, DEBUG_EPOLL
? 0 : 8, HERE
<< "got FD " << fd
<< " events=" <<
265 std::hex
<< cevents
->events
<< " monitoring=" << F
->epoll_state
<<
266 " F->read_handler=" << F
->read_handler
<< " F->write_handler=" << F
->write_handler
);
268 // TODO: add EPOLLPRI??
270 if (cevents
->events
& (EPOLLIN
|EPOLLHUP
|EPOLLERR
) || F
->flags
.read_pending
) {
271 if ((hdl
= F
->read_handler
) != NULL
) {
272 debugs(5, DEBUG_EPOLL
? 0 : 8, HERE
<< "Calling read handler on FD " << fd
);
273 PROF_start(comm_write_handler
);
274 F
->read_handler
= NULL
;
275 hdl(fd
, F
->read_data
);
276 PROF_stop(comm_write_handler
);
277 ++ statCounter
.select_fds
;
279 debugs(5, DEBUG_EPOLL
? 0 : 8, HERE
<< "no read handler for FD " << fd
);
280 // remove interest since no handler exist for this event.
281 SetSelect(fd
, COMM_SELECT_READ
, NULL
, NULL
, 0);
285 if (cevents
->events
& (EPOLLOUT
|EPOLLHUP
|EPOLLERR
)) {
286 if ((hdl
= F
->write_handler
) != NULL
) {
287 debugs(5, DEBUG_EPOLL
? 0 : 8, HERE
<< "Calling write handler on FD " << fd
);
288 PROF_start(comm_read_handler
);
289 F
->write_handler
= NULL
;
290 hdl(fd
, F
->write_data
);
291 PROF_stop(comm_read_handler
);
292 ++ statCounter
.select_fds
;
294 debugs(5, DEBUG_EPOLL
? 0 : 8, HERE
<< "no write handler for FD " << fd
);
295 // remove interest since no handler exist for this event.
296 SetSelect(fd
, COMM_SELECT_WRITE
, NULL
, NULL
, 0);
301 CodeContext::Reset();
303 PROF_stop(comm_handle_ready_fd
);
309 Comm::QuickPollRequired(void)
314 #endif /* USE_EPOLL */