[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] Re: [Xen-devel] [PATCH 14/15] libxl: New API for providing OS events to libxl
On Mon, 5 Dec 2011, Ian Jackson wrote: > We provide a new set of functions and related structures > libxl_osevent_* > which are to be used by event-driven applications to receive > information from libxl about which fds libxl is interested in, and > what timeouts libxl is waiting for, and to pass back to libxl > information about which fds are readable/writeable etc., and which > timeouts have occurred. Ie, low-level events. > > In this patch, this new machinery is still all unused. Callers will > appear in the next patch in the series, which introduces a new API for > applications to receive high-level events about actual domains etc. > > Signed-off-by: Ian Jackson <ian.jackson@xxxxxxxxxxxxx> > --- > tools/libxl/Makefile | 2 +- > tools/libxl/libxl.c | 25 ++ > tools/libxl/libxl.h | 6 + > tools/libxl/libxl_event.c | 711 > ++++++++++++++++++++++++++++++++++++++++++ > tools/libxl/libxl_event.h | 199 ++++++++++++ > tools/libxl/libxl_internal.h | 216 +++++++++++++- > 6 files changed, 1155 insertions(+), 4 deletions(-) > create mode 100644 tools/libxl/libxl_event.c > create mode 100644 tools/libxl/libxl_event.h > > diff --git a/tools/libxl/Makefile b/tools/libxl/Makefile > index 4e0f3fb..3d575b8 100644 > --- a/tools/libxl/Makefile > +++ b/tools/libxl/Makefile > @@ -38,7 +38,7 @@ LIBXL_LIBS += -lyajl > LIBXL_OBJS = flexarray.o libxl.o libxl_create.o libxl_dm.o libxl_pci.o \ > libxl_dom.o libxl_exec.o libxl_xshelp.o > libxl_device.o \ > libxl_internal.o libxl_utils.o libxl_uuid.o > libxl_json.o \ > - libxl_qmp.o $(LIBXL_OBJS-y) > + libxl_qmp.o libxl_event.o $(LIBXL_OBJS-y) > LIBXL_OBJS += _libxl_types.o libxl_flask.o _libxl_types_internal.o > > $(LIBXL_OBJS): CFLAGS += $(CFLAGS_libxenctrl) $(CFLAGS_libxenguest) > $(CFLAGS_libxenstore) $(CFLAGS_libblktapctl) > diff --git a/tools/libxl/libxl.c b/tools/libxl/libxl.c > index 3a8cfe3..58f280c 100644 > --- a/tools/libxl/libxl.c > +++ b/tools/libxl/libxl.c > @@ -60,6 +60,16 @@ int libxl_ctx_alloc(libxl_ctx **pctx, int version, > * only as an initialiser, not as an expression. */ > memcpy(&ctx->lock, &mutex_value, sizeof(ctx->lock)); > > + ctx->osevent_hooks = 0; > + > + ctx->fd_beforepolled = 0; > + LIBXL_LIST_INIT(&ctx->efds); > + LIBXL_TAILQ_INIT(&ctx->etimes); > + > + ctx->watch_slots = 0; > + LIBXL_SLIST_INIT(&ctx->watch_freeslots); > + libxl__ev_fd_init(&ctx->watch_efd); > + > if ( stat(XENSTORE_PID_FILE, &stat_buf) != 0 ) { > LIBXL__LOG_ERRNO(ctx, LIBXL__LOG_ERROR, "Is xenstore daemon > running?\n" > "failed to stat %s", XENSTORE_PID_FILE); > @@ -89,10 +99,25 @@ int libxl_ctx_alloc(libxl_ctx **pctx, int version, > > int libxl_ctx_free(libxl_ctx *ctx) > { > + int i; > + GC_INIT(ctx); > + > if (!ctx) return 0; > + > + for (i = 0; i < ctx->watch_nslots; i++) > + assert(!libxl__watch_slot_contents(gc, i)); > + libxl__ev_fd_deregister(gc, &ctx->watch_efd); > + > + assert(LIBXL_LIST_EMPTY(&ctx->efds)); > + assert(LIBXL_TAILQ_EMPTY(&ctx->etimes)); > + > if (ctx->xch) xc_interface_close(ctx->xch); > libxl_version_info_dispose(&ctx->version_info); > if (ctx->xsh) xs_daemon_close(ctx->xsh); > + > + free(ctx->fd_beforepolled); > + free(ctx->watch_slots); > + > return 0; > } > > diff --git a/tools/libxl/libxl.h b/tools/libxl/libxl.h > index 289dc85..654a5b0 100644 > --- a/tools/libxl/libxl.h > +++ b/tools/libxl/libxl.h > @@ -137,6 +137,7 @@ > #include <xen/sysctl.h> > > #include <libxl_uuid.h> > +#include <_libxl_list.h> > > typedef uint8_t libxl_mac[6]; > #define LIBXL_MAC_FMT "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx" > @@ -222,6 +223,9 @@ enum { > ERROR_BADFAIL = -7, > ERROR_GUEST_TIMEDOUT = -8, > ERROR_TIMEDOUT = -9, > + ERROR_NOT_READY = -10, > + ERROR_OSEVENT_REG_FAIL = -11, > + ERROR_BUFFERFULL = -12, > }; > > #define LIBXL_VERSION 0 > @@ -635,6 +639,8 @@ const char *libxl_lock_dir_path(void); > const char *libxl_run_dir_path(void); > const char *libxl_xenpaging_dir_path(void); > > +#include <libxl_event.h> > + > #endif /* LIBXL_H */ > > /* > diff --git a/tools/libxl/libxl_event.c b/tools/libxl/libxl_event.c > new file mode 100644 > index 0000000..8d4dbf6 > --- /dev/null > +++ b/tools/libxl/libxl_event.c > @@ -0,0 +1,711 @@ > +/* > + * Copyright (C) 2011 Citrix Ltd. > + * > + * This program is free software; you can redistribute it and/or modify > + * it under the terms of the GNU Lesser General Public License as published > + * by the Free Software Foundation; version 2.1 only. with the special > + * exception on linking described in file LICENSE. > + * > + * This program is distributed in the hope that it will be useful, > + * but WITHOUT ANY WARRANTY; without even the implied warranty of > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > + * GNU Lesser General Public License for more details. > + */ > +/* > + * Internal event machinery for use by other parts of libxl > + */ > + > +#include <poll.h> > + > +#include "libxl_internal.h" > + > +/* > + * The counter osevent_in_hook is used to ensure that the application > + * honours the reentrancy restriction documented in libxl_event.h. > + * > + * The application's registration hooks should be called ONLY via > + * these macros, with the ctx locked. Likewise all the "occurred" > + * entrypoints from the application should assert(!in_hook); > + */ > +#define OSEVENT_HOOK_INTERN(defval, hookname, ...) \ > + (CTX->osevent_hooks \ > + ? (CTX->osevent_in_hook++, \ > + CTX->osevent_hooks->hookname(CTX->osevent_user, __VA_ARGS__), \ > + CTX->osevent_in_hook--) \ > + : defval) > + > +#define OSEVENT_HOOK(hookname,...) \ > + OSEVENT_HOOK_INTERN(0, hookname, __VA_ARGS__) > + > +#define OSEVENT_HOOK_VOID(hookname,...) \ > + OSEVENT_HOOK_INTERN((void)0, hookname, __VA_ARGS__) Is there any reasons why we cannot use static inline functions here? > +/* > + * fd events > + */ > + > +int libxl__ev_fd_register(libxl__gc *gc, libxl__ev_fd *ev, > + libxl__ev_fd_callback *func, > + int fd, short events) { > + int rc; > + > + assert(fd >= 0); > + > + CTX_LOCK; > + > + rc = OSEVENT_HOOK(fd_register, fd, &ev->for_app_reg, events, ev); > + if (rc) goto out; > + > + LIBXL_LIST_INSERT_HEAD(&CTX->efds, ev, entry); > + > + ev->fd = fd; > + ev->events = events; > + ev->in_beforepolled = -1; > + ev->func = func; > + > + rc = 0; > + > + out: > + CTX_UNLOCK; > + return rc; > +} > + > +int libxl__ev_fd_modify(libxl__gc *gc, libxl__ev_fd *ev, short events) { > + int rc; > + > + CTX_LOCK; > + assert(libxl__ev_fd_isregistered(ev)); > + > + rc = OSEVENT_HOOK(fd_modify, ev->fd, &ev->for_app_reg, events); > + if (rc) goto out; > + > + ev->events = events; > + > + rc = 0; > + out: > + CTX_UNLOCK; > + return rc; > +} > + > +void libxl__ev_fd_deregister(libxl__gc *gc, libxl__ev_fd *ev) { > + CTX_LOCK; > + > + if (!libxl__ev_fd_isregistered(ev)) > + goto out; > + > + OSEVENT_HOOK_VOID(fd_deregister, ev->fd, ev->for_app_reg); > + LIBXL_LIST_REMOVE(ev, entry); > + ev->fd = -1; > + > + if (ev->in_beforepolled >= 0 && > + ev->in_beforepolled < CTX->fd_beforepolled_used) > + /* remove stale reference */ > + CTX->fd_beforepolled[ev->in_beforepolled] = NULL; > + > + out: > + CTX_UNLOCK; > +} > + > +/* > + * timeouts > + */ > + > + > +int libxl__gettimeofday(libxl__gc *gc, struct timeval *now_r) { > + int rc = gettimeofday(now_r, 0); > + if (rc) { > + LIBXL__LOG_ERRNO(CTX, LIBXL__LOG_ERROR, "gettimeofday failed"); > + return ERROR_FAIL; > + } > + return 0; > +} > + > +static int time_rel_to_abs(libxl__gc *gc, int ms, struct timeval *abs_out) { > + int rc; > + struct timeval additional = { > + .tv_sec = ms / 1000, > + .tv_usec = (ms % 1000) * 1000 > + }; > + struct timeval now; > + > + rc = libxl__gettimeofday(gc, &now); > + if (rc) return rc; > + > + timeradd(&now, &additional, abs_out); > + return 0; > +} > + > +static void time_insert_finite(libxl__gc *gc, libxl__ev_time *ev) { > + libxl__ev_time *evsearch; > + LIBXL_TAILQ_INSERT_SORTED(&CTX->etimes, entry, ev, evsearch, , > + timercmp(&ev->abs, &evsearch->abs, >)); > + ev->infinite = 0; > +} > + > +static int time_register_finite(libxl__gc *gc, libxl__ev_time *ev, > + struct timeval abs) { > + int rc; > + > + rc = OSEVENT_HOOK(timeout_register, &ev->for_app_reg, abs, ev); > + if (rc) return rc; > + > + ev->infinite = 0; > + ev->abs = abs; > + time_insert_finite(gc, ev); > + > + return 0; > +} > + > +static void time_deregister(libxl__gc *gc, libxl__ev_time *ev) { > + if (!ev->infinite) { > + OSEVENT_HOOK_VOID(timeout_deregister, &ev->for_app_reg); > + LIBXL_TAILQ_REMOVE(&CTX->etimes, ev, entry); > + } > +} > + > + > +int libxl__ev_time_register_abs(libxl__gc *gc, libxl__ev_time *ev, > + libxl__ev_time_callback *func, > + struct timeval abs) { > + int rc; > + > + CTX_LOCK; > + > + rc = time_register_finite(gc, ev, abs); > + if (rc) goto out; > + > + ev->func = func; > + > + rc = 0; > + out: > + CTX_UNLOCK; > + return rc; > +} > + > + > +int libxl__ev_time_register_rel(libxl__gc *gc, libxl__ev_time *ev, > + libxl__ev_time_callback *func, > + int milliseconds /* as for poll(2) */) { > + struct timeval abs; > + int rc; > + > + CTX_LOCK; > + > + if (milliseconds < 0) { > + ev->infinite = 1; > + } else { > + rc = time_rel_to_abs(gc, milliseconds, &abs); > + if (rc) goto out; > + > + rc = time_register_finite(gc, ev, abs); > + if (rc) goto out; > + } > + > + ev->func = func; > + rc = 0; > + > + out: > + CTX_UNLOCK; > + return 0; > +} > + > +int libxl__ev_time_modify_abs(libxl__gc *gc, libxl__ev_time *ev, > + struct timeval abs) { > + int rc; > + > + CTX_LOCK; > + > + assert(libxl__ev_time_isregistered(ev)); > + > + if (ev->infinite) { > + rc = time_register_finite(gc, ev, abs); > + if (rc) goto out; > + } else { > + rc = OSEVENT_HOOK(timeout_modify, &ev->for_app_reg, abs); > + if (rc) goto out; > + > + LIBXL_TAILQ_REMOVE(&CTX->etimes, ev, entry); > + ev->abs = abs; > + time_insert_finite(gc, ev); > + } > + > + rc = 0; > + out: > + CTX_UNLOCK; > + return rc; > +} > + > +int libxl__ev_time_modify_rel(libxl__gc *gc, libxl__ev_time *ev, > + int milliseconds) { > + struct timeval abs; > + int rc; > + > + CTX_LOCK; > + > + assert(libxl__ev_time_isregistered(ev)); > + > + if (milliseconds < 0) { > + time_deregister(gc, ev); > + ev->infinite = 1; > + rc = 0; > + goto out; > + } > + > + rc = time_rel_to_abs(gc, milliseconds, &abs); > + if (rc) goto out; > + > + rc = libxl__ev_time_modify_abs(gc, ev, abs); > + if (rc) goto out; > + > + rc = 0; > + out: > + CTX_UNLOCK; > + return 0; > +} > + > +void libxl__ev_time_deregister(libxl__gc *gc, libxl__ev_time *ev) { > + CTX_LOCK; > + > + if (!libxl__ev_time_isregistered(ev)) > + goto out; > + > + time_deregister(gc, ev); > + ev->func = 0; > + > + out: > + CTX_UNLOCK; > + return; > +} > + > + > +/* > + * xenstore watches > + */ > + > +libxl__ev_xswatch *libxl__watch_slot_contents(libxl__gc *gc, int slotnum) { > + libxl__ev_watch_slot *slot = &CTX->watch_slots[slotnum]; > + libxl__ev_watch_slot *slotcontents = LIBXL_SLIST_NEXT(slot, empty); > + > + if (slotcontents == NULL || > + ((uintptr_t)slotcontents >= (uintptr_t)CTX->watch_slots && > + (uintptr_t)slotcontents < (uintptr_t)(CTX->watch_slots + > + CTX->watch_nslots))) > + /* An empty slot has either a NULL pointer (end of the > + * free list), or a pointer to another entry in the array. > + * So we can do a bounds check to distinguish empty from > + * full slots. > + */ > + /* We need to do the comparisons as uintptr_t because > + * comparing pointers which are not in the same object is > + * undefined behaviour; if the compiler managed to figure > + * out that watch_slots[0..watch_nslots-1] is all of the > + * whole array object it could prove that the above bounds > + * check was always true if it was legal, and remove it! > + * > + * uintptr_t because even on a machine with signed > + * pointers, objects do not cross zero; whereas on > + * machines with unsigned pointers, they may cross > + * 0x8bazillion. > + */ > + return NULL; > + > + /* see comment near libxl__ev_watch_slot definition */ > + return (void*)slotcontents; > +} > + > +static void watchfd_callback(libxl__gc *gc, libxl__ev_fd *ev, > + int fd, short events, short revents) { > + for (;;) { > + char **event = xs_check_watch(CTX->xsh); > + if (!event) { > + if (errno == EAGAIN) break; > + if (errno == EINTR) continue; > + LIBXL__EVENT_DISASTER(gc, "cannot check/read watches", errno, 0); > + return; > + } > + > + const char *epath = event[0]; > + const char *token = event[1]; > + int slotnum; > + uint32_t counterval; OK, this is the last time I am going to point this out, but epath, token, etc, should be declared above, at the beginning of the block. > + int rc = sscanf(token, "%d/%"SCNx32, &slotnum, &counterval); > + if (rc != 2) { > + LIBXL__LOG(CTX, LIBXL__LOG_ERROR, > + "watch epath=%s token=%s: failed to parse token", > + epath, token); > + /* oh well */ > + goto ignore; > + } > + if (slotnum < 0 || slotnum >= CTX->watch_nslots) { > + /* perhaps in the future we will make the watchslots array > shrink */ > + LIBXL__LOG(CTX, LIBXL__LOG_DEBUG, "watch epath=%s token=%s:" > + " slotnum %d out of range [0,%d>", > + epath, token, slotnum, CTX->watch_nslots); > + goto ignore; > + } > + > + libxl__ev_xswatch *w = libxl__watch_slot_contents(gc, slotnum); > + > + if (!w) { > + LIBXL__LOG(CTX, LIBXL__LOG_DEBUG, > + "watch epath=%s token=%s: empty slot", > + epath, token); > + goto ignore; > + } > + > + if (w->counterval != counterval) { > + LIBXL__LOG(CTX, LIBXL__LOG_DEBUG, > + "watch epath=%s token=%s: counter != %"PRIx32, > + epath, token, w->counterval); > + goto ignore; > + } > + > + /* Now it's possible, though unlikely, that this was an event > + * from a previous use of the same slot with the same counterval. > + * > + * In that case either: > + * - the event path is a child of the watch path, in > + * which case this watch would really have generated this > + * event if it had been registered soon enough and we are > + * OK to give this possibly-spurious event to the caller; or > + * - it is not, in which case we must suppress it as the > + * caller should not see events for unrelated paths. > + * > + * See also docs/misc/xenstore.txt. > + */ > + size_t epathlen = strlen(epath); > + size_t wpathlen = strlen(w->path); > + if (epathlen < wpathlen || > + memcmp(epath, w->path, wpathlen) || > + (epathlen > wpathlen && epath[wpathlen] != '/')) { > + LIBXL__LOG(CTX, LIBXL__LOG_DEBUG, > + "watch epath=%s token=%s: not child of wpath=%s", > + epath, token, w->path); > + goto ignore; > + } > + > + /* At last, we have checked everything! */ > + LIBXL__LOG(CTX, LIBXL__LOG_DEBUG, > + "watch event: epath=%s token=%s wpath=%s w=%p", > + epath, token, w->path, w); > + w->callback(gc, w, w->path, epath); > + > + ignore: > + free(event); > + } > +} > + > +static char *watch_token(libxl__gc *gc, int slotnum, uint32_t counterval) { > + return libxl__sprintf(gc, "%d/%"PRIx32, slotnum, counterval); > +} > + > +int libxl__ev_xswatch_register(libxl__gc *gc, libxl__ev_xswatch *w, > + libxl__ev_xswatch_callback *func, > + const char *path /* copied */) { > + libxl__ev_watch_slot *use = NULL; > + char *path_copy = NULL; > + int rc; > + > + CTX_LOCK; > + > + if (!libxl__ev_fd_isregistered(&CTX->watch_efd)) { > + rc = libxl__ev_fd_register(gc, &CTX->watch_efd, watchfd_callback, > + xs_fileno(CTX->xsh), POLLIN); > + if (rc) goto out_rc; > + } > + > + if (LIBXL_SLIST_EMPTY(&CTX->watch_freeslots)) { > + /* Free list is empty so there is not in fact a linked > + * free list in the array and we can safely realloc it */ > + int newarraysize = (CTX->watch_nslots + 1) << 2; > + int i; > + libxl__ev_watch_slot *newarray = > + realloc(CTX->watch_slots, sizeof(*newarray) * newarraysize); > + if (!newarray) goto out_nomem; > + for (i=CTX->watch_nslots; i<newarraysize; i++) does not follow the CODING_STYLE, it should be for (i = CTX->watch_nslots; i < newarraysize; i++) > + LIBXL_SLIST_INSERT_HEAD(&CTX->watch_freeslots, > + &newarray[i], empty); > + CTX->watch_slots = newarray; > + CTX->watch_nslots = newarraysize; > + } would it make sense to move this code in its own allocate_watch_slots function? > + use = LIBXL_SLIST_FIRST(&CTX->watch_freeslots); > + assert(use); > + LIBXL_SLIST_REMOVE_HEAD(&CTX->watch_freeslots, empty); > + > + path_copy = strdup(path); > + if (!path_copy) goto out_nomem; > + > + int slotnum = use - CTX->watch_slots; > + w->counterval = CTX->watch_counter++; > + > + if (!xs_watch(CTX->xsh, path, watch_token(gc, slotnum, w->counterval))) { > + LIBXL__LOG_ERRNOVAL(CTX, LIBXL__LOG_ERROR, errno, > + "create watch for path %s", path); > + rc = ERROR_FAIL; > + goto out_rc; > + } > + > + w->slotnum = slotnum; > + w->path = path_copy; > + w->callback = func; > + /* we look a bit behind the curtain of LIBXL_SLIST, to explictly > + * assign to the pointer that's the next link. See the comment > + * by the definitionn of libxl__ev_watch_slot */ > + use->empty.sle_next = (void*)w; > + > + CTX_UNLOCK; > + return 0; > + > + out_nomem: > + rc = ERROR_NOMEM; > + out_rc: > + if (use) > + LIBXL_SLIST_INSERT_HEAD(&CTX->watch_freeslots, use, empty); > + > + free(w->path); > + w->path = NULL; > + > + CTX_UNLOCK; > +} > + > +/* > + * osevent poll > + */ > + > +int libxl_osevent_beforepoll(libxl_ctx *ctx, int *nfds_io, > + struct pollfd *fds, int *timeout_upd, > + struct timeval now) { > + libxl__ev_fd *efd; > + int i; > + > + /* > + * In order to be able to efficiently find the libxl__ev_fd > + * for a struct poll during _afterpoll, we maintain a shadow > + * data structure in CTX->fd_beforepolled: each slot in > + * the fds array corresponds to a slot in fd_beforepolled. > + */ > + > + GC_INIT(ctx); > + CTX_LOCK; > + > + if (*nfds_io) { > + /* > + * As an optimisation, we don't touch fd_beforepolled_used > + * if *nfds_io is zero on entry, since in that case the > + * caller just wanted to know how big an array to give us. > + * > + * If !*nfds_io, the unconditional parts below are guaranteed > + * not to mess with fd_beforepolled... or any in_beforepolled. > + */ > + > + /* Remove all the old references into beforepolled */ > + for (i = 0; i < CTX->fd_beforepolled_used; i++) { > + efd = CTX->fd_beforepolled[i]; > + if (efd) { > + assert(efd->in_beforepolled == i); > + efd->in_beforepolled = -1; > + CTX->fd_beforepolled[i] = NULL; > + } > + } > + CTX->fd_beforepolled_used = 0; > + > + /* make sure our array is as big as *nfds_io */ > + if (CTX->fd_beforepolled_allocd < *nfds_io) { > + assert(*nfds_io < INT_MAX / sizeof(libxl__ev_fd*) / 2); > + libxl__ev_fd **newarray = > + realloc(CTX->fd_beforepolled, sizeof(*newarray) * *nfds_io); > + if (!newarray) > + return ERROR_NOMEM; > + CTX->fd_beforepolled = newarray; > + CTX->fd_beforepolled_allocd = *nfds_io; > + } > + } > + > + int used = 0; > + LIBXL_LIST_FOREACH(efd, &CTX->efds, entry) { > + if (used < *nfds_io) { > + fds[used].fd = efd->fd; > + fds[used].events = efd->events; > + fds[used].revents = 0; > + CTX->fd_beforepolled[used] = efd; > + efd->in_beforepolled = used; > + } > + used++; > + } > + int rc = used <= *nfds_io ? 0 : ERROR_BUFFERFULL; > + > + if (*nfds_io) { > + CTX->fd_beforepolled_used = used; > + } > + > + *nfds_io = used; > + > + libxl__ev_time *etime = LIBXL_TAILQ_FIRST(&CTX->etimes); > + if (etime) { > + int our_timeout; > + struct timeval rel; > + static struct timeval zero; > + > + timersub(&etime->abs, &now, &rel); > + > + if (timercmp(&rel, &zero, <)) { > + our_timeout = 0; > + } else if (rel.tv_sec >= 2000000) { > + our_timeout = 2000000000; > + } else { > + our_timeout = rel.tv_sec * 1000 + (rel.tv_usec + 999) / 1000; > + } > + if (*timeout_upd < 0 || our_timeout < *timeout_upd) > + *timeout_upd = our_timeout; > + } > + > + CTX_UNLOCK; > + GC_FREE; > + return rc; > +} > + > +void libxl_osevent_afterpoll(libxl_ctx *ctx, int nfds, const struct pollfd > *fds, > + struct timeval now) { > + int i; > + GC_INIT(ctx); > + CTX_LOCK; > + > + assert(nfds <= CTX->fd_beforepolled_used); > + > + for (i = 0; i < nfds; i++) { > + if (!fds[i].revents) > + continue; > + > + libxl__ev_fd *efd = CTX->fd_beforepolled[i]; > + if (!efd) > + continue; > + > + assert(efd->in_beforepolled == i); > + assert(fds[i].fd == efd->fd); > + > + int revents = fds[i].revents & efd->events; > + if (!revents) > + continue; > + > + efd->func(gc, efd, efd->fd, efd->events, revents); > + } > + > + for (;;) { > + libxl__ev_time *etime = LIBXL_TAILQ_FIRST(&CTX->etimes); > + if (!etime) > + break; > + > + assert(!etime->infinite); > + > + if (timercmp(&etime->abs, &now, >)) > + break; > + > + time_deregister(gc, etime); > + > + etime->func(gc, etime, &etime->abs); > + } > + > + CTX_UNLOCK; > + GC_FREE; > +} > + > + > +/* > + * osevent hook and callback machinery > + */ > + > +void libxl_osevent_register_hooks(libxl_ctx *ctx, > + const libxl_osevent_hooks *hooks, > + void *user) { > + GC_INIT(ctx); > + CTX_LOCK; > + ctx->osevent_hooks = hooks; > + ctx->osevent_user = user; > + CTX_UNLOCK; > + GC_FREE; > +} > + > + > +void libxl_osevent_occurred_fd(libxl_ctx *ctx, void *for_libxl, > + int fd, short events, short revents) { > + libxl__ev_fd *ev = for_libxl; > + > + GC_INIT(ctx); > + CTX_LOCK; > + assert(!CTX->osevent_in_hook); > + > + assert(fd == ev->fd); > + revents &= ev->events; > + if (revents) > + ev->func(gc, ev, fd, ev->events, revents); > + > + CTX_UNLOCK; > + GC_FREE; > +} > + > +void libxl_osevent_occurred_timeout(libxl_ctx *ctx, void *for_libxl) { > + libxl__ev_time *ev = for_libxl; > + > + GC_INIT(ctx); > + CTX_LOCK; > + assert(!CTX->osevent_in_hook); > + > + assert(!ev->infinite); > + LIBXL_TAILQ_REMOVE(&CTX->etimes, ev, entry); > + ev->func(gc, ev, &ev->abs); > + > + CTX_UNLOCK; > + GC_FREE; > +} > + > +void libxl__event_disaster(libxl__gc *gc, const char *msg, int errnoval, > + libxl_event_type type /* may be 0 */, > + const char *file, int line, const char *func) { > + libxl__log(CTX, XTL_CRITICAL, errnoval, file, line, func, > + "DISASTER in event loop: %s%s%s%s", > + msg, > + type ? " (relates to event type " : "", > + type ? libxl_event_type_to_string(type) : "", > + type ? ")" : ""); > + > + /* > + * FIXME: This should call the "disaster" hook supplied to > + * libxl_event_register_callbacks, which will be introduced in the > + * next patch. > + */ > + > + const char verybad[] = > + "DISASTER in event loop not handled by libxl application"; > + LIBXL__LOG(CTX, XTL_CRITICAL, verybad); > + fprintf(stderr, "libxl: fatal error, exiting program: %s\n", verybad); > + exit(-1); > +} > + > +/* > + * Local variables: > + * mode: C > + * c-basic-offset: 4 > + * indent-tabs-mode: nil > + * End: > + */ > diff --git a/tools/libxl/libxl_event.h b/tools/libxl/libxl_event.h > new file mode 100644 > index 0000000..25efbdf > --- /dev/null > +++ b/tools/libxl/libxl_event.h > @@ -0,0 +1,199 @@ > +/* > + * Copyright (C) 2011 Citrix Ltd. > + * Author Ian Jackson <ian.jackson@xxxxxxxxxxxxx> > + * > + * This program is free software; you can redistribute it and/or modify > + * it under the terms of the GNU Lesser General Public License as published > + * by the Free Software Foundation; version 2.1 only. with the special > + * exception on linking described in file LICENSE. > + * > + * This program is distributed in the hope that it will be useful, > + * but WITHOUT ANY WARRANTY; without even the implied warranty of > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > + * GNU Lesser General Public License for more details. > + */ > + > +#ifndef LIBXL_EVENT_H > +#define LIBXL_EVENT_H > + > +#include <libxl.h> > + > + > +/*======================================================================*/ > + > +/* > + * OS event handling - passing low-level OS events to libxl > + * > + * Event-driven programs must use these facilities to allow libxl > + * to become aware of readability/writeability of file descriptors > + * and the occurrence of timeouts. > + * > + * There are two approaches available. The first is appropriate for > + * simple programs handling reasonably small numbers of domains: > + * > + * for (;;) { > + * libxl_osevent_beforepoll(...) > + * poll(); > + * libxl_osevent_afterpoll(...); > + * for (;;) { > + * r=libxl_event_check(...); > + * if (r==LIBXL_NOT_READY) break; > + * if (r) handle failure; > + * do something with the event; > + * } > + * } > + * > + * The second approach uses libxl_osevent_register_hooks and is > + * suitable for programs which are already using a callback-based > + * event library. > + * > + * An application may freely mix the two styles of interaction. > + */ > + > +struct pollfd; > + > +int libxl_osevent_beforepoll(libxl_ctx *ctx, int *nfds_io, > + struct pollfd *fds, int *timeout_upd, > + struct timeval now); > + /* The caller should provide beforepoll with some space for libxl's > + * fds, and tell libxl how much space is available by setting *nfds_io. > + * fds points to the start of this space (and fds may be a pointer into > + * a larger array, for example, if the application has some fds of > + * its own that it is interested in). > + * > + * On return *nfds_io will in any case have been updated by libxl > + * according to how many fds libxl wants to poll on. > + * > + * If the space was sufficient, libxl fills in fds[0..<new > + * *nfds_io>] suitably for poll(2), updates *timeout_upd if needed, > + * and returns ok. > + * > + * If space was insufficient, fds[0..<old *nfds_io>] is undefined on > + * return; *nfds_io on return will be greater than the value on > + * entry; *timeout_upd may or may not have been updated; and > + * libxl_osevent_beforepoll returns ERROR_BUFERFULL. In this case > + * the application needs to make more space (enough space for > + * *nfds_io struct pollfd) and then call beforepoll again, before > + * entering poll(2). Typically this will involve calling realloc. > + * > + * The application may call beforepoll with fds==NULL and > + * *nfds_io==0 in order to find out how much space is needed. > + * > + * *timeout_upd is as for poll(2): it's in milliseconds, and > + * negative values mean no timeout (infinity). > + * libxl_osevent_beforepoll will only reduce the timeout, naturally. > + */ so far we have always added the comment on a function above the declaration of the function; we should keep doing it for consistency > +void libxl_osevent_afterpoll(libxl_ctx *ctx, int nfds, const struct pollfd > *fds, > + struct timeval now); > + /* nfds and fds[0..nfds] must be from the most recent call to > + * _beforepoll, as modified by poll. > + * > + * This function actually performs all of the IO and other actions, > + * and generates events (libxl_event), which are implied by either > + * (a) the time of day or (b) both (i) the returned information from > + * _beforepoll, and (ii) the results from poll specified in > + * fds[0..nfds-1]. Generated events can then be retrieved by > + * libxl_event_check. > + */ > + I see that the implementation of libxl_event_check is actually missing from this patch. Is this patch supposed to compiled, even without the actual libxl_event generation? Or are the two patches have to be committed together? > +typedef struct libxl_osevent_hooks { > + int (*fd_register)(void *uselibxl_event_check.r, int fd, void > **for_app_registration_out, > + short events, void *for_libxl); > + int (*fd_modify)(void *user, int fd, void **for_app_registration_update, > + short events); > + void (*fd_deregister)(void *user, int fd, void *for_app_registration); > + int (*timeout_register)(void *user, void **for_app_registration_out, > + struct timeval abs, void *for_libxl); > + int (*timeout_modify)(void *user, void **for_app_registration_update, > + struct timeval abs); > + void (*timeout_deregister)(void *user, void *for_app_registration_io); > +} libxl_osevent_hooks; > + > +void libxl_osevent_register_hooks(libxl_ctx *ctx, > + const libxl_osevent_hooks *hooks, > + void *user); > + /* The application which calls register_fd_hooks promises to > + * maintain a register of fds and timeouts that libxl is interested > + * in, and make calls into libxl (libxl_osevent_occurred_*) > + * when those fd events and timeouts occur. This is more efficient > + * than _beforepoll/_afterpoll if there are many fds (which can > + * happen if the same libxl application is managing many domains). > + * > + * For an fd event, events is as for poll(). register or modify may > + * be called with events==0, in which case it must still work > + * normally, just not generate any events. > + * > + * For a timeout event, milliseconds is as for poll(). > + * Specifically, negative values of milliseconds mean NO TIMEOUT. > + * This is used by libxl to temporarily disable a timeout. > + * > + * If the register or modify hook succeeds it may update > + * *for_app_registration_out/_update and must then return 0. > + * On entry to register, *for_app_registration_out is always NULL. > + * > + * A registration or modification hook may fail, in which case it > + * must leave the registration state of the fd or timeout unchanged. > + * It may then either return ERROR_OSEVENT_REG_FAIL or any positive > + * int. The value returned will be passed up through libxl and > + * eventually returned back to the application. When register > + * fails, any value stored into *for_registration_out is ignored by > + * libxl; when modify fails, any changed value stored into > + * *for_registration_update is honoured by libxl and will be passed > + * to future modify or deregister calls. > + * > + * libxl will only attempt to register one callback for any one fd. > + * libxl will remember the value stored in *for_app_registration_io > + * by a successful call to register or modify and pass it into > + * subsequent calls to modify or deregister. > + * > + * register_fd_hooks may be called only once for each libxl_ctx. > + * libxl may make calls to register/modify/deregister from within > + * any libxl function (indeed, it will usually call register from > + * register_event_hooks). Conversely, the application MUST NOT make > + * the event occurrence calls (libxl_osevent_occurred_*) into libxl > + * reentrantly from within libxl (for example, from within the > + * register/modify functions). > + * > + * Lock hierarchy: the register/modify/deregister functions may be > + * called with locks held. These locks (the "libxl internal locks") > + * are inside the libxl_ctx. Therefore, if those register functions > + * acquire any locks of their own ("caller register locks") outside > + * libxl, to avoid deadlock one of the following must hold for each > + * such caller register lock: > + * (a) "acquire libxl internal locks before caller register lock": > + * No libxl function may be called with the caller register > + * lock held. > + * (b) "acquire caller register lock before libxl internal locks": > + * No libxl function may be called _without_ the caller > + * register lock held. > + * Of these we would normally recommend (a). > + * > + * The value *hooks is not copied and must outlast the libxl_ctx. > + */ while this description is very verbose, it doesn't contain informations on: - what is void *user; - what is "const libxl_osevent_hooks *hooks", in particular the role of each of these function pointers and the description of their arguments. If I am a user of the library, how am I supposed to pass as user? and as hooks? I think these few lines should go first, then the description of the contract. > +/* It is NOT legal to call _occurred_ reentrantly within any libxl > + * function. Specifically it is NOT legal to call it from within > + * a register callback. Conversely, libxl MAY call register/deregister > + * from within libxl_event_registered_call_*. > + */ > + > +void libxl_osevent_occurred_fd(libxl_ctx *ctx, void *for_libxl, > + int fd, short events, short revents); > + > +void libxl_osevent_occurred_timeout(libxl_ctx *ctx, void *for_libxl); > + /* Implicitly, on entry to this function the timeout has been > + * deregistered. If _occurred_timeout is called, libxl will not > + * call timeout_deregister; if it wants to requeue the timeout it > + * will call timeout_register again. > + */ > + > +#endif > + > +/* > + * Local variables: > + * mode: C > + * c-basic-offset: 4 > + * indent-tabs-mode: nil > + * End: > + */ > diff --git a/tools/libxl/libxl_internal.h b/tools/libxl/libxl_internal.h > index d015c7c..88e7dbb 100644 > --- a/tools/libxl/libxl_internal.h > +++ b/tools/libxl/libxl_internal.h > @@ -24,6 +24,9 @@ > #include <stdlib.h> > #include <string.h> > #include <pthread.h> > +#include <inttypes.h> > +#include <assert.h> > +#include <sys/poll.h> > > #include <xs.h> > #include <xenctrl.h> > @@ -91,6 +94,66 @@ _hidden void libxl__log(libxl_ctx *ctx, xentoollog_level > msglevel, int errnoval, > > /* these functions preserve errno (saving and restoring) */ > > +typedef struct libxl__gc libxl__gc; > + > +typedef struct libxl__ev_fd libxl__ev_fd; > +typedef void libxl__ev_fd_callback(libxl__gc *gc, libxl__ev_fd *ev, > + int fd, short events, short revents); > +struct libxl__ev_fd { > + /* all private for libxl__ev_fd... */ > + LIBXL_LIST_ENTRY(libxl__ev_fd) entry; > + int fd; > + short events; > + int in_beforepolled; /* -1 means not in fd_beforepolled */ > + void *for_app_reg; > + libxl__ev_fd_callback *func; > +}; > + > + > +typedef struct libxl__ev_time libxl__ev_time; > +typedef void libxl__ev_time_callback(libxl__gc *gc, libxl__ev_time *ev, > + const struct timeval *requested_abs); > +struct libxl__ev_time { > + /* all private for libxl__ev_time... */ > + int infinite; /* not registered in list or with app if infinite */ > + LIBXL_TAILQ_ENTRY(libxl__ev_time) entry; > + struct timeval abs; > + void *for_app_reg; > + libxl__ev_time_callback *func; > +}; > + > +typedef struct libxl__ev_xswatch libxl__ev_xswatch; > +typedef void libxl__ev_xswatch_callback(libxl__gc *gc, libxl__ev_xswatch*, > + const char *watch_path, const char *event_path); > +struct libxl__ev_xswatch { > + /* caller should include this in their own struct */ > + /* contents are private to xswatch_register */ > + int slotnum; > + uint32_t counterval; > + char *path; > + libxl__ev_xswatch_callback *callback; > +}; > + > +/* > + * 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 > + * 2. an pointer to a libxl__ev_xswatch > + * > + * But we don't want to use unions or type-punning because the > + * compiler might "prove" that our code is wrong and misoptimise it. > + * > + * The rules say that all struct pointers have identical > + * representation and alignment requirements (C99+TC1+TC2 6.2.5p26) so > + * what we do is simply declare our array as containing only the free > + * list pointers, and explicitly convert from and to our actual > + * xswatch pointers when we store and retrieve them. > + */ > +typedef struct libxl__ev_watch_slot { > + LIBXL_SLIST_ENTRY(struct libxl__ev_watch_slot) empty; > +} libxl__ev_watch_slot; > + > +libxl__ev_xswatch *libxl__watch_slot_contents(libxl__gc *gc, int slotnum); > + > struct libxl__ctx { > xentoollog_logger *lg; > xc_interface *xch; > @@ -108,6 +171,23 @@ struct libxl__ctx { > * documented in the libxl public interface. > */ > > + int osevent_in_hook; > + const libxl_osevent_hooks *osevent_hooks; > + void *osevent_user; > + /* See the comment for OSEVENT_HOOK_INTERN in libxl_event.c > + * for restrictions on the use of the osevent fields. */ > + > + int fd_beforepolled_allocd, fd_beforepolled_used; > + libxl__ev_fd **fd_beforepolled; /* see libxl_osevent_beforepoll */ > + LIBXL_LIST_HEAD(, libxl__ev_fd) efds; > + LIBXL_TAILQ_HEAD(, libxl__ev_time) etimes; > + > + libxl__ev_watch_slot *watch_slots; > + int watch_nslots; > + LIBXL_SLIST_HEAD(, libxl__ev_watch_slot) watch_freeslots; > + uint32_t watch_counter; /* helps disambiguate slot reuse */ > + libxl__ev_fd watch_efd; > + > /* for callers who reap children willy-nilly; caller must only > * set this after libxl_init and before any other call - or > * may leave them untouched */ > @@ -138,12 +218,12 @@ typedef struct { > > #define PRINTF_ATTRIBUTE(x, y) __attribute__((format(printf, x, y))) > > -typedef struct { > +struct libxl__gc { > /* mini-GC */ > int alloc_maxsize; > void **alloc_ptrs; > libxl_ctx *owner; > -} libxl__gc; > +}; > > #define LIBXL_INIT_GC(gc,ctx) do{ \ > (gc).alloc_maxsize = 0; \ > @@ -209,9 +289,137 @@ _hidden char *libxl__xs_read(libxl__gc *gc, > xs_transaction_t t, > _hidden char **libxl__xs_directory(libxl__gc *gc, xs_transaction_t t, > const char *path, unsigned int *nb); > /* On error: returns NULL, sets errno (no logging) */ > - > _hidden char *libxl__xs_libxl_path(libxl__gc *gc, uint32_t domid); > > + > +/* > + * Event generation functions provided by the libxl event core to the > + * rest of libxl. Implemented in terms of _beforepoll/_afterpoll > + * and/or the fd registration machinery, as provided by the > + * application. > + * > + * Semantics are similar to those of the fd and timeout registration > + * functions provided to libxl_osevent_register_hooks. > + * > + * Non-0 returns from libxl__ev_{modify,deregister} have already been > + * logged by the core and should be returned unmodified to libxl's > + * caller; NB that they may be valid libxl error codes but they may > + * also be positive numbers supplied by the caller. > + * > + * In each case, there is a libxl__ev_FOO structure which can be in > + * one of three states: > + * > + * Undefined - Might contain anything. All-bits-zero is > + * an undefined state. > + * > + * Idle - Struct contents are defined enough to pass to any > + * libxl__ev_FOO function but not registered and > + * callback will not be called. The struct does not > + * contain references to any allocated resources so > + * can be thrown away. > + * > + * Active - Request for events has been registered and events > + * may be generated. _deregister must be called to > + * reclaim resources. > + * > + * These functions are provided for each kind of event KIND: > + * > + * int libxl__ev_KIND_register(libxl__gc *gc, libxl__ev_KIND *GEN, > + * libxl__ev_KIND_callback *FUNC, > + * DETAILS); > + * On entry *GEN must be in state Undefined or Idle. > + * Returns a libxl error code; on error return *GEN is Idle. > + * On successful return *GEN is Active and FUNC wil be > + * called by the event machinery in future. FUNC will > + * not be called from within the call to _register. > + * > + * void libxl__ev_KIND_deregister(libxl__gc *gc, libxl__ev_KIND *GEN_upd); > + * On entry *GEN must be in state Active or Idle. > + * On return it is Idle. (Idempotent.) > + * > + * void libxl__ev_KIND_init(libxl__ev_KIND *GEN); > + * Provided for initialising an Undefined KIND. > + * On entry *GEN must be in state Idle or Undefined. > + * On return it is Idle. (Idempotent.) > + * > + * int libxl__ev_KIND_isregistered(const libxl__ev_KIND *GEN); > + * On entry *GEN must be Idle or Active. > + * Returns nonzero if it is Active, zero otherwise. > + * Cannot fail. > + * > + * int libxl__ev_KiND_modify(libxl__gc*, libxl__ev_KIND *GEN, > + * DETAILS); > + * Only provided for some kinds of generator. > + * On entry *GEN must be Active and on return, whether successful > + * or not, it will be Active. > + * Returns a libxl error code; on error the modification > + * is not effective. > + * > + * All of these functions are fully threadsafe and may be called by > + * general code in libxl even from within event callback FUNCs. > + */ > + > + > +_hidden int libxl__ev_fd_register(libxl__gc*, libxl__ev_fd *ev_out, > + libxl__ev_fd_callback*, > + int fd, short events /* as for poll(2) */); > +_hidden int libxl__ev_fd_modify(libxl__gc*, libxl__ev_fd *ev, > + short events); > +_hidden void libxl__ev_fd_deregister(libxl__gc*, libxl__ev_fd *ev); > +static inline void libxl__ev_fd_init(libxl__ev_fd *efd) > + { efd->fd = -1; } > +static inline int libxl__ev_fd_isregistered(libxl__ev_fd *efd) > + { return efd->fd >= 0; } > + > +_hidden int libxl__ev_time_register_rel(libxl__gc*, libxl__ev_time *ev_out, > + libxl__ev_time_callback*, > + int milliseconds /* as for poll(2) > */); > +_hidden int libxl__ev_time_register_abs(libxl__gc*, libxl__ev_time *ev_out, > + libxl__ev_time_callback*, > + struct timeval); > +_hidden int libxl__ev_time_modify_rel(libxl__gc*, libxl__ev_time *ev, > + int milliseconds /* as for poll(2) */); > +_hidden int libxl__ev_time_modify_abs(libxl__gc*, libxl__ev_time *ev, > + struct timeval); > +_hidden void libxl__ev_time_deregister(libxl__gc*, libxl__ev_time *ev); > +static inline void libxl__ev_time_init(libxl__ev_time *ev) > + { ev->func = 0; } > +static inline int libxl__ev_time_isregistered(libxl__ev_time *ev) > + { return !!ev->func; } > + > + > +_hidden int libxl__ev_xswatch_register(libxl__gc*, libxl__ev_xswatch > *xsw_out, > + libxl__ev_xswatch_callback*, > + const char *path /* copied */); > +_hidden void libxl__ev_xswatch_deregister(libxl__gc *gc, libxl__ev_xswatch*); > + > +static inline void libxl__ev_xswatch_init(libxl__ev_xswatch *xswatch_out) > + { xswatch_out->slotnum = -1; } > +static inline int libxl__ev_xswatch_isregistered(const libxl__ev_xswatch *xw) > + { return xw->slotnum >= 0; } > + > + > + > +_hidden void libxl__event_disaster(libxl__gc*, const char *msg, int errnoval, > + libxl_event_type type /* may be 0 */, > + const char *file, int line, > + const char *func); > + /* > + * In general, call this via the macro LIBXL__EVENT_DISASTER. > + * > + * Event-generating functions may call this if they might have > + * wanted to generate an event (either an internal one ie a > + * libxl__ev_FOO_callback or an application event), but are > + * prevented from doing so due to eg lack of memory. > + * > + * NB that this function may return and the caller isn't supposed to > + * then crash, although it may fail (and henceforth leave things in > + * a state where many or all calls fail). > + */ > +#define LIBXL__EVENT_DISASTER(gc, msg, errnoval, type) \ > + libxl__event_disaster(gc, msg, errnoval, type, __FILE__, __LINE__, > __func__) any reason why this shouldn't be a static inline? > + > /* from xl_dom */ > _hidden libxl_domain_type libxl__domain_type(libxl__gc *gc, uint32_t domid); > _hidden int libxl__domain_shutdown_reason(libxl__gc *gc, uint32_t domid); > @@ -536,6 +744,8 @@ _hidden int libxl__parse_mac(const char *s, libxl_mac > mac); > /* compare mac address @a and @b. 0 if the same, -ve if a<b and +ve if a>b */ > _hidden int libxl__compare_macs(libxl_mac *a, libxl_mac *b); > > +_hidden int libxl__gettimeofday(libxl__gc *gc, struct timeval *now_r); > + > #define STRINGIFY(x) #x > #define TOSTRING(x) STRINGIFY(x) > > -- > 1.7.2.5 > > > _______________________________________________ > Xen-devel mailing list > Xen-devel@xxxxxxxxxxxxxxxxxxx > http://lists.xensource.com/xen-devel > _______________________________________________ Xen-devel mailing list Xen-devel@xxxxxxxxxxxxxxxxxxx http://lists.xensource.com/xen-devel
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |