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

Jonathon Jongsma jjongsma at redhat.com
Fri Jan 6 15:00:44 UTC 2023


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 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;



More information about the libvir-list mailing list