Line data Source code
1 : /*
2 : Unix SMB/CIFS implementation.
3 : main select loop and event handling
4 : Copyright (C) Stefan Metzmacher 2013
5 : Copyright (C) Jeremy Allison 2013
6 :
7 : ** NOTE! The following LGPL license applies to the tevent
8 : ** library. This does NOT imply that all of Samba is released
9 : ** under the LGPL
10 :
11 : This library is free software; you can redistribute it and/or
12 : modify it under the terms of the GNU Lesser General Public
13 : License as published by the Free Software Foundation; either
14 : version 3 of the License, or (at your option) any later version.
15 :
16 : This library is distributed in the hope that it will be useful,
17 : but WITHOUT ANY WARRANTY; without even the implied warranty of
18 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 : Lesser General Public License for more details.
20 :
21 : You should have received a copy of the GNU Lesser General Public
22 : License along with this library; if not, see <http://www.gnu.org/licenses/>.
23 : */
24 :
25 : /*
26 : This is SAMBA's default event loop code
27 :
28 : - we try to use epoll if configure detected support for it
29 : otherwise we use poll()
30 : - if epoll is broken on the system or the kernel doesn't support it
31 : at runtime we fallback to poll()
32 : */
33 :
34 : #include "replace.h"
35 : #include "tevent.h"
36 : #include "tevent_util.h"
37 : #include "tevent_internal.h"
38 :
39 : struct std_event_glue {
40 : const struct tevent_ops *epoll_ops;
41 : const struct tevent_ops *poll_ops;
42 : struct tevent_ops *glue_ops;
43 : bool fallback_replay;
44 : };
45 :
46 : static int std_event_context_init(struct tevent_context *ev);
47 :
48 : static const struct tevent_ops std_event_ops = {
49 : .context_init = std_event_context_init,
50 : };
51 :
52 : /*
53 : If this function gets called. epoll failed at runtime.
54 : Move us to using poll instead. If we return false here,
55 : caller should abort().
56 : */
57 : #ifdef HAVE_EPOLL
58 5 : static bool std_fallback_to_poll(struct tevent_context *ev, bool replay)
59 : {
60 5 : void *glue_ptr = talloc_parent(ev->ops);
61 5 : struct std_event_glue *glue =
62 0 : talloc_get_type_abort(glue_ptr,
63 : struct std_event_glue);
64 : int ret;
65 : struct tevent_fd *fde;
66 :
67 5 : glue->fallback_replay = replay;
68 :
69 : /* First switch all the ops to poll. */
70 5 : glue->epoll_ops = NULL;
71 :
72 : /*
73 : * Set custom_ops the same as poll.
74 : */
75 5 : *glue->glue_ops = *glue->poll_ops;
76 5 : glue->glue_ops->context_init = std_event_context_init;
77 :
78 : /* Next initialize the poll backend. */
79 5 : ret = glue->poll_ops->context_init(ev);
80 5 : if (ret != 0) {
81 0 : return false;
82 : }
83 :
84 : /*
85 : * Now we have to change all the existing file descriptor
86 : * events from the epoll backend to the poll backend.
87 : */
88 10 : for (fde = ev->fd_events; fde; fde = fde->next) {
89 : bool ok;
90 :
91 : /* Re-add this event as a poll backend event. */
92 5 : ok = tevent_poll_event_add_fd_internal(ev, fde);
93 5 : if (!ok) {
94 0 : return false;
95 : }
96 : }
97 :
98 5 : return true;
99 : }
100 : #endif
101 :
102 200473792 : static int std_event_loop_once(struct tevent_context *ev, const char *location)
103 : {
104 200473792 : void *glue_ptr = talloc_parent(ev->ops);
105 174441963 : struct std_event_glue *glue =
106 26031829 : talloc_get_type_abort(glue_ptr,
107 : struct std_event_glue);
108 : int ret;
109 :
110 200473792 : ret = glue->epoll_ops->loop_once(ev, location);
111 : /*
112 : * If the above hasn't panicked due to an epoll interface failure,
113 : * std_fallback_to_poll() wasn't called, and hasn't cleared epoll_ops to
114 : * signify fallback to poll_ops.
115 : */
116 200436410 : if (glue->epoll_ops != NULL) {
117 : /* No fallback */
118 200436410 : return ret;
119 : }
120 :
121 0 : if (!glue->fallback_replay) {
122 : /*
123 : * The problem happened while modifying an event.
124 : * An event handler was triggered in this case
125 : * and there is no need to call loop_once() again.
126 : */
127 0 : return ret;
128 : }
129 :
130 0 : return glue->poll_ops->loop_once(ev, location);
131 : }
132 :
133 37293 : static int std_event_loop_wait(struct tevent_context *ev, const char *location)
134 : {
135 37293 : void *glue_ptr = talloc_parent(ev->ops);
136 24547 : struct std_event_glue *glue =
137 12746 : talloc_get_type_abort(glue_ptr,
138 : struct std_event_glue);
139 : int ret;
140 :
141 37293 : ret = glue->epoll_ops->loop_wait(ev, location);
142 : /*
143 : * If the above hasn't panicked due to an epoll interface failure,
144 : * std_fallback_to_poll() wasn't called, and hasn't cleared epoll_ops to
145 : * signify fallback to poll_ops.
146 : */
147 1 : if (glue->epoll_ops != NULL) {
148 : /* No fallback */
149 1 : return ret;
150 : }
151 :
152 0 : return glue->poll_ops->loop_wait(ev, location);
153 : }
154 : /*
155 : Initialize the epoll backend and allow it to call a
156 : switch function if epoll fails at runtime.
157 : */
158 54585303 : static int std_event_context_init(struct tevent_context *ev)
159 : {
160 : struct std_event_glue *glue;
161 : int ret;
162 :
163 : /*
164 : * If this is the first initialization
165 : * we need to set up the allocated ops
166 : * pointers.
167 : */
168 :
169 54585303 : if (ev->ops == &std_event_ops) {
170 54547972 : glue = talloc_zero(ev, struct std_event_glue);
171 54547972 : if (glue == NULL) {
172 0 : return -1;
173 : }
174 :
175 54547972 : glue->epoll_ops = tevent_find_ops_byname("epoll");
176 :
177 54547972 : glue->poll_ops = tevent_find_ops_byname("poll");
178 54547972 : if (glue->poll_ops == NULL) {
179 0 : return -1;
180 : }
181 :
182 : /*
183 : * Allocate space for our custom ops.
184 : * Allocate as a child of our epoll_ops pointer
185 : * so we can easily get to it using talloc_parent.
186 : */
187 54547972 : glue->glue_ops = talloc_zero(glue, struct tevent_ops);
188 54547972 : if (glue->glue_ops == NULL) {
189 0 : talloc_free(glue);
190 0 : return -1;
191 : }
192 :
193 54547972 : ev->ops = glue->glue_ops;
194 : } else {
195 37331 : void *glue_ptr = talloc_parent(ev->ops);
196 37331 : glue = talloc_get_type_abort(glue_ptr, struct std_event_glue);
197 : }
198 :
199 54585303 : if (glue->epoll_ops != NULL) {
200 : /*
201 : * Set custom_ops the same as epoll,
202 : * except re-init using std_event_context_init()
203 : * and use std_event_loop_once() to add the
204 : * ability to fallback to a poll backend on
205 : * epoll runtime error.
206 : */
207 54585301 : *glue->glue_ops = *glue->epoll_ops;
208 54585301 : glue->glue_ops->context_init = std_event_context_init;
209 54585301 : glue->glue_ops->loop_once = std_event_loop_once;
210 54585301 : glue->glue_ops->loop_wait = std_event_loop_wait;
211 :
212 54585301 : ret = glue->epoll_ops->context_init(ev);
213 54585301 : if (ret == -1) {
214 0 : goto fallback;
215 : }
216 : #ifdef HAVE_EPOLL
217 54585301 : tevent_epoll_set_panic_fallback(ev, std_fallback_to_poll);
218 : #endif
219 :
220 54585301 : return ret;
221 : }
222 :
223 2 : fallback:
224 2 : glue->epoll_ops = NULL;
225 :
226 : /*
227 : * Set custom_ops the same as poll.
228 : */
229 2 : *glue->glue_ops = *glue->poll_ops;
230 2 : glue->glue_ops->context_init = std_event_context_init;
231 :
232 2 : return glue->poll_ops->context_init(ev);
233 : }
234 :
235 28196 : _PRIVATE_ bool tevent_standard_init(void)
236 : {
237 28196 : return tevent_register_backend("standard", &std_event_ops);
238 : }
|