On 20.12.2018 23:31, John Ferlan wrote:
On 12/13/18 3:03 AM, Nikolay Shirokovskiy wrote:
> In case of active persistent domain snapshot metadata is
> not complete. We save only active configuration and on
> restore use it both for active and inactive configuration.
> Let's fix it and save and restore both in this case.
>
> In case of active transient or inactive domain we have
> only one config and thus everything is good.
>
> By the way this patch fixes config memleak on error paths.
use @config to make it clearer...
I see how/why you combined things and although extracting out the
memleak is a bit difficult, I think it should be done.
Perhaps in the cleanup code to *SaveConfig, you just set @config = NULL
"temporarily" since we know/assume virDomainObjAssignDef was successful
and we wouldn't want the subsequently added virDomainDefFree(config) to
free it on those paths, but we would want it to be free'd on error
paths. Then by using @newConfig in this patch, it's even more obvious
and of course the temporary config = NULL would be removed... Hope this
makes sense.
Ok. I thought of a distinct patch too, but having in mind that adding/removing
code in subsequent patches is not preferred I decided to just note this moment
in the message.
>
> Signed-off-by: Nikolay Shirokovskiy <nshirokovskiy(a)virtuozzo.com>
> ---
> src/conf/snapshot_conf.c | 1 +
> src/conf/snapshot_conf.h | 2 ++
> src/qemu/qemu_driver.c | 44 +++++++++++++++++++++++++++++++++++++++-----
> 3 files changed, 42 insertions(+), 5 deletions(-)
>
Not my area of expertise, but rather than have it sit unreviewed...
> diff --git a/src/conf/snapshot_conf.c b/src/conf/snapshot_conf.c
> index 5a511b4..2e4a0b9 100644
> --- a/src/conf/snapshot_conf.c
> +++ b/src/conf/snapshot_conf.c
> @@ -102,6 +102,7 @@ void virDomainSnapshotDefFree(virDomainSnapshotDefPtr def)
> virDomainSnapshotDiskDefClear(&def->disks[i]);
> VIR_FREE(def->disks);
> virDomainDefFree(def->dom);
> + virDomainDefFree(def->persistDom);
> virObjectUnref(def->cookie);
> VIR_FREE(def);
> }
> diff --git a/src/conf/snapshot_conf.h b/src/conf/snapshot_conf.h
> index 20a42bd..3da204a 100644
> --- a/src/conf/snapshot_conf.h
> +++ b/src/conf/snapshot_conf.h
> @@ -75,6 +75,8 @@ struct _virDomainSnapshotDef {
> virDomainSnapshotDiskDef *disks;
>
> virDomainDefPtr dom;
> + /* inactive domain config in case of active persistent domain */
> + virDomainDefPtr persistDom;
>
> virObjectPtr cookie;
>
> diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c
> index f7c1d6f..7e0585d 100644
> --- a/src/qemu/qemu_driver.c
> +++ b/src/qemu/qemu_driver.c
> @@ -15687,6 +15687,12 @@ qemuDomainSnapshotCreateXML(virDomainPtr domain,
>
VIR_DOMAIN_DEF_PARSE_SKIP_VALIDATE)))
> goto endjob;
>
> + if (virDomainObjIsActive(vm) && vm->persistent &&
As it's easy to get lost or forget... IIRC vm->persistent means that w> have
used the Define functions to provide a "config" at some point in
time. Then eventually when/if we modify the "active" object
to make a
configuration specific change, then the @newDef gets populated with the
config change(s); otherwise, the changes are made to the "active" def
and eventually/possibly saved. All the magic is hidden away in
accessors that I don't think about all that often any more.
If by otherwise you mean "if we make changes to active config" - then
such changes are saved to status files but they can't be written
directly to config file.
So, should the vm->persistent be vm->newDef instead? Either way if the
following call is made and vm->newDef == NULL, then eventually in
virDomainDefFormatInternal we would core when @def was NULL.
Well there is at least one case when vm->newDef != NULL but domain is not persistent
- if we undefine domain vm->newDef is not released. This can be fixed but
I'm not sure there are no other cases. So checking vm->persisent is more reliable.
I think this is currenly invariant that if virDomainObjIsActive(vm) &&
vm->persistent
then vm->newDef != NULL but we can add explicit check.
The best option I think is to fix all cases when vm->newDef != NULL for non persistent
active domain and replace check as you suggested. What do you think?
> + !(def->persistDom = qemuDomainDefCopy(driver, vm->newDef,
> + VIR_DOMAIN_XML_SECURE |
> + VIR_DOMAIN_XML_MIGRATABLE)))
Do we need VIR_DOMAIN_XML_INACTIVE too since this is the "next"
persistent config def? A variant for parse is used later in patch5.
This is one of the hardest questions - what combinations of flags one should
use for one's purpuses :) I'm not kidding, there are no good definitions of
VIR_DOMAIN_XML_INACTIVE or VIR_DOMAIN_DEF_FORMAT_INACTIVE. So I use same
combination migration uses to pass persistent config to destination -
check qemuMigrationSrcRun.
Hmm, virDomainDefFormatInternal has next hunk at the beginning:
if (def->id == -1)
flags |= VIR_DOMAIN_DEF_FORMAT_INACTIVE;
and def->id == -1 for inactive config (@id is set to -1 on parsing
inactive configs and on domain start @def is copied to @newDef before
allocating @id - check qemuProcessInit). So we have this flag anyway :)
Also even the above fact does not have the case AFAIU having this flag for inactive
config
does not have much meaning - random hunks of code that I checked filter
dumping info that gained by config on domain start. However I run across a
kind of exception too - @id is not dumped at all if VIR_DOMAIN_DEF_FORMAT_INACTIVE
is set however this is not make much difference.
> + goto endjob;
> +
Since we are doing this as "best chance" and aren't requiring it, what
are your (and perhaps others) thoughts related to making this really
optional. As in if the qemuDomainDefCopy fails, then we just throw a
VIR_INFO, clear the last error, and continue on?
I don't have a preference, but I'm just throwing it out there as an idea.
I should say I'm not looking at this step as "best chance" - if we can
not revert exact state I'd prefer to fail here.
> if (flags & VIR_DOMAIN_SNAPSHOT_CREATE_DISK_ONLY) {
> align_location = VIR_DOMAIN_SNAPSHOT_LOCATION_EXTERNAL;
> align_match = false;
> @@ -16219,6 +16225,7 @@ qemuDomainRevertToSnapshot(virDomainSnapshotPtr snapshot,
> qemuDomainObjPrivatePtr priv;
> int rc;
> virDomainDefPtr config = NULL;
> + virDomainDefPtr persistConfig = NULL;
> virQEMUDriverConfigPtr cfg = NULL;
> virCapsPtr caps = NULL;
> bool was_stopped = false;
> @@ -16226,6 +16233,7 @@ qemuDomainRevertToSnapshot(virDomainSnapshotPtr snapshot,
> virCPUDefPtr origCPU = NULL;
> unsigned int start_flags = VIR_QEMU_PROCESS_START_GEN_VMID;
> qemuDomainAsyncJob jobType = QEMU_ASYNC_JOB_START;
> + virDomainDefPtr newConfig = NULL;
>
> virCheckFlags(VIR_DOMAIN_SNAPSHOT_REVERT_RUNNING |
> VIR_DOMAIN_SNAPSHOT_REVERT_PAUSED |
> @@ -16332,6 +16340,11 @@ qemuDomainRevertToSnapshot(virDomainSnapshotPtr snapshot,
> goto endjob;
> }
>
> + if (snap->def->persistDom &&
> + !(persistConfig = virDomainDefCopy(snap->def->persistDom, caps,
> + driver->xmlopt, NULL, true)))
> + goto endjob;
> +
Based only on my previous comment, for this part I agree especially
since if we were able to save a config, then we'd want to be sure to
restore it.
> cookie = (qemuDomainSaveCookiePtr) snap->def->cookie;
>
> switch ((virDomainState) snap->def->state) {
> @@ -16440,8 +16453,18 @@ qemuDomainRevertToSnapshot(virDomainSnapshotPtr snapshot,
> * failed loadvm attempt? */
> goto endjob;
> }
> - if (config) {
> - virDomainObjAssignDef(vm, config, false, NULL);
> +
> + /* Older versions do not save inactive config in metadata, instead
> + * they use active config for this purpose, so keep this behaviour
> + * for backward compat.
> + */
> + if (persistConfig)
> + VIR_STEAL_PTR(newConfig, persistConfig);
> + else
> + VIR_STEAL_PTR(newConfig, config);
> +
> + if (newConfig) {
> + virDomainObjAssignDef(vm, newConfig, false, NULL);
> virCPUDefFree(priv->origCPU);
> VIR_STEAL_PTR(priv->origCPU, origCPU);
> }
> @@ -16449,8 +16472,10 @@ qemuDomainRevertToSnapshot(virDomainSnapshotPtr snapshot,
> /* Transitions 2, 3 */
> load:
> was_stopped = true;
> - if (config)
> + if (config) {
> virDomainObjAssignDef(vm, config, false, NULL);
> + config = NULL;
This "makes sense" except for the case where @persistConfig == NULL,
then we wouldn't virDomainSaveConfig below as we currently do. So maybe
this should be:
if (!persistConfig)
VIR_STEAL_PTR(newConfig, config);
else
config = NULL;
That way we won't virDomainDefFree @config below (which is what you're
trying to avoid), but we will save @config as we did before unless of
course @persistConfig is going to be used.
Make sense?
Good catch! I think I'll change to:
if (config) {
virDomainObjAssignDef(vm, config, false, NULL);
VIR_STEAL_PTR(newConfig, config);
}
because if we have persistConfig but fail to assing it - means
we are on error path and we won't save newConfig anyway.
> + }
>
> /* No cookie means libvirt which saved the domain was too old to
> * mess up the CPU definitions.
> @@ -16471,6 +16496,11 @@ qemuDomainRevertToSnapshot(virDomainSnapshotPtr snapshot,
> detail);
> if (rc < 0)
> goto endjob;
> +
> + if (persistConfig) {
Perhaps we should point out:
/* This will save the @persistConfig in the vm->newDef where it
* was originally copied from. */
John
> + virDomainObjAssignDef(vm, persistConfig, false, NULL);
> + VIR_STEAL_PTR(newConfig, persistConfig);
> + }
> }
This makes me wonder if virDomainObjAssignDef is too overcomplicated
if we need such comments.
Nikolay
>
> /* Touch up domain state. */
> @@ -16535,8 +16565,10 @@ qemuDomainRevertToSnapshot(virDomainSnapshotPtr snapshot,
> qemuDomainRemoveInactive(driver, vm);
> goto endjob;
> }
> - if (config)
> + if (config) {
> virDomainObjAssignDef(vm, config, false, NULL);
> + VIR_STEAL_PTR(newConfig, config);
> + }
>
> if (flags & (VIR_DOMAIN_SNAPSHOT_REVERT_RUNNING |
> VIR_DOMAIN_SNAPSHOT_REVERT_PAUSED)) {
> @@ -16600,7 +16632,7 @@ qemuDomainRevertToSnapshot(virDomainSnapshotPtr snapshot,
> } else if (snap) {
> snap->def->current = false;
> }
> - if (ret == 0 && config && vm->persistent &&
> + if (ret == 0 && newConfig && vm->persistent &&
> !(ret = virDomainSaveConfig(cfg->configDir, driver->caps,
> vm->newDef ? vm->newDef : vm->def))) {
> detail = VIR_DOMAIN_EVENT_DEFINED_FROM_SNAPSHOT;
> @@ -16616,6 +16648,8 @@ qemuDomainRevertToSnapshot(virDomainSnapshotPtr snapshot,
> virObjectUnref(cfg);
> virNWFilterUnlockFilterUpdates();
> virCPUDefFree(origCPU);
> + virDomainDefFree(config);
> + virDomainDefFree(persistConfig);
>
> return ret;
> }
>