[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