mirror of
https://gitlab.com/libvirt/libvirt.git
synced 2025-02-02 09:55:18 +00:00
snapshot: use metaroot node to simplify management
This idea was first suggested by Daniel Veillard here: https://www.redhat.com/archives/libvir-list/2011-October/msg00353.html Now that I am about to add more complexity to snapshot listing, it makes sense to avoid code duplication and special casing for domain listing (all snapshots) vs. snapshot listing (descendants); adding a metaroot reduces the number of code lines by having the domain listing turn into a descendant listing of the metaroot. Note that this has one minor pessimization - if we are going to list ALL snapshots without filtering, then virHashForeach is more efficient than recursing through the child relationships; restoring that minor optimization will occur in the next patch. * src/conf/domain_conf.h (_virDomainSnapshotObj) (_virDomainSnapshotObjList): Repurpose some fields. (virDomainSnapshotDropParent): Drop unused parameter. * src/conf/domain_conf.c (virDomainSnapshotObjListGetNames) (virDomainSnapshotObjListCount): Simplify. (virDomainSnapshotFindByName, virDomainSnapshotSetRelations) (virDomainSnapshotDropParent): Match new field semantics. * src/qemu/qemu_driver.c (qemuDomainSnapshotCreateXML) (qemuDomainSnapshotReparentChildren, qemuDomainSnapshotDelete): Adjust clients.
This commit is contained in:
parent
7dcee3f956
commit
06d4a1e429
@ -14272,8 +14272,9 @@ static void virDomainSnapshotObjListCopyNames(void *payload,
|
|||||||
|
|
||||||
if (data->oom)
|
if (data->oom)
|
||||||
return;
|
return;
|
||||||
/* LIST_ROOTS/LIST_DESCENDANTS was handled by caller,
|
/* LIST_ROOTS/LIST_DESCENDANTS was handled by the choice of
|
||||||
* LIST_METADATA is a no-op if we get this far. */
|
* iteration made in the caller, and LIST_METADATA is a no-op if
|
||||||
|
* we get this far. */
|
||||||
if ((data->flags & VIR_DOMAIN_SNAPSHOT_LIST_LEAVES) && obj->nchildren)
|
if ((data->flags & VIR_DOMAIN_SNAPSHOT_LIST_LEAVES) && obj->nchildren)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -14289,32 +14290,12 @@ int virDomainSnapshotObjListGetNames(virDomainSnapshotObjListPtr snapshots,
|
|||||||
char **const names, int maxnames,
|
char **const names, int maxnames,
|
||||||
unsigned int flags)
|
unsigned int flags)
|
||||||
{
|
{
|
||||||
struct virDomainSnapshotNameData data = { 0, 0, maxnames, names, 0 };
|
/* LIST_ROOTS and LIST_DESCENDANTS have the same bit value, but
|
||||||
int i;
|
* opposite semantics. Toggle here to get the correct traversal
|
||||||
|
* on the metaroot. */
|
||||||
data.flags = flags & ~VIR_DOMAIN_SNAPSHOT_LIST_ROOTS;
|
flags ^= VIR_DOMAIN_SNAPSHOT_LIST_ROOTS;
|
||||||
|
return virDomainSnapshotObjListGetNamesFrom(&snapshots->metaroot, names,
|
||||||
if (!(flags & VIR_DOMAIN_SNAPSHOT_LIST_ROOTS)) {
|
maxnames, flags);
|
||||||
virHashForEach(snapshots->objs, virDomainSnapshotObjListCopyNames,
|
|
||||||
&data);
|
|
||||||
} else {
|
|
||||||
virDomainSnapshotObjPtr root = snapshots->first_root;
|
|
||||||
while (root) {
|
|
||||||
virDomainSnapshotObjListCopyNames(root, root->def->name, &data);
|
|
||||||
root = root->sibling;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (data.oom) {
|
|
||||||
virReportOOMError();
|
|
||||||
goto cleanup;
|
|
||||||
}
|
|
||||||
|
|
||||||
return data.numnames;
|
|
||||||
|
|
||||||
cleanup:
|
|
||||||
for (i = 0; i < data.numnames; i++)
|
|
||||||
VIR_FREE(data.names[i]);
|
|
||||||
return -1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int virDomainSnapshotObjListGetNamesFrom(virDomainSnapshotObjPtr snapshot,
|
int virDomainSnapshotObjListGetNamesFrom(virDomainSnapshotObjPtr snapshot,
|
||||||
@ -14359,8 +14340,9 @@ static void virDomainSnapshotObjListCount(void *payload,
|
|||||||
virDomainSnapshotObjPtr obj = payload;
|
virDomainSnapshotObjPtr obj = payload;
|
||||||
struct virDomainSnapshotNumData *data = opaque;
|
struct virDomainSnapshotNumData *data = opaque;
|
||||||
|
|
||||||
/* LIST_ROOTS/LIST_DESCENDANTS was handled by caller,
|
/* LIST_ROOTS/LIST_DESCENDANTS was handled by the choice of
|
||||||
* LIST_METADATA is a no-op if we get this far. */
|
* iteration made in the caller, and LIST_METADATA is a no-op if
|
||||||
|
* we get this far. */
|
||||||
if ((data->flags & VIR_DOMAIN_SNAPSHOT_LIST_LEAVES) && obj->nchildren)
|
if ((data->flags & VIR_DOMAIN_SNAPSHOT_LIST_LEAVES) && obj->nchildren)
|
||||||
return;
|
return;
|
||||||
data->count++;
|
data->count++;
|
||||||
@ -14369,23 +14351,11 @@ static void virDomainSnapshotObjListCount(void *payload,
|
|||||||
int virDomainSnapshotObjListNum(virDomainSnapshotObjListPtr snapshots,
|
int virDomainSnapshotObjListNum(virDomainSnapshotObjListPtr snapshots,
|
||||||
unsigned int flags)
|
unsigned int flags)
|
||||||
{
|
{
|
||||||
struct virDomainSnapshotNumData data = { 0, 0 };
|
/* LIST_ROOTS and LIST_DESCENDANTS have the same bit value, but
|
||||||
|
* opposite semantics. Toggle here to get the correct traversal
|
||||||
data.flags = flags & ~VIR_DOMAIN_SNAPSHOT_LIST_ROOTS;
|
* on the metaroot. */
|
||||||
|
flags ^= VIR_DOMAIN_SNAPSHOT_LIST_ROOTS;
|
||||||
if (!(flags & VIR_DOMAIN_SNAPSHOT_LIST_ROOTS)) {
|
return virDomainSnapshotObjListNumFrom(&snapshots->metaroot, flags);
|
||||||
virHashForEach(snapshots->objs, virDomainSnapshotObjListCount, &data);
|
|
||||||
} else if (data.flags) {
|
|
||||||
virDomainSnapshotObjPtr root = snapshots->first_root;
|
|
||||||
while (root) {
|
|
||||||
virDomainSnapshotObjListCount(root, root->def->name, &data);
|
|
||||||
root = root->sibling;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
data.count = snapshots->nroots;
|
|
||||||
}
|
|
||||||
|
|
||||||
return data.count;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
@ -14413,7 +14383,7 @@ virDomainSnapshotObjPtr
|
|||||||
virDomainSnapshotFindByName(const virDomainSnapshotObjListPtr snapshots,
|
virDomainSnapshotFindByName(const virDomainSnapshotObjListPtr snapshots,
|
||||||
const char *name)
|
const char *name)
|
||||||
{
|
{
|
||||||
return virHashLookup(snapshots->objs, name);
|
return name ? virHashLookup(snapshots->objs, name) : &snapshots->metaroot;
|
||||||
}
|
}
|
||||||
|
|
||||||
void virDomainSnapshotObjListRemove(virDomainSnapshotObjListPtr snapshots,
|
void virDomainSnapshotObjListRemove(virDomainSnapshotObjListPtr snapshots,
|
||||||
@ -14499,34 +14469,27 @@ virDomainSnapshotSetRelations(void *payload,
|
|||||||
struct snapshot_set_relation *curr = data;
|
struct snapshot_set_relation *curr = data;
|
||||||
virDomainSnapshotObjPtr tmp;
|
virDomainSnapshotObjPtr tmp;
|
||||||
|
|
||||||
if (obj->def->parent) {
|
obj->parent = virDomainSnapshotFindByName(curr->snapshots,
|
||||||
obj->parent = virDomainSnapshotFindByName(curr->snapshots,
|
obj->def->parent);
|
||||||
obj->def->parent);
|
if (!obj->parent) {
|
||||||
if (!obj->parent) {
|
curr->err = -1;
|
||||||
curr->err = -1;
|
obj->parent = &curr->snapshots->metaroot;
|
||||||
VIR_WARN("snapshot %s lacks parent", obj->def->name);
|
VIR_WARN("snapshot %s lacks parent", obj->def->name);
|
||||||
} else {
|
|
||||||
tmp = obj->parent;
|
|
||||||
while (tmp) {
|
|
||||||
if (tmp == obj) {
|
|
||||||
curr->err = -1;
|
|
||||||
obj->parent = NULL;
|
|
||||||
VIR_WARN("snapshot %s in circular chain", obj->def->name);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
tmp = tmp->parent;
|
|
||||||
}
|
|
||||||
if (!tmp) {
|
|
||||||
obj->parent->nchildren++;
|
|
||||||
obj->sibling = obj->parent->first_child;
|
|
||||||
obj->parent->first_child = obj;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
curr->snapshots->nroots++;
|
tmp = obj->parent;
|
||||||
obj->sibling = curr->snapshots->first_root;
|
while (tmp && tmp->def) {
|
||||||
curr->snapshots->first_root = obj;
|
if (tmp == obj) {
|
||||||
|
curr->err = -1;
|
||||||
|
obj->parent = &curr->snapshots->metaroot;
|
||||||
|
VIR_WARN("snapshot %s in circular chain", obj->def->name);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
tmp = tmp->parent;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
obj->parent->nchildren++;
|
||||||
|
obj->sibling = obj->parent->first_child;
|
||||||
|
obj->parent->first_child = obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Populate parent link and child count of all snapshots, with all
|
/* Populate parent link and child count of all snapshots, with all
|
||||||
@ -14546,28 +14509,13 @@ virDomainSnapshotUpdateRelations(virDomainSnapshotObjListPtr snapshots)
|
|||||||
* of a parent, it is faster to just 0 the count rather than calling
|
* of a parent, it is faster to just 0 the count rather than calling
|
||||||
* this function on each child. */
|
* this function on each child. */
|
||||||
void
|
void
|
||||||
virDomainSnapshotDropParent(virDomainSnapshotObjListPtr snapshots,
|
virDomainSnapshotDropParent(virDomainSnapshotObjPtr snapshot)
|
||||||
virDomainSnapshotObjPtr snapshot)
|
|
||||||
{
|
{
|
||||||
virDomainSnapshotObjPtr prev = NULL;
|
virDomainSnapshotObjPtr prev = NULL;
|
||||||
virDomainSnapshotObjPtr curr = NULL;
|
virDomainSnapshotObjPtr curr = NULL;
|
||||||
size_t *count;
|
|
||||||
virDomainSnapshotObjPtr *first;
|
|
||||||
|
|
||||||
if (snapshot->parent) {
|
snapshot->parent->nchildren--;
|
||||||
count = &snapshot->parent->nchildren;
|
curr = snapshot->parent->first_child;
|
||||||
first = &snapshot->parent->first_child;
|
|
||||||
} else {
|
|
||||||
count = &snapshots->nroots;
|
|
||||||
first = &snapshots->first_root;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!*count || !*first) {
|
|
||||||
VIR_WARN("inconsistent snapshot relations");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
(*count)--;
|
|
||||||
curr = *first;
|
|
||||||
while (curr != snapshot) {
|
while (curr != snapshot) {
|
||||||
if (!curr) {
|
if (!curr) {
|
||||||
VIR_WARN("inconsistent snapshot relations");
|
VIR_WARN("inconsistent snapshot relations");
|
||||||
@ -14579,7 +14527,7 @@ virDomainSnapshotDropParent(virDomainSnapshotObjListPtr snapshots,
|
|||||||
if (prev)
|
if (prev)
|
||||||
prev->sibling = snapshot->sibling;
|
prev->sibling = snapshot->sibling;
|
||||||
else
|
else
|
||||||
*first = snapshot->sibling;
|
snapshot->parent->first_child = snapshot->sibling;
|
||||||
snapshot->parent = NULL;
|
snapshot->parent = NULL;
|
||||||
snapshot->sibling = NULL;
|
snapshot->sibling = NULL;
|
||||||
}
|
}
|
||||||
|
@ -1726,9 +1726,11 @@ struct _virDomainSnapshotDef {
|
|||||||
typedef struct _virDomainSnapshotObj virDomainSnapshotObj;
|
typedef struct _virDomainSnapshotObj virDomainSnapshotObj;
|
||||||
typedef virDomainSnapshotObj *virDomainSnapshotObjPtr;
|
typedef virDomainSnapshotObj *virDomainSnapshotObjPtr;
|
||||||
struct _virDomainSnapshotObj {
|
struct _virDomainSnapshotObj {
|
||||||
virDomainSnapshotDefPtr def;
|
virDomainSnapshotDefPtr def; /* non-NULL except for metaroot */
|
||||||
|
|
||||||
virDomainSnapshotObjPtr parent; /* NULL if root */
|
virDomainSnapshotObjPtr parent; /* non-NULL except for metaroot, before
|
||||||
|
virDomainSnapshotUpdateRelations, or
|
||||||
|
after virDomainSnapshotDropParent */
|
||||||
virDomainSnapshotObjPtr sibling; /* NULL if last child of parent */
|
virDomainSnapshotObjPtr sibling; /* NULL if last child of parent */
|
||||||
size_t nchildren;
|
size_t nchildren;
|
||||||
virDomainSnapshotObjPtr first_child; /* NULL if no children */
|
virDomainSnapshotObjPtr first_child; /* NULL if no children */
|
||||||
@ -1741,8 +1743,7 @@ struct _virDomainSnapshotObjList {
|
|||||||
* for O(1), lockless lookup-by-name */
|
* for O(1), lockless lookup-by-name */
|
||||||
virHashTable *objs;
|
virHashTable *objs;
|
||||||
|
|
||||||
size_t nroots;
|
virDomainSnapshotObj metaroot; /* Special parent of all root snapshots */
|
||||||
virDomainSnapshotObjPtr first_root;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
@ -1788,8 +1789,7 @@ int virDomainSnapshotForEachDescendant(virDomainSnapshotObjPtr snapshot,
|
|||||||
virHashIterator iter,
|
virHashIterator iter,
|
||||||
void *data);
|
void *data);
|
||||||
int virDomainSnapshotUpdateRelations(virDomainSnapshotObjListPtr snapshots);
|
int virDomainSnapshotUpdateRelations(virDomainSnapshotObjListPtr snapshots);
|
||||||
void virDomainSnapshotDropParent(virDomainSnapshotObjListPtr snapshots,
|
void virDomainSnapshotDropParent(virDomainSnapshotObjPtr snapshot);
|
||||||
virDomainSnapshotObjPtr snapshot);
|
|
||||||
|
|
||||||
/* Guest VM runtime state */
|
/* Guest VM runtime state */
|
||||||
typedef struct _virDomainStateReason virDomainStateReason;
|
typedef struct _virDomainStateReason virDomainStateReason;
|
||||||
|
@ -10514,7 +10514,7 @@ qemuDomainSnapshotCreateXML(virDomainPtr domain,
|
|||||||
}
|
}
|
||||||
/* Drop and rebuild the parent relationship, but keep all
|
/* Drop and rebuild the parent relationship, but keep all
|
||||||
* child relations by reusing snap. */
|
* child relations by reusing snap. */
|
||||||
virDomainSnapshotDropParent(&vm->snapshots, other);
|
virDomainSnapshotDropParent(other);
|
||||||
virDomainSnapshotDefFree(other->def);
|
virDomainSnapshotDefFree(other->def);
|
||||||
other->def = NULL;
|
other->def = NULL;
|
||||||
snap = other;
|
snap = other;
|
||||||
@ -10623,18 +10623,12 @@ cleanup:
|
|||||||
} else {
|
} else {
|
||||||
if (update_current)
|
if (update_current)
|
||||||
vm->current_snapshot = snap;
|
vm->current_snapshot = snap;
|
||||||
if (snap->def->parent) {
|
other = virDomainSnapshotFindByName(&vm->snapshots,
|
||||||
other = virDomainSnapshotFindByName(&vm->snapshots,
|
snap->def->parent);
|
||||||
snap->def->parent);
|
snap->parent = other;
|
||||||
snap->parent = other;
|
other->nchildren++;
|
||||||
other->nchildren++;
|
snap->sibling = other->first_child;
|
||||||
snap->sibling = other->first_child;
|
other->first_child = snap;
|
||||||
other->first_child = snap;
|
|
||||||
} else {
|
|
||||||
vm->snapshots.nroots++;
|
|
||||||
snap->sibling = vm->snapshots.first_root;
|
|
||||||
vm->snapshots.first_root = snap;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else if (snap) {
|
} else if (snap) {
|
||||||
virDomainSnapshotObjListRemove(&vm->snapshots, snap);
|
virDomainSnapshotObjListRemove(&vm->snapshots, snap);
|
||||||
@ -11439,7 +11433,7 @@ qemuDomainSnapshotReparentChildren(void *payload,
|
|||||||
VIR_FREE(snap->def->parent);
|
VIR_FREE(snap->def->parent);
|
||||||
snap->parent = rep->parent;
|
snap->parent = rep->parent;
|
||||||
|
|
||||||
if (rep->parent) {
|
if (rep->parent->def) {
|
||||||
snap->def->parent = strdup(rep->parent->def->name);
|
snap->def->parent = strdup(rep->parent->def->name);
|
||||||
|
|
||||||
if (snap->def->parent == NULL) {
|
if (snap->def->parent == NULL) {
|
||||||
@ -11547,15 +11541,9 @@ static int qemuDomainSnapshotDelete(virDomainSnapshotPtr snapshot,
|
|||||||
if (rep.err < 0)
|
if (rep.err < 0)
|
||||||
goto endjob;
|
goto endjob;
|
||||||
/* Can't modify siblings during ForEachChild, so do it now. */
|
/* Can't modify siblings during ForEachChild, so do it now. */
|
||||||
if (snap->parent) {
|
snap->parent->nchildren += snap->nchildren;
|
||||||
snap->parent->nchildren += snap->nchildren;
|
rep.last->sibling = snap->parent->first_child;
|
||||||
rep.last->sibling = snap->parent->first_child;
|
snap->parent->first_child = snap->first_child;
|
||||||
snap->parent->first_child = snap->first_child;
|
|
||||||
} else {
|
|
||||||
vm->snapshots.nroots += snap->nchildren;
|
|
||||||
rep.last->sibling = vm->snapshots.first_root;
|
|
||||||
vm->snapshots.first_root = snap->first_child;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (flags & VIR_DOMAIN_SNAPSHOT_DELETE_CHILDREN_ONLY) {
|
if (flags & VIR_DOMAIN_SNAPSHOT_DELETE_CHILDREN_ONLY) {
|
||||||
@ -11563,7 +11551,7 @@ static int qemuDomainSnapshotDelete(virDomainSnapshotPtr snapshot,
|
|||||||
snap->first_child = NULL;
|
snap->first_child = NULL;
|
||||||
ret = 0;
|
ret = 0;
|
||||||
} else {
|
} else {
|
||||||
virDomainSnapshotDropParent(&vm->snapshots, snap);
|
virDomainSnapshotDropParent(snap);
|
||||||
ret = qemuDomainSnapshotDiscard(driver, vm, snap, true, metadata_only);
|
ret = qemuDomainSnapshotDiscard(driver, vm, snap, true, metadata_only);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user