From nobody Tue Dec 16 08:36:40 2025 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=1673027645; cv=none; d=zohomail.com; s=zohoarc; b=Wmccb3I6gNPIzKQspcIc+zc2FMoKOlw8helP/ThxLrahJVIXIgo6Z4KNNgbVtA8THF4kK+AaHJYspEBkVXHshTiVBsw9Sy0ESya7sd4Nu2vXUyrTnHjMFntVYJG+6E77FjoGtfuQkmEbVmYZuyV+P6JD7R4daQ5J13VY6shjAeY= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1673027645; 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=+PKSo7a7FlUl2Cpo9rd8/+ltkNtlrmvfL1zuAgA0olQ=; b=geKc3Ka/wJPfQbpCbCZ2N+L54A9j2QO7k47FbIVKlIuIbC0OYlsVsthrP2BwP5r/Zib4kdmbjDzjPAOmoFRW6Ps2fMXHwBRK77zP40H7TMTKL2JrNGRNsnt0Nzf6O2+/2lsa39K5JbuzlQ6DBmF6I5518bqiGNv7GfTcFHLf0Jk= 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 1673027645427847.3774132797131; Fri, 6 Jan 2023 09:54:05 -0800 (PST) Received: from mimecast-mx02.redhat.com (mx3-rdu2.redhat.com [66.187.233.73]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-132-Gdsi8VEPNMK8OasAhekPAA-1; Fri, 06 Jan 2023 12:52:42 -0500 Received: from smtp.corp.redhat.com (int-mx02.intmail.prod.int.rdu2.redhat.com [10.11.54.2]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx02.redhat.com (Postfix) with ESMTPS id AFD782802E27; Fri, 6 Jan 2023 17:52:34 +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 9B1FB40C1074; Fri, 6 Jan 2023 17:52:34 +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 7BC1919459CF; Fri, 6 Jan 2023 17:52:34 +0000 (UTC) Received: from smtp.corp.redhat.com (int-mx01.intmail.prod.int.rdu2.redhat.com [10.11.54.1]) by mm-prod-listman-01.mail-001.prod.us-east-1.aws.redhat.com (Postfix) with ESMTP id 88D8419459C1 for ; Fri, 6 Jan 2023 17:52:30 +0000 (UTC) Received: by smtp.corp.redhat.com (Postfix) id 7AFFF40C2007; Fri, 6 Jan 2023 17:52:30 +0000 (UTC) Received: from antique-work.redhat.com (ovpn-193-172.brq.redhat.com [10.40.193.172]) by smtp.corp.redhat.com (Postfix) with ESMTP id 17B3D40C2005 for ; Fri, 6 Jan 2023 17:52:29 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1673027644; 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=+PKSo7a7FlUl2Cpo9rd8/+ltkNtlrmvfL1zuAgA0olQ=; b=fYEQFTZiAPcaeETWSveOSDkGYycs4G3aCOgoTOf3iZ0BI/yLyvV2pV6or7ARzH92qI4/gH JxAsDmI85xqtfWqGOHctVygvTx2wKUxNCdug1A0gDXTFVbuFbauZtMShTN8Fytuxs1vRpc bS9yfz22bulDl9pJXbKzrpXyAHoDkdE= X-MC-Unique: Gdsi8VEPNMK8OasAhekPAA-1 X-Original-To: libvir-list@listman.corp.redhat.com From: Pavel Hrdina To: libvir-list@redhat.com Subject: [libvirt PATCH v3 24/32] qemu_snapshot: prepare data for external snapshot deletion Date: Fri, 6 Jan 2023 18:51:59 +0100 Message-Id: <310cd259c190d411c020e55405b1295e19051cbe.1673027206.git.phrdina@redhat.com> In-Reply-To: References: MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.1 on 10.11.54.1 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.2 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: 1673027645842100003 Content-Type: text/plain; charset="utf-8"; x-default="true" In order to save some CPU cycles we will collect all the necessary data to delete external snapshot before we even start. They will be later used by code that deletes the snapshots and updates metadata when needed. With external snapshots we need data that libvirt gets from running QEMU process so if the VM is not running we need to start paused QEMU process for the snapshot deletion and kill at afterwards. Signed-off-by: Pavel Hrdina Reviewed-by: Peter Krempa --- src/qemu/qemu_snapshot.c | 181 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 181 insertions(+) diff --git a/src/qemu/qemu_snapshot.c b/src/qemu/qemu_snapshot.c index 3fd348efd3..c7136efe93 100644 --- a/src/qemu/qemu_snapshot.c +++ b/src/qemu/qemu_snapshot.c @@ -2232,6 +2232,150 @@ qemuSnapshotRevert(virDomainObj *vm, } =20 =20 +typedef struct _qemuSnapshotDeleteExternalData { + virDomainSnapshotDiskDef *snapDisk; /* snapshot disk definition */ + virDomainDiskDef *domDisk; /* VM disk definition */ + virStorageSource *diskSrc; /* source of disk we are deleting */ + virDomainMomentObj *parentSnap; + virDomainDiskDef *parentDomDisk; /* disk definition from snapshot meta= data */ + virStorageSource *parentDiskSrc; /* backing disk source of the @diskSr= c */ + virStorageSource *prevDiskSrc; /* source of disk for which @diskSrc is + backing disk */ + qemuBlockJobData *job; +} qemuSnapshotDeleteExternalData; + + +static void +qemuSnapshotDeleteExternalDataFree(qemuSnapshotDeleteExternalData *data) +{ + if (!data) + return; + + virObjectUnref(data->job); + + g_free(data); +} + + +G_DEFINE_AUTOPTR_CLEANUP_FUNC(qemuSnapshotDeleteExternalData, qemuSnapshot= DeleteExternalDataFree); + + +/** + * qemuSnapshotFindParentSnapForDisk: + * @snap: snapshot object that is about to be deleted + * @snapDisk: snapshot disk definition + * + * Find parent snapshot object that contains snapshot of the @snapDisk. + * It may not be the next snapshot in the snapshot tree as the disk in + * question may be skipped by using VIR_DOMAIN_SNAPSHOT_LOCATION_NO. + * + * Returns virDomainMomentObj* on success or NULL if there is no parent + * snapshot containing the @snapDisk. + * */ +static virDomainMomentObj* +qemuSnapshotFindParentSnapForDisk(virDomainMomentObj *snap, + virDomainSnapshotDiskDef *snapDisk) +{ + virDomainMomentObj *parentSnap =3D snap->parent; + + while (parentSnap) { + ssize_t i; + virDomainSnapshotDef *parentSnapdef =3D virDomainSnapshotObjGetDef= (parentSnap); + + if (!parentSnapdef) + break; + + for (i =3D 0; i < parentSnapdef->ndisks; i++) { + virDomainSnapshotDiskDef *parentSnapDisk =3D &(parentSnapdef->= disks[i]); + + if (parentSnapDisk->snapshot !=3D VIR_DOMAIN_SNAPSHOT_LOCATION= _NO && + STREQ(snapDisk->name, parentSnapDisk->name)) { + return parentSnap; + } + } + + parentSnap =3D parentSnap->parent; + } + + return NULL; +} + + +static GSList* +qemuSnapshotDeleteExternalPrepare(virDomainObj *vm, + virDomainMomentObj *snap) +{ + ssize_t i; + virDomainSnapshotDef *snapdef =3D virDomainSnapshotObjGetDef(snap); + g_autoslist(qemuSnapshotDeleteExternalData) ret =3D NULL; + + for (i =3D 0; i < snapdef->ndisks; i++) { + g_autofree qemuSnapshotDeleteExternalData *data =3D NULL; + virDomainSnapshotDiskDef *snapDisk =3D &(snapdef->disks[i]); + + if (snapDisk->snapshot =3D=3D VIR_DOMAIN_SNAPSHOT_LOCATION_NO) + continue; + + data =3D g_new0(qemuSnapshotDeleteExternalData, 1); + data->snapDisk =3D snapDisk; + + data->domDisk =3D qemuDomainDiskByName(vm->def, snapDisk->name); + if (!data->domDisk) + return NULL; + + data->diskSrc =3D virStorageSourceChainLookupBySource(data->domDis= k->src, + data->snapDisk= ->src, + &data->prevDis= kSrc); + if (!data->diskSrc) + return NULL; + + if (!virStorageSourceIsSameLocation(data->diskSrc, data->snapDisk-= >src)) { + virReportError(VIR_ERR_OPERATION_FAILED, "%s", + _("VM disk source and snapshot disk source are = not the same")); + return NULL; + } + + data->parentDomDisk =3D virDomainDiskByTarget(snapdef->parent.dom, + data->snapDisk->name); + if (!data->parentDomDisk) { + virReportError(VIR_ERR_OPERATION_FAILED, + _("failed to find disk '%s' in snapshot VM XML"= ), + snapDisk->name); + return NULL; + } + + if (virDomainObjIsActive(vm)) { + data->parentDiskSrc =3D data->diskSrc->backingStore; + if (!virStorageSourceIsBacking(data->parentDiskSrc)) { + virReportError(VIR_ERR_OPERATION_FAILED, "%s", + _("failed to find parent disk source in bac= king chain")); + return NULL; + } + + if (!virStorageSourceIsSameLocation(data->parentDiskSrc, data-= >parentDomDisk->src)) { + virReportError(VIR_ERR_OPERATION_FAILED, "%s", + _("snapshot VM disk source and parent disk = source are not the same")); + return NULL; + } + } + + data->parentSnap =3D qemuSnapshotFindParentSnapForDisk(snap, data-= >snapDisk); + + if (data->parentSnap && !virDomainSnapshotIsExternal(data->parentS= nap)) { + virReportError(VIR_ERR_OPERATION_UNSUPPORTED, "%s", + _("deleting external snapshot that has internal= snapshot as parent not supported")); + return NULL; + } + + ret =3D g_slist_prepend(ret, g_steal_pointer(&data)); + } + + ret =3D g_slist_reverse(ret); + + return g_steal_pointer(&ret); +} + + typedef struct _virQEMUMomentReparent virQEMUMomentReparent; struct _virQEMUMomentReparent { const char *dir; @@ -2563,6 +2707,10 @@ qemuSnapshotDelete(virDomainObj *vm, int ret =3D -1; virDomainMomentObj *snap =3D NULL; bool metadata_only =3D !!(flags & VIR_DOMAIN_SNAPSHOT_DELETE_METADATA_= ONLY); + bool stop_qemu =3D false; + qemuDomainObjPrivate *priv =3D vm->privateData; + virQEMUDriver *driver =3D priv->driver; + g_autoslist(qemuSnapshotDeleteExternalData) externalData =3D NULL; =20 virCheckFlags(VIR_DOMAIN_SNAPSHOT_DELETE_CHILDREN | VIR_DOMAIN_SNAPSHOT_DELETE_METADATA_ONLY | @@ -2580,6 +2728,34 @@ qemuSnapshotDelete(virDomainObj *vm, if (!metadata_only) { if (qemuSnapshotDeleteValidate(vm, snap, flags) < 0) goto endjob; + + if (virDomainSnapshotIsExternal(snap) && + !(flags & (VIR_DOMAIN_SNAPSHOT_DELETE_CHILDREN | + VIR_DOMAIN_SNAPSHOT_DELETE_CHILDREN_ONLY))) { + + externalData =3D qemuSnapshotDeleteExternalPrepare(vm, snap); + + if (!virDomainObjIsActive(vm)) { + g_autoslist(qemuSnapshotDeleteExternalData) delData =3D NU= LL; + + if (qemuProcessStart(NULL, driver, vm, NULL, VIR_ASYNC_JOB= _SNAPSHOT, + NULL, -1, NULL, NULL, + VIR_NETDEV_VPORT_PROFILE_OP_CREATE, + VIR_QEMU_PROCESS_START_PAUSED) < 0) { + goto endjob; + } + + stop_qemu =3D true; + + /* Call the prepare again as some data require that the VM= is + * running to get everything we need. */ + delData =3D g_steal_pointer(&externalData); + externalData =3D qemuSnapshotDeleteExternalPrepare(vm, sna= p); + } + + if (!externalData) + goto endjob; + } } =20 if (flags & (VIR_DOMAIN_SNAPSHOT_DELETE_CHILDREN | @@ -2591,6 +2767,11 @@ qemuSnapshotDelete(virDomainObj *vm, } =20 endjob: + if (stop_qemu) { + qemuProcessStop(driver, vm, VIR_DOMAIN_SHUTOFF_SHUTDOWN, + VIR_ASYNC_JOB_SNAPSHOT, 0); + } + virDomainObjEndAsyncJob(vm); =20 return ret; --=20 2.39.0