[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


 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.