]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/socketengines/socketengine_epoll.cpp
Merge v2.0.23 and v2.0.24 into master.
[user/henk/code/inspircd.git] / src / socketengines / socketengine_epoll.cpp
1 /*
2  * InspIRCd -- Internet Relay Chat Daemon
3  *
4  *   Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
5  *   Copyright (C) 2007-2008 Craig Edwards <craigedwards@brainbox.cc>
6  *
7  * This file is part of InspIRCd.  InspIRCd is free software: you can
8  * redistribute it and/or modify it under the terms of the GNU General Public
9  * License as published by the Free Software Foundation, version 2.
10  *
11  * This program is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
13  * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
14  * details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
18  */
19
20
21 #include "inspircd.h"
22 #include "exitcodes.h"
23
24 #include <sys/epoll.h>
25 #include <sys/resource.h>
26 #include <iostream>
27
28 /** A specialisation of the SocketEngine class, designed to use linux 2.6 epoll().
29  */
30 namespace
31 {
32         int EngineHandle;
33
34         /** These are used by epoll() to hold socket events
35          */
36         std::vector<struct epoll_event> events(1);
37 }
38
39 void SocketEngine::Init()
40 {
41         // MAX_DESCRIPTORS is mainly used for display purposes, no problem if getrlimit() fails
42         struct rlimit limit;
43         if (!getrlimit(RLIMIT_NOFILE, &limit))
44         {
45                 MAX_DESCRIPTORS = limit.rlim_cur;
46         }
47
48         // 128 is not a maximum, just a hint at the eventual number of sockets that may be polled,
49         // and it is completely ignored by 2.6.8 and later kernels, except it must be larger than zero.
50         EngineHandle = epoll_create(128);
51
52         if (EngineHandle == -1)
53         {
54                 ServerInstance->Logs->Log("SOCKET", LOG_DEFAULT, "ERROR: Could not initialize socket engine: %s", strerror(errno));
55                 ServerInstance->Logs->Log("SOCKET", LOG_DEFAULT, "ERROR: Your kernel probably does not have the proper features. This is a fatal error, exiting now.");
56                 std::cout << "ERROR: Could not initialize epoll socket engine: " << strerror(errno) << std::endl;
57                 std::cout << "ERROR: Your kernel probably does not have the proper features. This is a fatal error, exiting now." << std::endl;
58                 ServerInstance->QuickExit(EXIT_STATUS_SOCKETENGINE);
59         }
60 }
61
62 void SocketEngine::RecoverFromFork()
63 {
64 }
65
66 void SocketEngine::Deinit()
67 {
68         Close(EngineHandle);
69 }
70
71 static unsigned mask_to_epoll(int event_mask)
72 {
73         unsigned rv = 0;
74         if (event_mask & (FD_WANT_POLL_READ | FD_WANT_POLL_WRITE | FD_WANT_SINGLE_WRITE))
75         {
76                 // we need to use standard polling on this FD
77                 if (event_mask & (FD_WANT_POLL_READ | FD_WANT_FAST_READ))
78                         rv |= EPOLLIN;
79                 if (event_mask & (FD_WANT_POLL_WRITE | FD_WANT_FAST_WRITE | FD_WANT_SINGLE_WRITE))
80                         rv |= EPOLLOUT;
81         }
82         else
83         {
84                 // we can use edge-triggered polling on this FD
85                 rv = EPOLLET;
86                 if (event_mask & (FD_WANT_FAST_READ | FD_WANT_EDGE_READ))
87                         rv |= EPOLLIN;
88                 if (event_mask & (FD_WANT_FAST_WRITE | FD_WANT_EDGE_WRITE))
89                         rv |= EPOLLOUT;
90         }
91         return rv;
92 }
93
94 bool SocketEngine::AddFd(EventHandler* eh, int event_mask)
95 {
96         int fd = eh->GetFd();
97         if (fd < 0)
98         {
99                 ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "AddFd out of range: (fd: %d)", fd);
100                 return false;
101         }
102
103         if (!SocketEngine::AddFdRef(eh))
104         {
105                 ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "Attempt to add duplicate fd: %d", fd);
106                 return false;
107         }
108
109         struct epoll_event ev;
110         memset(&ev, 0, sizeof(ev));
111         ev.events = mask_to_epoll(event_mask);
112         ev.data.ptr = static_cast<void*>(eh);
113         int i = epoll_ctl(EngineHandle, EPOLL_CTL_ADD, fd, &ev);
114         if (i < 0)
115         {
116                 ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "Error adding fd: %d to socketengine: %s", fd, strerror(errno));
117                 return false;
118         }
119
120         ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "New file descriptor: %d", fd);
121
122         eh->SetEventMask(event_mask);
123         ResizeDouble(events);
124
125         return true;
126 }
127
128 void SocketEngine::OnSetEvent(EventHandler* eh, int old_mask, int new_mask)
129 {
130         unsigned old_events = mask_to_epoll(old_mask);
131         unsigned new_events = mask_to_epoll(new_mask);
132         if (old_events != new_events)
133         {
134                 // ok, we actually have something to tell the kernel about
135                 struct epoll_event ev;
136                 memset(&ev, 0, sizeof(ev));
137                 ev.events = new_events;
138                 ev.data.ptr = static_cast<void*>(eh);
139                 epoll_ctl(EngineHandle, EPOLL_CTL_MOD, eh->GetFd(), &ev);
140         }
141 }
142
143 void SocketEngine::DelFd(EventHandler* eh)
144 {
145         int fd = eh->GetFd();
146         if (fd < 0)
147         {
148                 ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "DelFd out of range: (fd: %d)", fd);
149                 return;
150         }
151
152         // Do not initialize epoll_event because for EPOLL_CTL_DEL operations the event is ignored and can be NULL.
153         // In kernel versions before 2.6.9, the EPOLL_CTL_DEL operation required a non-NULL pointer in event,
154         // even though this argument is ignored. Since Linux 2.6.9, event can be specified as NULL when using EPOLL_CTL_DEL.
155         struct epoll_event ev;
156         int i = epoll_ctl(EngineHandle, EPOLL_CTL_DEL, fd, &ev);
157
158         if (i < 0)
159         {
160                 ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "epoll_ctl can't remove socket: %s", strerror(errno));
161         }
162
163         SocketEngine::DelFdRef(eh);
164
165         ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "Remove file descriptor: %d", fd);
166 }
167
168 int SocketEngine::DispatchEvents()
169 {
170         int i = epoll_wait(EngineHandle, &events[0], events.size(), 1000);
171         ServerInstance->UpdateTime();
172
173         stats.TotalEvents += i;
174
175         for (int j = 0; j < i; j++)
176         {
177                 // Copy these in case the vector gets resized and ev invalidated
178                 const epoll_event ev = events[j];
179
180                 EventHandler* const eh = static_cast<EventHandler*>(ev.data.ptr);
181                 const int fd = eh->GetFd();
182                 if (fd < 0)
183                         continue;
184
185                 if (ev.events & EPOLLHUP)
186                 {
187                         stats.ErrorEvents++;
188                         eh->OnEventHandlerError(0);
189                         continue;
190                 }
191
192                 if (ev.events & EPOLLERR)
193                 {
194                         stats.ErrorEvents++;
195                         /* Get error number */
196                         socklen_t codesize = sizeof(int);
197                         int errcode;
198                         if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &errcode, &codesize) < 0)
199                                 errcode = errno;
200                         eh->OnEventHandlerError(errcode);
201                         continue;
202                 }
203
204                 int mask = eh->GetEventMask();
205                 if (ev.events & EPOLLIN)
206                         mask &= ~FD_READ_WILL_BLOCK;
207                 if (ev.events & EPOLLOUT)
208                 {
209                         mask &= ~FD_WRITE_WILL_BLOCK;
210                         if (mask & FD_WANT_SINGLE_WRITE)
211                         {
212                                 int nm = mask & ~FD_WANT_SINGLE_WRITE;
213                                 OnSetEvent(eh, mask, nm);
214                                 mask = nm;
215                         }
216                 }
217                 eh->SetEventMask(mask);
218                 if (ev.events & EPOLLIN)
219                 {
220                         stats.ReadEvents++;
221                         eh->OnEventHandlerRead();
222                         if (eh != GetRef(fd))
223                                 // whoa! we got deleted, better not give out the write event
224                                 continue;
225                 }
226                 if (ev.events & EPOLLOUT)
227                 {
228                         stats.WriteEvents++;
229                         eh->OnEventHandlerWrite();
230                 }
231         }
232
233         return i;
234 }