[PATCH 07/36] virclosecallbacks: Add new close callbacks APIs

Peter Krempa pkrempa at redhat.com
Thu Jan 5 16:29:56 UTC 2023


The new APIs store the list of callbacks for a VM inside the
virDomainObj and also allow registering multiple callbacks for a single
domain and also for multiple connections.

For now this code is dormant until each driver using the old APIs is not
refactored to use the new APIs.

Signed-off-by: Peter Krempa <pkrempa at redhat.com>
---
 src/hypervisor/virclosecallbacks.c | 336 +++++++++++++++++++++++++++++
 src/hypervisor/virclosecallbacks.h |  24 +++
 src/libvirt_private.syms           |   5 +
 3 files changed, 365 insertions(+)

diff --git a/src/hypervisor/virclosecallbacks.c b/src/hypervisor/virclosecallbacks.c
index a08464438a..21b97cce12 100644
--- a/src/hypervisor/virclosecallbacks.c
+++ b/src/hypervisor/virclosecallbacks.c
@@ -310,3 +310,339 @@ virCloseCallbacksRun(virCloseCallbacks *closeCallbacks,
     VIR_FREE(list->entries);
     VIR_FREE(list);
 }
+
+
+struct _virCloseCallbacksDomainData {
+    virConnectPtr conn;
+    virCloseCallback cb;
+};
+typedef struct _virCloseCallbacksDomainData virCloseCallbacksDomainData;
+
+
+static void
+virCloseCallbacksDomainDataFree(virCloseCallbacksDomainData* data)
+{
+    g_free(data);
+}
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(virCloseCallbacksDomainData, virCloseCallbacksDomainDataFree);
+
+
+virClass *virCloseCallbacksDomainListClass;
+
+struct _virCloseCallbacksDomainList {
+    virObjectLockable parent;
+
+    GList *callbacks;
+};
+typedef struct _virCloseCallbacksDomainList virCloseCallbacksDomainList;
+
+
+static void
+virCloseCallbacksDomainListDispose(void *obj G_GNUC_UNUSED)
+{
+    virCloseCallbacksDomainList *cc = obj;
+
+    g_list_free_full(cc->callbacks, (GDestroyNotify) virCloseCallbacksDomainDataFree);
+}
+
+
+static int
+virCloseCallbacksDomainListOnceInit(void)
+{
+    if (!(VIR_CLASS_NEW(virCloseCallbacksDomainList, virClassForObjectLockable())))
+        return -1;
+
+    return 0;
+}
+
+VIR_ONCE_GLOBAL_INIT(virCloseCallbacksDomainList);
+
+
+/**
+ * virCloseCallbacksDomainAlloc:
+ *
+ * Allocates and returns a data structure for holding close callback data in
+ * a virDomainObj.
+ */
+virObject *
+virCloseCallbacksDomainAlloc(void)
+{
+    if (virCloseCallbacksDomainListInitialize() < 0)
+        abort();
+
+    return virObjectNew(virCloseCallbacksDomainListClass);
+}
+
+
+/**
+ * virCloseCallbacksDomainAdd:
+ * @vm: domain object
+ * @conn: pointer to the connection which should trigger the close callback
+ * @cb: pointer to the callback function
+ *
+ * Registers @cb as a connection close callback for the @conn connection with
+ * the @vm domain. Duplicate registrations are ignored.
+ *
+ * Caller must hold lock on @vm.
+ */
+void
+virCloseCallbacksDomainAdd(virDomainObj *vm,
+                          virConnectPtr conn,
+                          virCloseCallback cb)
+{
+    virCloseCallbacksDomainList *cc = (virCloseCallbacksDomainList *) vm->closecallbacks;
+
+    if (!conn || !cb)
+        return;
+
+    VIR_WITH_OBJECT_LOCK_GUARD(cc) {
+        virCloseCallbacksDomainData *data;
+        GList *n;
+
+        for (n = cc->callbacks; n; n = n->next) {
+            data = n->data;
+
+            if (data->cb == cb && data->conn == conn)
+                return;
+        }
+
+        data = g_new0(virCloseCallbacksDomainData, 1);
+        data->conn = conn;
+        data->cb = cb;
+
+        cc->callbacks = g_list_prepend(cc->callbacks, data);
+    }
+}
+
+
+/**
+ * virCloseCallbacksDomainMatch:
+ * @data: pointer to a close callback data structure
+ * @conn: connection pointer matched against @data
+ * @cb: callback pointer matched against @data
+ *
+ * Returns true if the @data callback structure matches the requested @conn
+ * and/or @cb parameters. If either of @conn/@cb is NULL it is interpreted as
+ * a wildcard.
+ */
+static bool
+virCloseCallbacksDomainMatch(virCloseCallbacksDomainData *data,
+                             virConnectPtr conn,
+                             virCloseCallback cb)
+{
+    if (conn && cb)
+        return data->conn == conn && data->cb == cb;
+
+    if (conn)
+        return data->conn == conn;
+
+    if (cb)
+        return data->cb == cb;
+
+    return true;
+}
+
+
+/**
+ * virCloseCallbacksDomainIsRegistered:
+ * @vm: domain object
+ * @conn: connection pointer
+ * @cb: callback pointer
+ *
+ * Returns true if @vm has one or more matching (see virCloseCallbacksDomainMatch)
+ * callback(s) registered. Caller must hold lock on @vm.
+ */
+bool
+virCloseCallbacksDomainIsRegistered(virDomainObj *vm,
+                                    virConnectPtr conn,
+                                    virCloseCallback cb)
+{
+    virCloseCallbacksDomainList *cc = (virCloseCallbacksDomainList *) vm->closecallbacks;
+
+    VIR_WITH_OBJECT_LOCK_GUARD(cc) {
+        GList *n;
+
+        for (n = cc->callbacks; n; n = n->next) {
+            virCloseCallbacksDomainData *data = n->data;
+
+            if (virCloseCallbacksDomainMatch(data, conn, cb))
+                return true;
+        }
+    }
+
+    return false;
+}
+
+
+/**
+ * virCloseCallbacksDomainRemove:
+ * @vm: domain object
+ * @conn: connection pointer
+ * @cb: callback pointer
+ *
+ * Removes all the registered matching (see virCloseCallbacksDomainMatch)
+ * callbacks for @vm. Caller must hold lock on @vm.
+ */
+void
+virCloseCallbacksDomainRemove(virDomainObj *vm,
+                             virConnectPtr conn,
+                             virCloseCallback cb)
+{
+    virCloseCallbacksDomainList *cc = (virCloseCallbacksDomainList *) vm->closecallbacks;
+
+    VIR_WITH_OBJECT_LOCK_GUARD(cc) {
+        GList *n = cc->callbacks;
+
+        while (n) {
+            GList *cur = n;
+
+            n = n->next;
+
+            if (virCloseCallbacksDomainMatch(cur->data, conn, cb)) {
+                cc->callbacks = g_list_remove_link(cc->callbacks, cur);
+                g_list_free_full(cur, (GDestroyNotify) virCloseCallbacksDomainDataFree);
+            }
+        }
+    }
+}
+
+
+/**
+ * virCloseCallbacksDomainFetchForConn:
+ * @vm: domain object
+ * @conn: pointer to connection being closed
+ *
+ * Fetches connection close callbacks for @conn from @vm. The fetched close
+ * callbacks are removed from the list of callbacks of @vm. This function
+ * must be called with lock on @vm held. Caller is responsible for freeing the
+ * returned list.
+ */
+static GList *
+virCloseCallbacksDomainFetchForConn(virDomainObj *vm,
+                                    virConnectPtr conn)
+{
+    virCloseCallbacksDomainList *cc = (virCloseCallbacksDomainList *) vm->closecallbacks;
+    GList *conncallbacks = NULL;
+
+    VIR_WITH_OBJECT_LOCK_GUARD(cc) {
+        GList *n;
+
+        for (n = cc->callbacks; n;) {
+            virCloseCallbacksDomainData *data = n->data;
+            GList *cur = n;
+
+            n = n->next;
+
+            if (data->conn == conn) {
+                cc->callbacks = g_list_remove_link(cc->callbacks, cur);
+                conncallbacks = g_list_concat(cur, conncallbacks);
+            }
+        }
+    }
+
+    return conncallbacks;
+}
+
+
+/**
+ * virCloseCallbacksDomainRun
+ * @vm: domain object
+ * @conn: pointer to connection being closed
+ *
+ * Fetches and sequentially calls all connection close callbacks for @conn from
+ * @vm. This function must be called with lock on @vm held.
+ */
+static void
+virCloseCallbacksDomainRun(virDomainObj *vm,
+                           virConnectPtr conn)
+{
+    g_autolist(virCloseCallbacksDomainData) callbacks = NULL;
+    GList *n;
+
+    callbacks = virCloseCallbacksDomainFetchForConn(vm, conn);
+
+    for (n = callbacks; n; n = n->next) {
+        virCloseCallbacksDomainData *data = n->data;
+
+        VIR_DEBUG("vm='%s' cb='%p'", vm->def->name, data->cb);
+
+        (data->cb)(vm, conn);
+    }
+}
+
+
+/**
+ * virCloseCallbacksDomainHasCallbackForConn:
+ * @vm: domain object
+ * @conn: connection being closed
+ *
+ * Returns true if @vm has a callback registered for the @conn connection. This
+ * function doesn't require a lock being held on @vm.
+ */
+static bool
+virCloseCallbacksDomainHasCallbackForConn(virDomainObj *vm,
+                                          virConnectPtr conn)
+{
+    /* we can access vm->closecallbacks as it's a immutable pointer */
+    virCloseCallbacksDomainList *cc = (virCloseCallbacksDomainList *) vm->closecallbacks;
+
+    if (!cc)
+        return false;
+
+    VIR_WITH_OBJECT_LOCK_GUARD(cc) {
+        GList *n;
+
+        for (n = cc->callbacks; n; n = n->next) {
+            virCloseCallbacksDomainData *data = n->data;
+
+            if (data->conn == conn)
+                return true;
+        }
+    }
+
+    return false;
+}
+
+
+/**
+ * virCloseCallbacksDomainRunForConn:
+ * @domains: domain list object
+ * @conn: connection being closed
+ *
+ * Finds all domains in @domains which registered one or more connection close
+ * callbacks for @conn and calls the callbacks. This function is designed to
+ * be called from virDrvConnectClose function of individual drivers.
+ *
+ * To minimize lock contention the function first fetches a list of all domain
+ * objects, then checks whether a connect close callback is actually registered
+ * for the domain object and just then acquires the lock on the VM object.
+ */
+void
+virCloseCallbacksDomainRunForConn(virDomainObjList *domains,
+                                  virConnectPtr conn)
+{
+    virDomainObj **vms = NULL;
+    size_t nvms;
+    size_t i;
+
+    VIR_DEBUG("conn=%p", conn);
+
+    virDomainObjListCollectAll(domains, &vms, &nvms);
+
+    for (i = 0; i < nvms; i++) {
+        virDomainObj *vm = vms[i];
+
+        if (!virCloseCallbacksDomainHasCallbackForConn(vm, conn))
+            continue;
+
+        VIR_WITH_OBJECT_LOCK_GUARD(vm) {
+            /* VIR_WITH_OBJECT_LOCK_GUARD is a for loop, so this break applies to that */
+            if (vm->removing)
+                break;
+
+            virCloseCallbacksDomainRun(vm, conn);
+        }
+    }
+
+    virObjectListFreeCount(vms, nvms);
+}
diff --git a/src/hypervisor/virclosecallbacks.h b/src/hypervisor/virclosecallbacks.h
index 7afb0e5640..b471f6b160 100644
--- a/src/hypervisor/virclosecallbacks.h
+++ b/src/hypervisor/virclosecallbacks.h
@@ -49,3 +49,27 @@ void
 virCloseCallbacksRun(virCloseCallbacks *closeCallbacks,
                      virConnectPtr conn,
                      virDomainObjList *domains);
