From mboxrd@z Thu Jan 1 00:00:00 1970 From: Ian Jackson Subject: [PATCH 10/23] libxl: events: Provide libxl__ev_evtchn* Date: Tue, 17 Dec 2013 18:35:24 +0000 Message-ID: <1387305337-15355-11-git-send-email-ian.jackson@eu.citrix.com> References: <1387305337-15355-1-git-send-email-ian.jackson@eu.citrix.com> Mime-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Return-path: In-Reply-To: <1387305337-15355-1-git-send-email-ian.jackson@eu.citrix.com> List-Unsubscribe: , List-Post: List-Help: List-Subscribe: , Sender: xen-devel-bounces@lists.xen.org Errors-To: xen-devel-bounces@lists.xen.org To: xen-devel@lists.xensource.com Cc: Shriram Rajagopalan , George Dunlap , Ian Jackson , Ian Campbell , Stefano Stabellini List-Id: xen-devel@lists.xenproject.org This also involves providing a gc for the latter part of libxl_ctx_alloc. Signed-off-by: Ian Jackson CC: Stefano Stabellini CC: Ian Campbell --- tools/libxl/libxl.c | 9 +++ tools/libxl/libxl_event.c | 152 ++++++++++++++++++++++++++++++++++++++++++ tools/libxl/libxl_internal.h | 48 +++++++++++++ 3 files changed, 209 insertions(+) diff --git a/tools/libxl/libxl.c b/tools/libxl/libxl.c index 85b56a9..51158aa 100644 --- a/tools/libxl/libxl.c +++ b/tools/libxl/libxl.c @@ -60,6 +60,10 @@ int libxl_ctx_alloc(libxl_ctx **pctx, int version, LIBXL_SLIST_INIT(&ctx->watch_freeslots); libxl__ev_fd_init(&ctx->watch_efd); + ctx->xce = 0; + LIBXL_LIST_INIT(&ctx->evtchns_waiting); + libxl__ev_fd_init(&ctx->evtchn_efd); + LIBXL_TAILQ_INIT(&ctx->death_list); libxl__ev_xswatch_init(&ctx->death_watch); @@ -104,6 +108,8 @@ int libxl_ctx_alloc(libxl_ctx **pctx, int version, rc = ERROR_FAIL; goto out; } + rc = libxl__ctx_evtchn_init(gc); + *pctx = ctx; return 0; @@ -147,16 +153,19 @@ int libxl_ctx_free(libxl_ctx *ctx) for (i = 0; i < ctx->watch_nslots; i++) assert(!libxl__watch_slot_contents(gc, i)); libxl__ev_fd_deregister(gc, &ctx->watch_efd); + libxl__ev_fd_deregister(gc, &ctx->evtchn_efd); libxl__ev_fd_deregister(gc, &ctx->sigchld_selfpipe_efd); /* Now there should be no more events requested from the application: */ assert(LIBXL_LIST_EMPTY(&ctx->efds)); assert(LIBXL_TAILQ_EMPTY(&ctx->etimes)); + assert(LIBXL_LIST_EMPTY(&ctx->evtchns_waiting)); if (ctx->xch) xc_interface_close(ctx->xch); libxl_version_info_dispose(&ctx->version_info); if (ctx->xsh) xs_daemon_close(ctx->xsh); + if (ctx->xce) xc_evtchn_close(ctx->xce); libxl__poller_dispose(&ctx->poller_app); assert(LIBXL_LIST_EMPTY(&ctx->pollers_event)); diff --git a/tools/libxl/libxl_event.c b/tools/libxl/libxl_event.c index 9c4fe1c..838b620 100644 --- a/tools/libxl/libxl_event.c +++ b/tools/libxl/libxl_event.c @@ -614,6 +614,158 @@ void libxl__ev_xswatch_deregister(libxl__gc *gc, libxl__ev_xswatch *w) } /* + * evtchn + */ + +static int evtchn_revents_check(libxl__egc *egc, int revents) +{ + EGC_GC; + + if (revents & ~POLLIN) { + LOG(ERROR, "unexpected poll event on event channel fd: %x", revents); + LIBXL__EVENT_DISASTER(egc, + "unexpected poll event on event channel fd", 0, 0); + libxl__ev_fd_deregister(gc, &CTX->evtchn_efd); + return ERROR_FAIL; + } + + assert(revents & POLLIN); + + return 0; +} + +static void evtchn_fd_callback(libxl__egc *egc, libxl__ev_fd *ev, + int fd, short events, short revents) +{ + EGC_GC; + libxl__ev_evtchn *evev; + int port, r, rc; + struct pollfd recheck; + + rc = evtchn_revents_check(egc, revents); + if (rc) return; + + for (;;) { + /* Check the fd again. The incoming revent may no longer be + * true, because the libxl ctx lock has not necessarily been + * held continuously since someone noticed the fd. Normally + * this wouldn't be a problem but evtchn devices don't always + * honour O_NONBLOCK (see xenctrl.h). */ + + recheck.fd = fd; + recheck.events = POLLIN; + recheck.revents = 0; + r = poll(&recheck, 1, 0); + DBG("ev_evtchn recheck r=%d revents=%#x", r, recheck.revents); + if (r < 0) { + LIBXL__EVENT_DISASTER(egc, + "unexpected failure polling event channel fd for recheck", + errno, 0); + return; + } + if (r == 0) + break; + rc = evtchn_revents_check(egc, recheck.revents); + if (rc) return; + + /* OK, that's that workaround done. We can actually check for + * work for us to do: */ + + port = xc_evtchn_pending(CTX->xce); + if (port < 0) { + if (errno == EAGAIN) + break; + LIBXL__EVENT_DISASTER(egc, + "unexpected failure fetching occurring event port number from evtchn", + errno, 0); + return; + } + + LIBXL_LIST_FOREACH(evev, &CTX->evtchns_waiting, entry) + if (port == evev->port) + goto found; + /* not found */ + DBG("ev_evtchn port=%d no-one cared", port); + continue; + + found: + DBG("ev_evtchn=%p port=%d signaled", evev, port); + evev->waiting = 0; + LIBXL_LIST_REMOVE(evev, entry); + evev->callback(egc, evev); + } +} + +int libxl__ctx_evtchn_init(libxl__gc *gc) { + xc_evtchn *xce; + int rc, fd; + + if (CTX->xce) + return 0; + + xce = xc_evtchn_open(CTX->lg, 0); + if (!xce) { + LOGE(ERROR,"cannot open libxc evtchn handle"); + rc = ERROR_FAIL; + goto out; + } + + fd = xc_evtchn_fd(xce); + assert(fd >= 0); + + rc = libxl_fd_set_nonblock(CTX, fd, 1); + if (rc) goto out; + + rc = libxl__ev_fd_register(gc, &CTX->evtchn_efd, + evtchn_fd_callback, fd, POLLIN); + if (rc) goto out; + + CTX->xce = xce; + return 0; + + out: + xc_evtchn_close(xce); + return rc; +} + +int libxl__ev_evtchn_wait(libxl__gc *gc, libxl__ev_evtchn *evev) +{ + int r, rc; + + DBG("ev_evtchn=%p port=%d wait (was waiting=%d)", + evev, evev->port, evev->waiting); + + if (evev->waiting) + return 0; + + r = xc_evtchn_unmask(CTX->xce, evev->port); + if (r) { + LOGE(ERROR,"cannot unmask event channel %d",evev->port); + rc = ERROR_FAIL; + goto out; + } + + evev->waiting = 1; + LIBXL_LIST_INSERT_HEAD(&CTX->evtchns_waiting, evev, entry); + return 0; + + out: + return rc; +} + +void libxl__ev_evtchn_cancel(libxl__gc *gc, libxl__ev_evtchn *evev) +{ + DBG("ev_evtchn=%p port=%d cancel (was waiting=%d)", + evev, evev->port, evev->waiting); + + if (!evev->waiting) + return; + + evev->waiting = 0; + LIBXL_LIST_REMOVE(evev, entry); +} + +/* * waiting for device state */ diff --git a/tools/libxl/libxl_internal.h b/tools/libxl/libxl_internal.h index 5d2e651..c519abc 100644 --- a/tools/libxl/libxl_internal.h +++ b/tools/libxl/libxl_internal.h @@ -197,6 +197,17 @@ struct libxl__ev_xswatch { uint32_t counterval; }; +typedef struct libxl__ev_evtchn libxl__ev_evtchn; +typedef void libxl__ev_evtchn_callback(libxl__egc *egc, libxl__ev_evtchn*); +struct libxl__ev_evtchn { + /* caller must fill these in, and they must all remain valid */ + libxl__ev_evtchn_callback *callback; + int port; + /* remainder is private for libxl__ev_evtchn_... */ + bool waiting; + LIBXL_LIST_ENTRY(libxl__ev_evtchn) entry; +}; + /* * An entry in the watch_slots table is either: * 1. an entry in the free list, ie NULL or pointer to next free list entry @@ -343,6 +354,10 @@ struct libxl__ctx { uint32_t watch_counter; /* helps disambiguate slot reuse */ libxl__ev_fd watch_efd; + xc_evtchn *xce; /* for waiting use only libxl__ev_evtchn* */ + LIBXL_LIST_HEAD(, libxl__ev_evtchn) evtchns_waiting; + libxl__ev_fd evtchn_efd; + LIBXL_TAILQ_HEAD(libxl__evgen_domain_death_list, libxl_evgen_domain_death) death_list /* sorted by domid */, death_reported; @@ -748,6 +763,39 @@ static inline int libxl__ev_xswatch_isregistered(const libxl__ev_xswatch *xw) /* + * The evtchn facility is one-shot per call t libxl__ev_evtchn_wait. + * You should call some suitable xc bind function on (or to obtain) + * the port, then libxl__ev_evtchn_wait. + * + * When the event is signaled then the callback will be made, once. + * Then you must call libxl__ev_evtchn_wait again, if desired. + * + * You must NOT call xc_evtchn_unmask. wait will do that for you. + * + * Calling libxl__ev_evtchn_cancel will arrange for libxl to disregard + * future occurrences of event. Both libxl__ev_evtchn_wait and + * libxl__ev_evtchn_cancel are idempotent. + * + * (Note of course that an event channel becomes signaled when it is + * first bound, so you will get one call to libxl__ev_evtchn_wait + * "right away"; unless you have won a very fast race, the condition + * you were waiting for won't exist yet so when you check for it + * you'll find you need to call wait again.) + * + * You must not wait on the same port twice at once (that is, with + * two separate libxl__ev_evtchn's). + */ +_hidden int libxl__ev_evtchn_wait(libxl__gc*, libxl__ev_evtchn *evev); +_hidden void libxl__ev_evtchn_cancel(libxl__gc *gc, libxl__ev_evtchn *evev); + +static inline void libxl__ev_evtchn_init(libxl__ev_evtchn *evev) + { evev->waiting = 0; } +static inline bool libxl__ev_evtchn_iswaiting(const libxl__ev_evtchn *evev) + { return evev->waiting; } + +_hidden int libxl__ctx_evtchn_init(libxl__gc *gc); /* for libxl_ctx_alloc */ + +/* * For making subprocesses. This is the only permitted mechanism for * code in libxl to do so. * -- 1.7.10.4