From nobody Sat Feb 7 05:01:57 2026 Delivered-To: importer@patchew.org Received-SPF: pass (zohomail.com: domain of redhat.com designates 170.10.129.124 as permitted sender) client-ip=170.10.129.124; envelope-from=libvir-list-bounces@redhat.com; helo=us-smtp-delivery-124.mimecast.com; Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of redhat.com designates 170.10.129.124 as permitted sender) smtp.mailfrom=libvir-list-bounces@redhat.com; dmarc=pass(p=none dis=none) header.from=redhat.com ARC-Seal: i=1; a=rsa-sha256; t=1672936266; cv=none; d=zohomail.com; s=zohoarc; b=b4dy839Yy2bVuzD59UnpjBTfNsRnUcYAWOyZeMOdhS+G8A2KF0ZnuT4lpq4A+Spk98sFrfIGPhbDILAeA1dse/uzP0SGeUgplxNWkCHmn129Tng3URinj5kvUxY1UXTq0X3R0wAp5mUpONhWI8uy9Eoijai2wiVrZTU/v3gpFEw= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1672936266; h=Content-Type:Content-Transfer-Encoding:Date:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:To; bh=fQX8DhhWnMO8vn2VmwhABdewRWK+dtkwrXPj/e+2+FM=; b=WBXFhX5uv7GOeOAeAkgweAW8LlatQC+hMdhIDMFBxIHeH4NkPqXMzEJ0FxkchZm3lNNaxKvxtV8sV/eGT2F2KUSmH/sf5Oj0J/Lywz8vdO1tL3+MlKsf/WlsnUP4J5mem68GLVow0kZPq+O8DZ17C/409ujunkzQoytUh8MQlp0= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of redhat.com designates 170.10.129.124 as permitted sender) smtp.mailfrom=libvir-list-bounces@redhat.com; dmarc=pass header.from= (p=none dis=none) Return-Path: Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) by mx.zohomail.com with SMTPS id 1672936266359491.82953876713395; Thu, 5 Jan 2023 08:31:06 -0800 (PST) Received: from mimecast-mx02.redhat.com (mimecast-mx02.redhat.com [66.187.233.88]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-594-jbTzu4GQPNScE_GwpL4Lhw-1; Thu, 05 Jan 2023 11:30:48 -0500 Received: from smtp.corp.redhat.com (int-mx09.intmail.prod.int.rdu2.redhat.com [10.11.54.9]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx02.redhat.com (Postfix) with ESMTPS id 7340585C6E4; Thu, 5 Jan 2023 16:30:41 +0000 (UTC) Received: from mm-prod-listman-01.mail-001.prod.us-east-1.aws.redhat.com (unknown [10.30.29.100]) by smtp.corp.redhat.com (Postfix) with ESMTP id 5B946492B06; Thu, 5 Jan 2023 16:30:41 +0000 (UTC) Received: from mm-prod-listman-01.mail-001.prod.us-east-1.aws.redhat.com (localhost [IPv6:::1]) by mm-prod-listman-01.mail-001.prod.us-east-1.aws.redhat.com (Postfix) with ESMTP id 51E6E1946589; Thu, 5 Jan 2023 16:30:41 +0000 (UTC) Received: from smtp.corp.redhat.com (int-mx06.intmail.prod.int.rdu2.redhat.com [10.11.54.6]) by mm-prod-listman-01.mail-001.prod.us-east-1.aws.redhat.com (Postfix) with ESMTP id CBE181946A7D for ; Thu, 5 Jan 2023 16:30:35 +0000 (UTC) Received: by smtp.corp.redhat.com (Postfix) id B174C2166B33; Thu, 5 Jan 2023 16:30:35 +0000 (UTC) Received: from speedmetal.redhat.com (ovpn-208-20.brq.redhat.com [10.40.208.20]) by smtp.corp.redhat.com (Postfix) with ESMTP id 130882166B32 for ; Thu, 5 Jan 2023 16:30:34 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1672936264; h=from:from:sender:sender:reply-to:subject:subject:date:date: message-id:message-id:to:to:cc:mime-version:mime-version: content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references:list-id:list-help: list-unsubscribe:list-subscribe:list-post; bh=fQX8DhhWnMO8vn2VmwhABdewRWK+dtkwrXPj/e+2+FM=; b=Dg+fecd7MrpieYobfgAtUsk+M9QQOdExJMBNjQEf+MlFFMol6VwO2ydd+XyGjtAwUdvV2w KKIVBt2YYAFe7OCWyvVNIMRTUDNszsAwqwU32AMdZOnwbF0wQVtvYKVJM3KhZWD1WPqixv qKLFNDG7RACxGShxXdoQZZ04Rvl8a5Y= X-MC-Unique: jbTzu4GQPNScE_GwpL4Lhw-1 X-Original-To: libvir-list@listman.corp.redhat.com From: Peter Krempa To: libvir-list@redhat.com Subject: [PATCH 07/36] virclosecallbacks: Add new close callbacks APIs Date: Thu, 5 Jan 2023 17:29:56 +0100 Message-Id: In-Reply-To: References: MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.1 on 10.11.54.6 X-BeenThere: libvir-list@redhat.com X-Mailman-Version: 2.1.29 Precedence: list List-Id: Development discussions about the libvirt library & tools List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libvir-list-bounces@redhat.com Sender: "libvir-list" X-Scanned-By: MIMEDefang 3.1 on 10.11.54.9 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Content-Transfer-Encoding: quoted-printable X-ZohoMail-DKIM: pass (identity @redhat.com) X-ZM-MESSAGEID: 1672936267795100008 Content-Type: text/plain; charset="utf-8" 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 Reviewed-by: Pavel Hrdina --- 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/vircloseca= llbacks.c index a08464438a..21b97cce12 100644 --- a/src/hypervisor/virclosecallbacks.c +++ b/src/hypervisor/virclosecallbacks.c @@ -310,3 +310,339 @@ virCloseCallbacksRun(virCloseCallbacks *closeCallback= s, 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, virCloseCallbac= ksDomainDataFree); + + +virClass *virCloseCallbacksDomainListClass; + +struct _virCloseCallbacksDomainList { + virObjectLockable parent; + + GList *callbacks; +}; +typedef struct _virCloseCallbacksDomainList virCloseCallbacksDomainList; + + +static void +virCloseCallbacksDomainListDispose(void *obj G_GNUC_UNUSED) +{ + virCloseCallbacksDomainList *cc =3D obj; + + g_list_free_full(cc->callbacks, (GDestroyNotify) virCloseCallbacksDoma= inDataFree); +} + + +static int +virCloseCallbacksDomainListOnceInit(void) +{ + if (!(VIR_CLASS_NEW(virCloseCallbacksDomainList, virClassForObjectLock= able()))) + 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 w= ith + * the @vm domain. Duplicate registrations are ignored. + * + * Caller must hold lock on @vm. + */ +void +virCloseCallbacksDomainAdd(virDomainObj *vm, + virConnectPtr conn, + virCloseCallback cb) +{ + virCloseCallbacksDomainList *cc =3D (virCloseCallbacksDomainList *) vm= ->closecallbacks; + + if (!conn || !cb) + return; + + VIR_WITH_OBJECT_LOCK_GUARD(cc) { + virCloseCallbacksDomainData *data; + GList *n; + + for (n =3D cc->callbacks; n; n =3D n->next) { + data =3D n->data; + + if (data->cb =3D=3D cb && data->conn =3D=3D conn) + return; + } + + data =3D g_new0(virCloseCallbacksDomainData, 1); + data->conn =3D conn; + data->cb =3D cb; + + cc->callbacks =3D 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 =3D=3D conn && data->cb =3D=3D cb; + + if (conn) + return data->conn =3D=3D conn; + + if (cb) + return data->cb =3D=3D cb; + + return true; +} + + +/** + * virCloseCallbacksDomainIsRegistered: + * @vm: domain object + * @conn: connection pointer + * @cb: callback pointer + * + * Returns true if @vm has one or more matching (see virCloseCallbacksDoma= inMatch) + * callback(s) registered. Caller must hold lock on @vm. + */ +bool +virCloseCallbacksDomainIsRegistered(virDomainObj *vm, + virConnectPtr conn, + virCloseCallback cb) +{ + virCloseCallbacksDomainList *cc =3D (virCloseCallbacksDomainList *) vm= ->closecallbacks; + + VIR_WITH_OBJECT_LOCK_GUARD(cc) { + GList *n; + + for (n =3D cc->callbacks; n; n =3D n->next) { + virCloseCallbacksDomainData *data =3D 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 =3D (virCloseCallbacksDomainList *) vm= ->closecallbacks; + + VIR_WITH_OBJECT_LOCK_GUARD(cc) { + GList *n =3D cc->callbacks; + + while (n) { + GList *cur =3D n; + + n =3D n->next; + + if (virCloseCallbacksDomainMatch(cur->data, conn, cb)) { + cc->callbacks =3D g_list_remove_link(cc->callbacks, cur); + g_list_free_full(cur, (GDestroyNotify) virCloseCallbacksDo= mainDataFree); + } + } + } +} + + +/** + * 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 =3D (virCloseCallbacksDomainList *) vm= ->closecallbacks; + GList *conncallbacks =3D NULL; + + VIR_WITH_OBJECT_LOCK_GUARD(cc) { + GList *n; + + for (n =3D cc->callbacks; n;) { + virCloseCallbacksDomainData *data =3D n->data; + GList *cur =3D n; + + n =3D n->next; + + if (data->conn =3D=3D conn) { + cc->callbacks =3D g_list_remove_link(cc->callbacks, cur); + conncallbacks =3D 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 =3D NULL; + GList *n; + + callbacks =3D virCloseCallbacksDomainFetchForConn(vm, conn); + + for (n =3D callbacks; n; n =3D n->next) { + virCloseCallbacksDomainData *data =3D n->data; + + VIR_DEBUG("vm=3D'%s' cb=3D'%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 =3D (virCloseCallbacksDomainList *) vm= ->closecallbacks; + + if (!cc) + return false; + + VIR_WITH_OBJECT_LOCK_GUARD(cc) { + GList *n; + + for (n =3D cc->callbacks; n; n =3D n->next) { + virCloseCallbacksDomainData *data =3D n->data; + + if (data->conn =3D=3D 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 c= lose + * 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 do= main + * objects, then checks whether a connect close callback is actually regis= tered + * for the domain object and just then acquires the lock on the VM object. + */ +void +virCloseCallbacksDomainRunForConn(virDomainObjList *domains, + virConnectPtr conn) +{ + virDomainObj **vms =3D NULL; + size_t nvms; + size_t i; + + VIR_DEBUG("conn=3D%p", conn); + + virDomainObjListCollectAll(domains, &vms, &nvms); + + for (i =3D 0; i < nvms; i++) { + virDomainObj *vm =3D 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 app= lies to that */ + if (vm->removing) + break; + + virCloseCallbacksDomainRun(vm, conn); + } + } + + virObjectListFreeCount(vms, nvms); +} diff --git a/src/hypervisor/virclosecallbacks.h b/src/hypervisor/vircloseca= llbacks.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; --=20 2.38.1