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 0 : struct std_event_glue *glue = 62 5 : talloc_get_type_abort(glue_ptr, 63 : struct std_event_glue); 64 0 : int ret; 65 0 : 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 0 : 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 416626754 : static int std_event_loop_once(struct tevent_context *ev, const char *location) 103 : { 104 416626754 : void *glue_ptr = talloc_parent(ev->ops); 105 14834114 : struct std_event_glue *glue = 106 416626754 : talloc_get_type_abort(glue_ptr, 107 : struct std_event_glue); 108 14834116 : int ret; 109 : 110 416626756 : 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 416531287 : if (glue->epoll_ops != NULL) { 117 : /* No fallback */ 118 401698936 : 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 95291 : static int std_event_loop_wait(struct tevent_context *ev, const char *location) 134 : { 135 95291 : void *glue_ptr = talloc_parent(ev->ops); 136 1755 : struct std_event_glue *glue = 137 95291 : talloc_get_type_abort(glue_ptr, 138 : struct std_event_glue); 139 1755 : int ret; 140 : 141 95291 : 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 10 : 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 83086477 : static int std_event_context_init(struct tevent_context *ev) 159 : { 160 3110090 : struct std_event_glue *glue; 161 3110090 : 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 83086477 : if (ev->ops->loop_once == NULL) { 170 82990998 : glue = talloc_zero(ev, struct std_event_glue); 171 82990998 : if (glue == NULL) { 172 0 : return -1; 173 : } 174 : 175 82990998 : glue->epoll_ops = tevent_find_ops_byname("epoll"); 176 : 177 82990998 : glue->poll_ops = tevent_find_ops_byname("poll"); 178 82990998 : 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 82990998 : glue->glue_ops = talloc_zero(glue, struct tevent_ops); 188 82990998 : if (glue->glue_ops == NULL) { 189 0 : talloc_free(glue); 190 0 : return -1; 191 : } 192 : 193 82990998 : ev->ops = glue->glue_ops; 194 : } else { 195 95479 : void *glue_ptr = talloc_parent(ev->ops); 196 95479 : glue = talloc_get_type_abort(glue_ptr, struct std_event_glue); 197 : } 198 : 199 83086477 : 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 83086475 : *glue->glue_ops = *glue->epoll_ops; 208 83086475 : glue->glue_ops->context_init = std_event_context_init; 209 83086475 : glue->glue_ops->loop_once = std_event_loop_once; 210 83086475 : glue->glue_ops->loop_wait = std_event_loop_wait; 211 : 212 83086475 : ret = glue->epoll_ops->context_init(ev); 213 83086475 : if (ret == -1) { 214 0 : goto fallback; 215 : } 216 : #ifdef HAVE_EPOLL 217 83086475 : tevent_epoll_set_panic_fallback(ev, std_fallback_to_poll); 218 : #endif 219 : 220 83086475 : 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 76842 : _PRIVATE_ bool tevent_standard_init(void) 236 : { 237 76842 : return tevent_register_backend("standard", &std_event_ops); 238 : }