[libvirt RFC 20/24] qemu_snapshot: prepare data for external snapshot deletion
Pavel Hrdina
phrdina at redhat.com
Wed Oct 12 15:19:23 UTC 2022
On Tue, Sep 06, 2022 at 12:19:38PM +0200, Peter Krempa wrote:
> On Tue, Aug 23, 2022 at 18:32:23 +0200, Pavel Hrdina wrote:
> > 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 <phrdina at redhat.com>
> > ---
> > src/qemu/qemu_snapshot.c | 144 ++++++++++++++++++++++++++++++++++++++-
> > 1 file changed, 142 insertions(+), 2 deletions(-)
>
> First a few general notes for all upcoming patches.
>
> 1) All of the non-trivial functions dealing with snapshots should have
> documentation explaing what they do. The snapshot code, as you certainly
> know, is very complex and in many cases un-obvious. Having them will
> save us (or anybody else wanting to maintain the code) some headaches in
> the future.
>
> 2) I'd also suggest that you add a knowledge base internals article
> outlining how snapshot deletion is supposed to work. In case there are
> some user-visible caveats, we also have an user-focused article on
> snapshots ( kbase/snapshots.rst ).
>
> 3) I'll try to assess the code also from the point of view of changes
> needed to implement reversion of snapshots and the intricacies that
> might bring for deletion.
>
> > diff --git a/src/qemu/qemu_snapshot.c b/src/qemu/qemu_snapshot.c
> > index da9b4c30f0..dade5dcea4 100644
> > --- a/src/qemu/qemu_snapshot.c
> > +++ b/src/qemu/qemu_snapshot.c
> > @@ -2232,6 +2232,120 @@ qemuSnapshotRevert(virDomainObj *vm,
> > }
> >
> >
> > +typedef struct {
> > + virDomainMomentObj *parentSnap;
> > + virDomainSnapshotDiskDef *snapDisk;
> > + virDomainDiskDef *domDisk;
> > + virDomainDiskDef *parentDomDisk;
> > + virStorageSource *diskSrc;
> > + virStorageSource *parentDiskSrc;
> > + virStorageSource *prevDiskSrc;
> > + qemuBlockJobData *job;
> > +} qemuSnapshotDeleteExternalData;
> > +
> > +
> > +static virDomainMomentObj*
> > +qemuSnapshotFindParentSnapForDisk(virDomainMomentObj *snap,
> > + virDomainSnapshotDiskDef *snapDisk)
> > +{
> > + virDomainMomentObj *parentSnap = snap->parent;
> > +
> > + while (parentSnap) {
> > + ssize_t i;
> > + virDomainSnapshotDef *parentSnapdef = virDomainSnapshotObjGetDef(parentSnap);
> > +
> > + if (!parentSnapdef)
> > + break;
> > +
> > + for (i = 0; i < parentSnapdef->ndisks; i++) {
> > + virDomainSnapshotDiskDef *parentSnapDisk = &(parentSnapdef->disks[i]);
> > +
> > + if (parentSnapDisk->snapshot != VIR_DOMAIN_SNAPSHOT_LOCATION_NO &&
> > + STREQ(snapDisk->name, parentSnapDisk->name)) {
> > + return parentSnap;
> > + }
> > + }
> > +
> > + parentSnap = parentSnap->parent;
> > + }
> > +
> > + return NULL;
> > +}
> > +
> > +
> > +static GPtrArray*
> > +qemuSnapshotDeleteExternalPrepare(virDomainObj *vm,
> > + virDomainMomentObj *snap)
> > +{
> > + ssize_t i;
> > + virDomainSnapshotDef *snapdef = virDomainSnapshotObjGetDef(snap);
> > + g_autoptr(GPtrArray) ret = g_ptr_array_new_full(0, g_free);
>
> Is there any specific requirement to access these randomly?
> (specifically why GPtrArray is used instead of e.g. a G(S)List?
There is no need to access these randomly but G(S)List doesn't work that
nicely with g_autoptr and dynamically allocated elements. For this case
we would have to use g_list_free_full().
> > + for (i = 0; i < snapdef->ndisks; i++) {
> > + g_autofree qemuSnapshotDeleteExternalData *data = NULL;
> > + virDomainSnapshotDiskDef *snapDisk = &(snapdef->disks[i]);
> > +
> > + if (snapDisk->snapshot == VIR_DOMAIN_SNAPSHOT_LOCATION_NO)
> > + continue;
>
> [...]
>
> > typedef struct _virQEMUMomentReparent virQEMUMomentReparent;
> > struct _virQEMUMomentReparent {
> > const char *dir;
> > @@ -2504,9 +2618,9 @@ qemuSnapshotDeleteValidate(virDomainMomentObj *snap,
> > }
> >
> > if (virDomainSnapshotIsExternal(snap) &&
> > - !(flags & VIR_DOMAIN_SNAPSHOT_DELETE_CHILDREN_ONLY)) {
> > + (flags & VIR_DOMAIN_SNAPSHOT_DELETE_CHILDREN)) {
> > virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
> > - _("deletion of external disk snapshots not supported"));
> > + _("deletion of external disk snapshot with internal children disk snapshots not supported"));
>
> This seems a bit unrelated to this patch.
>
> > return -1;
> > }
> >
> > @@ -2523,6 +2637,8 @@ qemuSnapshotDelete(virDomainObj *vm,
> > int ret = -1;
> > virDomainMomentObj *snap = NULL;
> > bool metadata_only = !!(flags & VIR_DOMAIN_SNAPSHOT_DELETE_METADATA_ONLY);
> > + bool stop_qemu = false;
> > + g_autoptr(GPtrArray) externalData = NULL;
> >
> > virCheckFlags(VIR_DOMAIN_SNAPSHOT_DELETE_CHILDREN |
> > VIR_DOMAIN_SNAPSHOT_DELETE_METADATA_ONLY |
> > @@ -2537,6 +2653,25 @@ qemuSnapshotDelete(virDomainObj *vm,
> > if (!metadata_only) {
> > if (qemuSnapshotDeleteValidate(snap, flags) < 0)
> > goto endjob;
> > +
> > + if (virDomainSnapshotIsExternal(snap) &&
> > + !(flags & (VIR_DOMAIN_SNAPSHOT_DELETE_CHILDREN |
> > + VIR_DOMAIN_SNAPSHOT_DELETE_CHILDREN_ONLY))) {
> > + if (!virDomainObjIsActive(vm)) {
> > + if (qemuProcessStart(NULL, driver, vm, NULL, VIR_ASYNC_JOB_NONE,
> > + NULL, -1, NULL, NULL,
> > + VIR_NETDEV_VPORT_PROFILE_OP_CREATE,
> > + VIR_QEMU_PROCESS_START_PAUSED) < 0) {
>
> Note that a user might attempt to call a 'virsh resume' here. This might
> be a problem especially if you went the route of asynchronously handling
> the deletion.
>
> > + goto endjob;
> > + }
> > +
> > + stop_qemu = true;
> > + }
> > +
> > + externalData = qemuSnapshotDeleteExternalPrepare(vm, snap);
> > + if (!externalData)
> > + goto endjob;
>
> Preferrably this operation will be done before starting qemu to do the
> actual deletion. I understand that it has to be done after, to have
> correct pointers after the copy of the definition needed for the
> startup.
>
> In this instance it probably is still better to do the checks first,
> throw them away if qemu startup is needed and re-do them after, as the
> startup of qemu is a waaay more expensive operation.
Yeah I don't like this ordering as well, I'll check what parts of the
qemuSnapshotDeleteExternalPrepare() can be done without qemu running.
> > + }
> > }
> >
> > if (flags & (VIR_DOMAIN_SNAPSHOT_DELETE_CHILDREN |
> > @@ -2547,6 +2682,11 @@ qemuSnapshotDelete(virDomainObj *vm,
> > }
> >
> > endjob:
> > + if (stop_qemu) {
> > + qemuProcessStop(driver, vm, VIR_DOMAIN_SHUTOFF_SHUTDOWN,
> > + VIR_ASYNC_JOB_NONE, 0);
>
> So definitely user interaction of the VM needs to be forbidden during
> this API because it would be bad if the VM gets terminated here.
Good point, will look into it.
Pavel
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 833 bytes
Desc: not available
URL: <http://listman.redhat.com/archives/libvir-list/attachments/20221012/6f2d74fb/attachment.sig>
More information about the libvir-list
mailing list