The branch, master has been updated via daada84 tevent: Remove the previous "standard" tevent backend implementation. via 16f5707 tevent: Add in the new implementation of "standard" tevent backend. via 203f85c tevent: Add a private function tevent_poll_event_add_fd_internal(). via 5fe459f tevent: make use of tevent_find_ops_byname() in tevent_context_init_byname() via b5556a7 tevent: make sure tevent_backend_init() only runs once via aceeb58 tevent: Add a utility function tevent_find_ops_byname(). via 1ee428d tevent: Add in the same tevent_re_initialise() fix Metze put in the tevent_poll backend. via 06fb88b tevent: Add in some test code to allow the panic fallback path to be tested. via 6f98192 tevent: Plumb in the panic fallback code into the epoll_panic() runtime call. via 04ba47e tevent: Add an internal function tevent_epoll_set_panic_fallback(). via e4ef2ecf tevent: pass 'bool replay' to epoll_panic() via 129da06 tevent: Ensure we return after every call to epoll_panic(). via c36f8c1 tevent: Preparing to fix "standard" backend fallback. Initialize standard after epoll. from 736e3b1 docs: fix typo in serverrole.xml
http://gitweb.samba.org/?p=samba.git;a=shortlog;h=master - Log ----------------------------------------------------------------- commit daada849209df893ef9b42ea2be5de77d2d4125f Author: Jeremy Allison <j...@samba.org> Date: Mon Feb 11 11:42:08 2013 -0800 tevent: Remove the previous "standard" tevent backend implementation. This was a horrible hybrid of duplicated epoll and select() code. Signed-off-by: Jeremy Allison <j...@samba.org> Reviewed-by: Stefan Metzmacher <me...@samba.org> Autobuild-User(master): Jeremy Allison <j...@samba.org> Autobuild-Date(master): Thu Feb 14 22:40:30 CET 2013 on sn-devel-104 commit 16f57076b1b527c65da42e1fab6119c67bcd7f47 Author: Jeremy Allison <j...@samba.org> Date: Mon Feb 11 11:40:49 2013 -0800 tevent: Add in the new implementation of "standard" tevent backend. Falls back cleanly from epoll -> poll, or uses poll if epoll not available. Signed-off-by: Jeremy Allison <j...@samba.org> Signed-off-by: Stefan Metzmacher <me...@samba.org> Reviewed-by: Stefan Metzmacher <me...@samba.org> commit 203f85c25eb9238eae8e4dc5b55b549294b0fa03 Author: Jeremy Allison <j...@samba.org> Date: Mon Feb 11 11:20:28 2013 -0800 tevent: Add a private function tevent_poll_event_add_fd_internal(). Not yet used, but will be called by the "standard" fallback from epoll -> poll backends. Signed-off-by: Jeremy Allison <j...@samba.org> Reviewed-by: Stefan Metzmacher <me...@samba.org> commit 5fe459f5d7c0e6b5eeeb1aa2c8248e174a31008f Author: Stefan Metzmacher <me...@samba.org> Date: Thu Feb 14 09:30:31 2013 +0100 tevent: make use of tevent_find_ops_byname() in tevent_context_init_byname() Signed-off-by: Stefan Metzmacher <me...@samba.org> commit b5556a79e39be0bc9945cbac4e603b045ab55505 Author: Stefan Metzmacher <me...@samba.org> Date: Thu Feb 14 09:29:57 2013 +0100 tevent: make sure tevent_backend_init() only runs once Signed-off-by: Stefan Metzmacher <me...@samba.org> commit aceeb585cb6170e42cadae17791b8314083c278c Author: Jeremy Allison <j...@samba.org> Date: Mon Feb 11 10:56:58 2013 -0800 tevent: Add a utility function tevent_find_ops_byname(). Returns an event ops struct given a string name. Not yet used, but will be part of the new "standard" fallback code. Signed-off-by: Jeremy Allison <j...@samba.org> Reviewed-by: Stefan Metzmacher <me...@samba.org> commit 1ee428d5cad6910521ce77322614183fa2c7916e Author: Jeremy Allison <j...@samba.org> Date: Mon Feb 11 10:53:15 2013 -0800 tevent: Add in the same tevent_re_initialise() fix Metze put in the tevent_poll backend. We might be called during tevent_re_initialise() which means we need to free our old additional_data. Signed-off-by: Jeremy Allison <j...@samba.org> Reviewed-by: Stefan Metzmacher <me...@samba.org> commit 06fb88b449806d6de7ca20c73778dd6545a38cf7 Author: Jeremy Allison <j...@samba.org> Date: Mon Feb 11 10:52:30 2013 -0800 tevent: Add in some test code to allow the panic fallback path to be tested. Signed-off-by: Jeremy Allison <j...@samba.org> Reviewed-by: Stefan Metzmacher <me...@samba.org> commit 6f9819229b3d60cb898a0f9bfc67793b6c40fc2c Author: Jeremy Allison <j...@samba.org> Date: Mon Feb 11 10:48:02 2013 -0800 tevent: Plumb in the panic fallback code into the epoll_panic() runtime call. Signed-off-by: Jeremy Allison <j...@samba.org> Reviewed-by: Stefan Metzmacher <me...@samba.org> commit 04ba47e491d84f58562b57f937be632e75c204aa Author: Jeremy Allison <j...@samba.org> Date: Mon Feb 11 10:43:39 2013 -0800 tevent: Add an internal function tevent_epoll_set_panic_fallback(). Can be set externally, allows us to fallback if epoll fails at runtime. Signed-off-by: Jeremy Allison <j...@samba.org> Reviewed-by: Stefan Metzmacher <me...@samba.org> commit e4ef2ecf023c63ee28e40a1b60ecc6091a126001 Author: Stefan Metzmacher <me...@samba.org> Date: Thu Feb 14 10:58:55 2013 +0100 tevent: pass 'bool replay' to epoll_panic() A fallback panic handler will need to know if there was an error while waiting for events (replay=true) or if the error happened on modify (replay=false). Signed-off-by: Stefan Metzmacher <me...@samba.org> commit 129da06d2d9c9d1cedc5e4de492744b29adc1684 Author: Jeremy Allison <j...@samba.org> Date: Mon Feb 11 10:38:01 2013 -0800 tevent: Ensure we return after every call to epoll_panic(). Currently we can't return from this, but the new fallback code will change this. Signed-off-by: Jeremy Allison <j...@samba.org> Reviewed-by: Stefan Metzmacher <me...@samba.org> commit c36f8c14008e55b2be2e93c0987eb6971d45855f Author: Jeremy Allison <j...@samba.org> Date: Mon Feb 11 10:36:52 2013 -0800 tevent: Preparing to fix "standard" backend fallback. Initialize standard after epoll. Signed-off-by: Jeremy Allison <j...@samba.org> Reviewed-by: Stefan Metzmacher <me...@samba.org> ----------------------------------------------------------------------- Summary of changes: lib/tevent/tevent.c | 51 +++- lib/tevent/tevent_epoll.c | 84 +++++- lib/tevent/tevent_internal.h | 7 + lib/tevent/tevent_poll.c | 16 + lib/tevent/tevent_select.c | 6 + lib/tevent/tevent_standard.c | 625 +++++++++--------------------------------- 6 files changed, 266 insertions(+), 523 deletions(-) Changeset truncated at 500 lines: diff --git a/lib/tevent/tevent.c b/lib/tevent/tevent.c index fa842e4..aa758de 100644 --- a/lib/tevent/tevent.c +++ b/lib/tevent/tevent.c @@ -112,13 +112,43 @@ void tevent_set_default_backend(const char *backend) */ static void tevent_backend_init(void) { + static bool done; + + if (done) { + return; + } + + done = true; + tevent_select_init(); tevent_poll_init(); tevent_poll_mt_init(); - tevent_standard_init(); #ifdef HAVE_EPOLL tevent_epoll_init(); #endif + tevent_standard_init(); +} + +_PRIVATE_ const struct tevent_ops *tevent_find_ops_byname(const char *name) +{ + struct tevent_ops_list *e; + + tevent_backend_init(); + + if (name == NULL) { + name = tevent_default_backend; + } + if (name == NULL) { + name = "standard"; + } + + for (e = tevent_backends; e != NULL; e = e->next) { + if (0 == strcmp(e->name, name)) { + return e->ops; + } + } + + return NULL; } /* @@ -243,23 +273,14 @@ struct tevent_context *tevent_context_init_ops(TALLOC_CTX *mem_ctx, struct tevent_context *tevent_context_init_byname(TALLOC_CTX *mem_ctx, const char *name) { - struct tevent_ops_list *e; + struct tevent_ops *ops; - tevent_backend_init(); - - if (name == NULL) { - name = tevent_default_backend; - } - if (name == NULL) { - name = "standard"; + ops = tevent_find_ops_byname(name); + if (ops == NULL) { + return NULL; } - for (e=tevent_backends;e;e=e->next) { - if (strcmp(name, e->name) == 0) { - return tevent_context_init_ops(mem_ctx, e->ops, NULL); - } - } - return NULL; + return tevent_context_init_ops(mem_ctx, ops, NULL); } diff --git a/lib/tevent/tevent_epoll.c b/lib/tevent/tevent_epoll.c index 5f93de2..8696215 100644 --- a/lib/tevent/tevent_epoll.c +++ b/lib/tevent/tevent_epoll.c @@ -39,16 +39,76 @@ struct epoll_event_context { int epoll_fd; pid_t pid; + + bool (*panic_fallback)(struct tevent_context *ev, bool replay); }; +#ifdef TEST_PANIC_FALLBACK +static int epoll_wait_panic_fallback(int epfd, + struct epoll_event *events, + int maxevents, + int timeout) +{ + /* 50% of the time, fail... */ + if ((random() % 2) == 0) { + errno = EINVAL; + return -1; + } + + return epoll_wait(epfd, events, maxevents, timeout); +} + +#define epoll_wait epoll_wait_panic_fallback +#endif + +/* + called to set the panic fallback function. +*/ +_PRIVATE_ bool tevent_epoll_set_panic_fallback(struct tevent_context *ev, + bool (*panic_fallback)(struct tevent_context *ev, + bool replay)) +{ + struct epoll_event_context *epoll_ev; + + if (ev->additional_data == NULL) { + return false; + } + + epoll_ev = talloc_get_type(ev->additional_data, + struct epoll_event_context); + if (epoll_ev == NULL) { + return false; + } + epoll_ev->panic_fallback = panic_fallback; + return true; +} + /* called when a epoll call fails */ -static void epoll_panic(struct epoll_event_context *epoll_ev, const char *reason) +static void epoll_panic(struct epoll_event_context *epoll_ev, + const char *reason, bool replay) { - tevent_debug(epoll_ev->ev, TEVENT_DEBUG_FATAL, - "%s (%s) - calling abort()\n", reason, strerror(errno)); - abort(); + struct tevent_context *ev = epoll_ev->ev; + + if (epoll_ev->panic_fallback == NULL) { + tevent_debug(ev, TEVENT_DEBUG_FATAL, + "%s (%s) replay[%u] - calling abort()\n", + reason, strerror(errno), (unsigned)replay); + abort(); + } + + tevent_debug(ev, TEVENT_DEBUG_WARNING, + "%s (%s) replay[%u] - calling panic_fallback\n", + reason, strerror(errno), (unsigned)replay); + + if (!epoll_ev->panic_fallback(ev, replay)) { + /* Fallback failed. */ + tevent_debug(ev, TEVENT_DEBUG_FATAL, + "%s (%s) replay[%u] - calling abort()\n", + reason, strerror(errno), (unsigned)replay); + abort(); + } } /* @@ -151,7 +211,8 @@ static void epoll_add_event(struct epoll_event_context *epoll_ev, struct tevent_ event.events = epoll_map_flags(fde->flags); event.data.ptr = fde; if (epoll_ctl(epoll_ev->epoll_fd, EPOLL_CTL_ADD, fde->fd, &event) != 0) { - epoll_panic(epoll_ev, "EPOLL_CTL_ADD failed"); + epoll_panic(epoll_ev, "EPOLL_CTL_ADD failed", false); + return; } fde->additional_flags |= EPOLL_ADDITIONAL_FD_FLAG_HAS_EVENT; @@ -200,7 +261,8 @@ static void epoll_mod_event(struct epoll_event_context *epoll_ev, struct tevent_ event.events = epoll_map_flags(fde->flags); event.data.ptr = fde; if (epoll_ctl(epoll_ev->epoll_fd, EPOLL_CTL_MOD, fde->fd, &event) != 0) { - epoll_panic(epoll_ev, "EPOLL_CTL_MOD failed"); + epoll_panic(epoll_ev, "EPOLL_CTL_MOD failed", false); + return; } /* only if we want to read we want to tell the event handler about errors */ @@ -275,7 +337,7 @@ static int epoll_event_loop(struct epoll_event_context *epoll_ev, struct timeval } if (ret == -1 && errno != EINTR) { - epoll_panic(epoll_ev, "epoll_wait() failed"); + epoll_panic(epoll_ev, "epoll_wait() failed", true); return -1; } @@ -291,7 +353,7 @@ static int epoll_event_loop(struct epoll_event_context *epoll_ev, struct timeval uint16_t flags = 0; if (fde == NULL) { - epoll_panic(epoll_ev, "epoll_wait() gave bad data"); + epoll_panic(epoll_ev, "epoll_wait() gave bad data", true); return -1; } if (events[i].events & (EPOLLHUP|EPOLLERR)) { @@ -327,6 +389,12 @@ static int epoll_event_context_init(struct tevent_context *ev) int ret; struct epoll_event_context *epoll_ev; + /* + * We might be called during tevent_re_initialise() + * which means we need to free our old additional_data. + */ + TALLOC_FREE(ev->additional_data); + epoll_ev = talloc_zero(ev, struct epoll_event_context); if (!epoll_ev) return -1; epoll_ev->ev = ev; diff --git a/lib/tevent/tevent_internal.h b/lib/tevent/tevent_internal.h index f09cf57..8433333 100644 --- a/lib/tevent/tevent_internal.h +++ b/lib/tevent/tevent_internal.h @@ -265,6 +265,7 @@ struct tevent_context { } tracing; }; +const struct tevent_ops *tevent_find_ops_byname(const char *name); int tevent_common_context_destructor(struct tevent_context *ev); int tevent_common_loop_wait(struct tevent_context *ev, @@ -315,10 +316,16 @@ void tevent_cleanup_pending_signal_handlers(struct tevent_signal *se); bool tevent_standard_init(void); bool tevent_select_init(void); bool tevent_poll_init(void); +void tevent_poll_event_add_fd_internal(struct tevent_context *ev, + struct tevent_fd *fde); bool tevent_poll_mt_init(void); #ifdef HAVE_EPOLL bool tevent_epoll_init(void); +bool tevent_epoll_set_panic_fallback(struct tevent_context *ev, + bool (*panic_fallback)(struct tevent_context *ev, + bool replay)); #endif + void tevent_trace_point_callback(struct tevent_context *ev, enum tevent_trace_point); diff --git a/lib/tevent/tevent_poll.c b/lib/tevent/tevent_poll.c index 5479f2f..89b3bbc 100644 --- a/lib/tevent/tevent_poll.c +++ b/lib/tevent/tevent_poll.c @@ -256,6 +256,22 @@ static void poll_event_schedule_immediate(struct tevent_immediate *im, } /* + Private function called by "standard" backend fallback. + Note this only allows fallback to "poll" backend, not "poll-mt". +*/ +_PRIVATE_ void tevent_poll_event_add_fd_internal(struct tevent_context *ev, + struct tevent_fd *fde) +{ + struct poll_event_context *poll_ev = talloc_get_type_abort( + ev->additional_data, struct poll_event_context); + + fde->additional_flags = UINT64_MAX; + fde->additional_data = NULL; + DLIST_ADD(poll_ev->fresh, fde); + talloc_set_destructor(fde, poll_fresh_fde_destructor); +} + +/* add a fd based event return NULL on failure (memory allocation error) */ diff --git a/lib/tevent/tevent_select.c b/lib/tevent/tevent_select.c index c11f0e8..7e0c927 100644 --- a/lib/tevent/tevent_select.c +++ b/lib/tevent/tevent_select.c @@ -47,6 +47,12 @@ static int select_event_context_init(struct tevent_context *ev) { struct select_event_context *select_ev; + /* + * We might be called during tevent_re_initialise() + * which means we need to free our old additional_data. + */ + TALLOC_FREE(ev->additional_data); + select_ev = talloc_zero(ev, struct select_event_context); if (!select_ev) return -1; select_ev->ev = ev; diff --git a/lib/tevent/tevent_standard.c b/lib/tevent/tevent_standard.c index 1e33720..d7a5bd7 100644 --- a/lib/tevent/tevent_standard.c +++ b/lib/tevent/tevent_standard.c @@ -1,8 +1,8 @@ /* Unix SMB/CIFS implementation. main select loop and event handling - Copyright (C) Andrew Tridgell 2003-2005 - Copyright (C) Stefan Metzmacher 2005-2009 + Copyright (C) Stefan Metzmacher 2013 + Copyright (C) Jeremy Allison 2013 ** NOTE! The following LGPL license applies to the tevent ** library. This does NOT imply that all of Samba is released @@ -26,567 +26,192 @@ This is SAMBA's default event loop code - we try to use epoll if configure detected support for it - otherwise we use select() + otherwise we use poll() - if epoll is broken on the system or the kernel doesn't support it - at runtime we fallback to select() + at runtime we fallback to poll() */ #include "replace.h" -#include "system/filesys.h" -#include "system/select.h" #include "tevent.h" #include "tevent_util.h" #include "tevent_internal.h" -struct std_event_context { - /* a pointer back to the generic event_context */ - struct tevent_context *ev; - - /* the maximum file descriptor number in fd_events */ - int maxfd; - - /* information for exiting from the event loop */ - int exit_code; - - /* when using epoll this is the handle from epoll_create */ - int epoll_fd; - - /* our pid at the time the epoll_fd was created */ - pid_t pid; +struct std_event_glue { + const struct tevent_ops *epoll_ops; + const struct tevent_ops *poll_ops; + struct tevent_ops *glue_ops; + bool fallback_replay; }; -/* use epoll if it is available */ -#if HAVE_EPOLL -/* - called when a epoll call fails, and we should fallback - to using select -*/ -static void epoll_fallback_to_select(struct std_event_context *std_ev, const char *reason) -{ - tevent_debug(std_ev->ev, TEVENT_DEBUG_FATAL, - "%s (%s) - falling back to select()\n", - reason, strerror(errno)); - close(std_ev->epoll_fd); - std_ev->epoll_fd = -1; - talloc_set_destructor(std_ev, NULL); -} - -/* - map from TEVENT_FD_* to EPOLLIN/EPOLLOUT -*/ -static uint32_t epoll_map_flags(uint16_t flags) -{ - uint32_t ret = 0; - if (flags & TEVENT_FD_READ) ret |= (EPOLLIN | EPOLLERR | EPOLLHUP); - if (flags & TEVENT_FD_WRITE) ret |= (EPOLLOUT | EPOLLERR | EPOLLHUP); - return ret; -} +static int std_event_context_init(struct tevent_context *ev); -/* - free the epoll fd -*/ -static int epoll_ctx_destructor(struct std_event_context *std_ev) -{ - if (std_ev->epoll_fd != -1) { - close(std_ev->epoll_fd); - } - std_ev->epoll_fd = -1; - return 0; -} +static const struct tevent_ops std_event_ops = { + .context_init = std_event_context_init, +}; /* - init the epoll fd + If this function gets called. epoll failed at runtime. + Move us to using poll instead. If we return false here, + caller should abort(). */ -static void epoll_init_ctx(struct std_event_context *std_ev) -{ - std_ev->epoll_fd = epoll_create(64); - if (std_ev->epoll_fd == -1) { - tevent_debug(std_ev->ev, TEVENT_DEBUG_FATAL, - "Failed to create epoll handle.\n"); - return; - } - - if (!ev_set_close_on_exec(std_ev->epoll_fd)) { - tevent_debug(std_ev->ev, TEVENT_DEBUG_WARNING, - "Failed to set close-on-exec, file descriptor may be leaked to children.\n"); - } - - std_ev->pid = getpid(); - talloc_set_destructor(std_ev, epoll_ctx_destructor); -} - -static void epoll_add_event(struct std_event_context *std_ev, struct tevent_fd *fde); - -/* - reopen the epoll handle when our pid changes - see http://junkcode.samba.org/ftp/unpacked/junkcode/epoll_fork.c for an - demonstration of why this is needed - */ -static void epoll_check_reopen(struct std_event_context *std_ev) +static bool std_fallback_to_poll(struct tevent_context *ev, bool replay) { + void *glue_ptr = talloc_parent(ev->ops); + struct std_event_glue *glue = + talloc_get_type_abort(glue_ptr, + struct std_event_glue); + int ret; struct tevent_fd *fde; + struct tevent_fd *fde_next; - if (std_ev->pid == getpid()) { - return; - } + glue->fallback_replay = replay; - close(std_ev->epoll_fd); - std_ev->epoll_fd = epoll_create(64); - if (std_ev->epoll_fd == -1) { - tevent_debug(std_ev->ev, TEVENT_DEBUG_FATAL, - "Failed to recreate epoll handle after fork\n"); - return; - } + /* First switch all the ops to poll. */ + glue->epoll_ops = NULL; + TALLOC_FREE(ev->additional_data); - if (!ev_set_close_on_exec(std_ev->epoll_fd)) { - tevent_debug(std_ev->ev, TEVENT_DEBUG_WARNING, - "Failed to set close-on-exec, file descriptor may be leaked to children.\n"); - } + /* + * Set custom_ops the same as poll. + */ + *glue->glue_ops = *glue->poll_ops; + glue->glue_ops->context_init = std_event_context_init; - std_ev->pid = getpid(); - for (fde=std_ev->ev->fd_events;fde;fde=fde->next) { - epoll_add_event(std_ev, fde); + /* Next initialize the poll backend. */ + ret = glue->poll_ops->context_init(ev); + if (ret != 0) { + return false; } -} -#define EPOLL_ADDITIONAL_FD_FLAG_HAS_EVENT (1<<0) -#define EPOLL_ADDITIONAL_FD_FLAG_REPORT_ERROR (1<<1) -#define EPOLL_ADDITIONAL_FD_FLAG_GOT_ERROR (1<<2) - -/* - add the epoll event to the given fd_event -*/ -static void epoll_add_event(struct std_event_context *std_ev, struct tevent_fd *fde) -{ - struct epoll_event event; - if (std_ev->epoll_fd == -1) return; - - fde->additional_flags &= ~EPOLL_ADDITIONAL_FD_FLAG_REPORT_ERROR; - - /* if we don't want events yet, don't add an epoll_event */ - if (fde->flags == 0) return; + /* + * Now we have to change all the existing file descriptor + * events from the epoll backend to the poll backend. + */ + for (fde = ev->fd_events; fde; fde = fde_next) { + /* + * We must remove this fde off the ev->fd_events list. + */ + fde_next = fde->next; - ZERO_STRUCT(event); - event.events = epoll_map_flags(fde->flags); - event.data.ptr = fde; - if (epoll_ctl(std_ev->epoll_fd, EPOLL_CTL_ADD, fde->fd, &event) != 0) { - epoll_fallback_to_select(std_ev, "EPOLL_CTL_ADD failed"); - } - fde->additional_flags |= EPOLL_ADDITIONAL_FD_FLAG_HAS_EVENT; + /* Remove from the ev->fd_events list. */ + DLIST_REMOVE(ev->fd_events, fde); - /* only if we want to read we want to tell the event handler about errors */ -- Samba Shared Repository