[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [Xen-devel] [PATCH 20/35] libxl: ao abort: Provide public ao abort request API
Provide libxl_ao_abort. There is machinery to allow an ao to register an interest in abort requests, using a libxl__ao_abortable. This API is not currently very functional: requesting abort will never have any effect. Signed-off-by: Ian Jackson <Ian.Jackson@xxxxxxxxxxxxx> --- v4: Rename from cancel to abort. Actually record aos on aos_inprogress. (Report from Koushik Chakravarty at Citrix.) Do not mark libxl_ao_cancel hidden (!) Abolish ERROR_NOTIMPLEMENTED from libxl_ao_cancel. All operations are supposed to support cancellation. v2: Minor comment improvements --- tools/libxl/libxl.c | 3 + tools/libxl/libxl.h | 57 ++++++++++++++++++ tools/libxl/libxl_event.c | 132 +++++++++++++++++++++++++++++++++++++++++- tools/libxl/libxl_internal.h | 41 ++++++++++++- 4 files changed, 229 insertions(+), 4 deletions(-) diff --git a/tools/libxl/libxl.c b/tools/libxl/libxl.c index 98b94ee..54b398b 100644 --- a/tools/libxl/libxl.c +++ b/tools/libxl/libxl.c @@ -73,6 +73,8 @@ int libxl_ctx_alloc(libxl_ctx **pctx, int version, LIBXL_LIST_INIT(&ctx->evtchns_waiting); libxl__ev_fd_init(&ctx->evtchn_efd); + LIBXL_LIST_INIT(&ctx->aos_inprogress); + LIBXL_TAILQ_INIT(&ctx->death_list); libxl__ev_xswatch_init(&ctx->death_watch); @@ -174,6 +176,7 @@ int libxl_ctx_free(libxl_ctx *ctx) assert(LIBXL_LIST_EMPTY(&ctx->efds)); assert(LIBXL_TAILQ_EMPTY(&ctx->etimes)); assert(LIBXL_LIST_EMPTY(&ctx->evtchns_waiting)); + assert(LIBXL_LIST_EMPTY(&ctx->aos_inprogress)); if (ctx->xch) xc_interface_close(ctx->xch); libxl_version_info_dispose(&ctx->version_info); diff --git a/tools/libxl/libxl.h b/tools/libxl/libxl.h index b113d8c..e52afca 100644 --- a/tools/libxl/libxl.h +++ b/tools/libxl/libxl.h @@ -688,6 +688,11 @@ typedef struct libxl__ctx libxl_ctx; */ #define LIBXL_HAVE_DEVICE_CHANNEL 1 +/* + * LIBXL_HAVE_AO_ABORT indicates the availability of libxl_ao_abort + */ +#define LIBXL_HAVE_AO_ABORT 1 + /* Functions annotated with LIBXL_EXTERNAL_CALLERS_ONLY may not be * called from within libxl itself. Callers outside libxl, who * do not #include libxl_internal.h, are fine. */ @@ -965,6 +970,58 @@ typedef struct { void *for_callback; /* passed to callback */ } libxl_asyncprogress_how; +/* + * It is sometimes possible to abort an asynchronous operation. + * + * libxl_ao_abort searches for an ongoing asynchronous operation whose + * ao_how is identical to *how, and tries to abort it. The return + * values from libxl_ao_abort are as follows: + * + * 0 + * + * The operation was found, and attempts are being made to cut it + * short. However, it may still take some time to stop. It is + * also possible that the operation will nevertheless complete + * successfully. + * + * ERROR_NOTFOUND + * + * No matching ongoing operation was found. This might happen + * for an actual operation if the operation has already completed + * (perhaps on another thread). The call to libxl_ao_abort has + * had no effect. + * + * ERROR_ABORTED + * + * The operation has already been the subject of at least one + * call to libxl_ao_abort. + * + * If the operation was indeed cut short due to the abort request, it + * will complete, at some point in the future, with ERROR_ABORTED. In + * that case, depending on the operation it have performed some of the + * work in question and left the operation half-done. Consult the + * documentation for individual operations. + * + * Note that an aborted operation might still fail for other reasons + * even after the abort was requested. + * + * If your application is multithreaded you must not reuse an + * ao_how->for_event or ao_how->for_callback value (with a particular + * ao_how->callback) unless you are sure that none of your other + * threads are going to abort the previous operation using that + * value; otherwise you risk aborting the wrong operation if the + * intended target of the abort request completes in the meantime. + * + * It is possible to abort even an operation which is being performed + * synchronously, but since in that case how==NULL you had better only + * have one such operation, because it is not possible to tell them + * apart. (And, if you want to do this, obviously the abort would + * have to be requested on a different thread.) + */ +int libxl_ao_abort(libxl_ctx *ctx, const libxl_asyncop_how *how) + LIBXL_EXTERNAL_CALLERS_ONLY; + + #define LIBXL_VERSION 0 /* context functions */ diff --git a/tools/libxl/libxl_event.c b/tools/libxl/libxl_event.c index 6d5f9d8..f9daa55 100644 --- a/tools/libxl/libxl_event.c +++ b/tools/libxl/libxl_event.c @@ -1776,6 +1776,8 @@ void libxl__ao_create_fail(libxl__ao *ao) assert(ao->in_initiator); assert(!ao->complete); assert(!ao->progress_reports_outstanding); + assert(!ao->aborting); + LIBXL_LIST_REMOVE(ao, inprogress_entry); libxl__ao__destroy(CTX, ao); } @@ -1796,7 +1798,7 @@ void libxl__ao_complete(libxl__egc *egc, libxl__ao *ao, int rc) assert(!ao->nested_progeny); ao->complete = 1; ao->rc = rc; - + LIBXL_LIST_REMOVE(ao, inprogress_entry); libxl__ao_complete_check_progress_reports(egc, ao); } @@ -1869,6 +1871,8 @@ libxl__ao *libxl__ao_create(libxl_ctx *ctx, uint32_t domid, "ao %p: create: how=%p callback=%p poller=%p", ao, how, ao->how.callback, ao->poller); + LIBXL_LIST_INSERT_HEAD(&ctx->aos_inprogress, ao, inprogress_entry); + return ao; out: @@ -1922,8 +1926,8 @@ int libxl__ao_inprogress(libxl__ao *ao, sleep(1); /* It's either this or return ERROR_I_DONT_KNOW_WHETHER * _THE_THING_YOU_ASKED_FOR_WILL_BE_DONE_LATER_WHEN - * _YOU_DIDNT_EXPECT_IT, since we don't have any kind of - * cancellation ability. */ + * _YOU_DIDNT_EXPECT_IT, since we don't have a + * synchronous cancellation ability. */ } CTX_UNLOCK; @@ -1941,6 +1945,128 @@ int libxl__ao_inprogress(libxl__ao *ao, } +/* abort requests */ + +static int ao__abort(libxl_ctx *ctx, libxl__ao *parent) +/* Temporarily unlocks ctx, which must be locked exactly once on entry. */ +{ + int rc; + ao__manip_enter(parent); + + if (parent->aborting) { + rc = ERROR_ABORTED; + goto out; + } + + parent->aborting = 1; + + if (LIBXL_LIST_EMPTY(&parent->abortables)) { + LIBXL__LOG(ctx, XTL_DEBUG, + "ao %p: abort requested and noted, but no-one interested", + parent); + rc = 0; + goto out; + } + + /* We keep calling abort hooks until there are none left */ + while (!LIBXL_LIST_EMPTY(&parent->abortables)) { + libxl__egc egc; + LIBXL_INIT_EGC(egc,ctx); + + assert(!parent->complete); + + libxl__ao_abortable *abrt = LIBXL_LIST_FIRST(&parent->abortables); + assert(parent == ao_nested_root(abrt->ao)); + + LIBXL_LIST_REMOVE(abrt, entry); + abrt->registered = 0; + + LIBXL__LOG(ctx, XTL_DEBUG, "ao %p: abrt=%p: aborting", + parent, abrt->ao); + abrt->callback(&egc, abrt, ERROR_ABORTED); + + libxl__ctx_unlock(ctx); + libxl__egc_cleanup(&egc); + libxl__ctx_lock(ctx); + } + + rc = 0; + + out: + ao__manip_leave(ctx, parent); + return rc; +} + +int libxl_ao_abort(libxl_ctx *ctx, const libxl_asyncop_how *how) +{ + libxl__ao *search; + libxl__ctx_lock(ctx); + int rc; + + LIBXL_LIST_FOREACH(search, &ctx->aos_inprogress, inprogress_entry) { + if (how) { + /* looking for ao to be reported by callback or event */ + if (search->poller) + /* sync */ + continue; + if (how->callback != search->how.callback) + continue; + if (how->callback + ? (how->u.for_callback != search->how.u.for_callback) + : (how->u.for_event != search->how.u.for_event)) + continue; + } else { + /* looking for synchronous call */ + if (!search->poller) + /* async */ + continue; + } + goto found; + } + rc = ERROR_NOTFOUND; + goto out; + + found: + rc = ao__abort(ctx, search); + out: + libxl__ctx_unlock(ctx); + return rc; +} + +int libxl__ao_abortable_register(libxl__ao_abortable *abrt) +{ + libxl__ao *ao = abrt->ao; + libxl__ao *root = ao_nested_root(ao); + AO_GC; + + if (root->aborting) { + DBG("ao=%p: preemptively aborting ao_abortable registration %p (root=%p)", + ao, abrt, root); + return ERROR_ABORTED; + } + + DBG("ao=%p, abrt=%p: registering (root=%p)", ao, abrt, root); + LIBXL_LIST_INSERT_HEAD(&root->abortables, abrt, entry); + abrt->registered = 1; + + return 0; +} + +_hidden void libxl__ao_abortable_deregister(libxl__ao_abortable *abrt) +{ + if (!abrt->registered) + return; + + libxl__ao *ao = abrt->ao; + libxl__ao *root __attribute__((unused)) = ao_nested_root(ao); + AO_GC; + + DBG("ao=%p, abrt=%p: deregistering (root=%p)", ao, abrt, root); + LIBXL_LIST_REMOVE(abrt, entry); + abrt->registered = 0; +} + + /* progress reporting */ /* The application indicates a desire to ignore events by passing NULL diff --git a/tools/libxl/libxl_internal.h b/tools/libxl/libxl_internal.h index 3ffefc3..b275ee4 100644 --- a/tools/libxl/libxl_internal.h +++ b/tools/libxl/libxl_internal.h @@ -193,6 +193,40 @@ struct libxl__ev_fd { }; +typedef struct libxl__ao_abortable libxl__ao_abortable; +typedef void libxl__ao_abortable_callback(libxl__egc *egc, + libxl__ao_abortable *ao_abortable, int rc /* ABORTED */); + +struct libxl__ao_abortable { + /* caller must fill this in and it must remain valid */ + libxl__ao *ao; + libxl__ao_abortable_callback *callback; + /* remainder is private for abort machinery */ + bool registered; + LIBXL_LIST_ENTRY(libxl__ao_abortable) entry; + /* + * For nested aos: + * Semantically, abort affects the whole tree of aos, + * not just the parent. + * libxl__ao_abortable.ao refers to the child, so + * that the child callback sees the right ao. (After all, + * it was code dealing with the child that set .ao.) + * But, the abortable is recorded on the "abortables" list + * for the ultimate root ao, so that every possible child + * abort occurs as a result of the abort of the parent. + * We set ao->aborting only in the root. + */ +}; + +_hidden int libxl__ao_abortable_register(libxl__ao_abortable*); +_hidden void libxl__ao_abortable_deregister(libxl__ao_abortable*); + +static inline void libxl__ao_abortable_init + (libxl__ao_abortable *c) { c->registered = 0; } +static inline bool libxl__ao_abortable_isregistered + (const libxl__ao_abortable *c) { return c->registered; } + + typedef struct libxl__ev_time libxl__ev_time; typedef void libxl__ev_time_callback(libxl__egc *egc, libxl__ev_time *ev, const struct timeval *requested_abs, @@ -382,6 +416,8 @@ struct libxl__ctx { LIBXL_LIST_HEAD(, libxl__ev_evtchn) evtchns_waiting; libxl__ev_fd evtchn_efd; + LIBXL_LIST_HEAD(, libxl__ao) aos_inprogress; + LIBXL_TAILQ_HEAD(libxl__evgen_domain_death_list, libxl_evgen_domain_death) death_list /* sorted by domid */, death_reported; @@ -468,12 +504,15 @@ struct libxl__ao { * only in libxl__ao_complete.) */ uint32_t magic; - unsigned constructing:1, in_initiator:1, complete:1, notified:1; + unsigned constructing:1, in_initiator:1, complete:1, notified:1, + aborting:1; int manip_refcnt; libxl__ao *nested_root; int nested_progeny; int progress_reports_outstanding; int rc; + LIBXL_LIST_HEAD(, libxl__ao_abortable) abortables; + LIBXL_LIST_ENTRY(libxl__ao) inprogress_entry; libxl__gc gc; libxl_asyncop_how how; libxl__poller *poller; -- 1.7.10.4 _______________________________________________ Xen-devel mailing list Xen-devel@xxxxxxxxxxxxx http://lists.xen.org/xen-devel
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |