On 1/5/23 10:29 AM, Peter Krempa wrote:
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.
I think you meant "is refactored" rather than "is not refactored"
here.
Signed-off-by: Peter Krempa <pkrempa(a)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;