+
+/* ---- */
+
+virObject *
+virCloseCallbacksDomainAlloc(void);
+
+void
+virCloseCallbacksDomainAdd(virDomainObj *vm,
+                          virConnectPtr conn,
+                          virCloseCallback cb);
+
+void
+virCloseCallbacksDomainRemove(virDomainObj *vm,
+                             virConnectPtr conn,
+                             virCloseCallback cb);
+
+bool
+virCloseCallbacksDomainIsRegistered(virDomainObj *vm,
+                                    virConnectPtr conn,
+                                    virCloseCallback cb);
+
+void
+virCloseCallbacksDomainRunForConn(virDomainObjList *domains,
+                                  virConnectPtr conn);
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
index 8f50f9fa1e..b1fa23729a 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -1607,6 +1607,11 @@ virDomainDriverSetupPersistentDefBlkioParams;


 # hypervisor/virclosecallbacks.h
+virCloseCallbacksDomainAdd;
+virCloseCallbacksDomainAlloc;
+virCloseCallbacksDomainIsRegistered;
+virCloseCallbacksDomainRemove;
+virCloseCallbacksDomainRunForConn;
 virCloseCallbacksGet;
 virCloseCallbacksNew;
 virCloseCallbacksRun;
-- 
2.38.1



More information about the libvir-list mailing list