From dc8d3dc6cdf85292630eb7c31ba8fb58b836fb02 Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Fri, 22 Mar 2019 00:39:02 -0500 Subject: [PATCH] snapshot: Create new virDomainMomentObjList type The new code here very heavily resembles the code in virDomainSnapshotObjList. There are still a couple of spots that are tied a little too heavily to snapshots (the free function lacks a polymorphic cleanup until we refactor code to use virObject; and an upcoming patch will add internal VIR_DOMAIN_MOMENT_LIST flags to replace the snapshot flag bits), but in general this is fairly close to the state needed to add checkpoint lists. Signed-off-by: Eric Blake Reviewed-by: John Ferlan --- src/conf/virconftypes.h | 3 + src/conf/virdomainmomentobjlist.c | 359 ++++++++++++++++++++++++++++++ src/conf/virdomainmomentobjlist.h | 33 +++ 3 files changed, 395 insertions(+) diff --git a/src/conf/virconftypes.h b/src/conf/virconftypes.h index 574815cf04..6a8267c422 100644 --- a/src/conf/virconftypes.h +++ b/src/conf/virconftypes.h @@ -223,6 +223,9 @@ typedef virDomainMomentDef *virDomainMomentDefPtr; typedef struct _virDomainMomentObj virDomainMomentObj; typedef virDomainMomentObj *virDomainMomentObjPtr; +typedef struct _virDomainMomentObjList virDomainMomentObjList; +typedef virDomainMomentObjList *virDomainMomentObjListPtr; + typedef struct _virDomainNVRAMDef virDomainNVRAMDef; typedef virDomainNVRAMDef *virDomainNVRAMDefPtr; diff --git a/src/conf/virdomainmomentobjlist.c b/src/conf/virdomainmomentobjlist.c index 209f92493a..b2122e7292 100644 --- a/src/conf/virdomainmomentobjlist.c +++ b/src/conf/virdomainmomentobjlist.c @@ -29,10 +29,25 @@ #include "virstring.h" #include "moment_conf.h" +/* FIXME: using virObject would allow us to not need this */ +#include "snapshot_conf.h" +#include "virdomainsnapshotobjlist.h" + #define VIR_FROM_THIS VIR_FROM_DOMAIN VIR_LOG_INIT("conf.virdomainmomentobjlist"); +/* Opaque struct */ +struct _virDomainMomentObjList { + /* name string -> virDomainMomentObj mapping + * for O(1), lockless lookup-by-name */ + virHashTable *objs; + + virDomainMomentObj metaroot; /* Special parent of all root moments */ + virDomainMomentObjPtr current; /* The current moment, if any */ +}; + + /* Run iter(data) on all direct children of moment, while ignoring all * other entries in moments. Return the number of children * visited. No particular ordering is guaranteed. */ @@ -165,3 +180,347 @@ virDomainMomentMoveChildren(virDomainMomentObjPtr from, from->nchildren = 0; from->first_child = NULL; } + + +static virDomainMomentObjPtr +virDomainMomentObjNew(void) +{ + virDomainMomentObjPtr moment; + + if (VIR_ALLOC(moment) < 0) + return NULL; + + VIR_DEBUG("obj=%p", moment); + + return moment; +} + + +static void +virDomainMomentObjFree(virDomainMomentObjPtr moment) +{ + if (!moment) + return; + + VIR_DEBUG("obj=%p", moment); + + /* FIXME: Make this polymorphic by inheriting from virObject */ + virDomainSnapshotDefFree(virDomainSnapshotObjGetDef(moment)); + VIR_FREE(moment); +} + + +/* Add def to the list and return a matching object, or NULL on error */ +virDomainMomentObjPtr +virDomainMomentAssignDef(virDomainMomentObjListPtr moments, + virDomainMomentDefPtr def) +{ + virDomainMomentObjPtr moment; + + if (virHashLookup(moments->objs, def->name) != NULL) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("unexpected domain moment %s already exists"), + def->name); + return NULL; + } + + if (!(moment = virDomainMomentObjNew())) + return NULL; + + if (virHashAddEntry(moments->objs, moment->def->name, moment) < 0) { + VIR_FREE(moment); + return NULL; + } + moment->def = def; + + return moment; +} + + +static void +virDomainMomentObjListDataFree(void *payload, + const void *name ATTRIBUTE_UNUSED) +{ + virDomainMomentObjPtr obj = payload; + + virDomainMomentObjFree(obj); +} + + +virDomainMomentObjListPtr +virDomainMomentObjListNew(void) +{ + virDomainMomentObjListPtr moments; + + if (VIR_ALLOC(moments) < 0) + return NULL; + moments->objs = virHashCreate(50, virDomainMomentObjListDataFree); + if (!moments->objs) { + VIR_FREE(moments); + return NULL; + } + return moments; +} + +void +virDomainMomentObjListFree(virDomainMomentObjListPtr moments) +{ + if (!moments) + return; + virHashFree(moments->objs); + VIR_FREE(moments); +} + + +/* Struct and callback for collecting a list of names of moments that + * meet a particular filter. */ +struct virDomainMomentNameData { + char **const names; + int maxnames; + unsigned int flags; + int count; + bool error; + virDomainMomentObjListFilter filter; +}; + + +static int virDomainMomentObjListCopyNames(void *payload, + const void *name ATTRIBUTE_UNUSED, + void *opaque) +{ + virDomainMomentObjPtr obj = payload; + struct virDomainMomentNameData *data = opaque; + + if (data->error) + return 0; + /* Caller already sanitized flags. Filtering on DESCENDANTS was + * done by choice of iteration in the caller. */ + /* TODO: Create VIR_DOMAIN_MOMENT_LIST names */ + if ((data->flags & VIR_DOMAIN_SNAPSHOT_LIST_LEAVES) && obj->nchildren) + return 0; + if ((data->flags & VIR_DOMAIN_SNAPSHOT_LIST_NO_LEAVES) && !obj->nchildren) + return 0; + + if (!data->filter(obj, data->flags)) + return 0; + + if (data->names && data->count < data->maxnames && + VIR_STRDUP(data->names[data->count], obj->def->name) < 0) { + data->error = true; + return 0; + } + data->count++; + return 0; +} + + +int +virDomainMomentObjListGetNames(virDomainMomentObjListPtr moments, + virDomainMomentObjPtr from, + char **const names, + int maxnames, + unsigned int flags, + virDomainMomentObjListFilter filter) +{ + struct virDomainMomentNameData data = { names, maxnames, flags, 0, + false, filter }; + size_t i; + + if (!from) { + /* LIST_ROOTS and LIST_DESCENDANTS have the same bit value, + * but opposite semantics. Toggle here to get the correct + * traversal on the metaroot. */ + /* TODO: Create VIR_DOMAIN_MOMENT_LIST names */ + flags ^= VIR_DOMAIN_SNAPSHOT_LIST_ROOTS; + from = &moments->metaroot; + } + + /* We handle LIST_ROOT/LIST_DESCENDANTS and LIST_TOPOLOGICAL directly, + * mask those bits out to determine when we must use the filter callback. */ + data.flags &= ~(VIR_DOMAIN_SNAPSHOT_LIST_DESCENDANTS | + VIR_DOMAIN_SNAPSHOT_LIST_TOPOLOGICAL); + + /* If this common code is being used, we assume that all moments + * have metadata, and thus can handle METADATA up front as an + * all-or-none filter. XXX This might not always be true, if we + * add the ability to track qcow2 internal snapshots without the + * use of metadata, in which case this check should move into the + * filter callback. */ + if ((data.flags & VIR_DOMAIN_SNAPSHOT_FILTERS_METADATA) == + VIR_DOMAIN_SNAPSHOT_LIST_NO_METADATA) + return 0; + data.flags &= ~VIR_DOMAIN_SNAPSHOT_FILTERS_METADATA; + + if (flags & VIR_DOMAIN_SNAPSHOT_LIST_DESCENDANTS) { + /* We could just always do a topological visit; but it is + * possible to optimize for less stack usage and time when a + * simpler full hashtable visit or counter will do. */ + if (from->def || (names && + (flags & VIR_DOMAIN_SNAPSHOT_LIST_TOPOLOGICAL))) + virDomainMomentForEachDescendant(from, + virDomainMomentObjListCopyNames, + &data); + else if (names || data.flags) + virHashForEach(moments->objs, virDomainMomentObjListCopyNames, + &data); + else + data.count = virHashSize(moments->objs); + } else if (names || data.flags) { + virDomainMomentForEachChild(from, + virDomainMomentObjListCopyNames, &data); + } else { + data.count = from->nchildren; + } + + if (data.error) { + for (i = 0; i < data.count; i++) + VIR_FREE(names[i]); + return -1; + } + + return data.count; +} + + +virDomainMomentObjPtr +virDomainMomentFindByName(virDomainMomentObjListPtr moments, + const char *name) +{ + return name ? virHashLookup(moments->objs, name) : &moments->metaroot; +} + + +/* Return the current moment, or NULL */ +virDomainMomentObjPtr +virDomainMomentGetCurrent(virDomainMomentObjListPtr moments) +{ + return moments->current; +} + + +/* Return the current moment's name, or NULL */ +const char * +virDomainMomentGetCurrentName(virDomainMomentObjListPtr moments) +{ + if (moments->current) + return moments->current->def->name; + return NULL; +} + + +/* Return true if name matches the current moment */ +bool +virDomainMomentIsCurrentName(virDomainMomentObjListPtr moments, + const char *name) +{ + return moments->current && STREQ(moments->current->def->name, name); +} + + +/* Update the current moment, using NULL if no current remains */ +void +virDomainMomentSetCurrent(virDomainMomentObjListPtr moments, + virDomainMomentObjPtr moment) +{ + moments->current = moment; +} + + +/* Return the number of moments in the list */ +int +virDomainMomentObjListSize(virDomainMomentObjListPtr moments) +{ + return virHashSize(moments->objs); +} + + +/* Remove moment from the list; return true if it was current */ +bool +virDomainMomentObjListRemove(virDomainMomentObjListPtr moments, + virDomainMomentObjPtr moment) +{ + bool ret = moments->current == moment; + + virHashRemoveEntry(moments->objs, moment->def->name); + if (ret) + moments->current = NULL; + return ret; +} + + +/* Remove all moments tracked in the list */ +void +virDomainMomentObjListRemoveAll(virDomainMomentObjListPtr moments) +{ + virHashRemoveAll(moments->objs); + virDomainMomentDropChildren(&moments->metaroot); +} + + +/* Call iter on each member of the list, in unspecified order */ +int +virDomainMomentForEach(virDomainMomentObjListPtr moments, + virHashIterator iter, + void *data) +{ + return virHashForEach(moments->objs, iter, data); +} + + +/* Struct and callback function used as a hash table callback; each call + * inspects the pre-existing moment->def->parent field, and adjusts + * the moment->parent field as well as the parent's child fields to + * wire up the hierarchical relations for the given moment. The error + * indicator gets set if a parent is missing or a requested parent would + * cause a circular parent chain. */ +struct moment_set_relation { + virDomainMomentObjListPtr moments; + int err; +}; +static int +virDomainMomentSetRelations(void *payload, + const void *name ATTRIBUTE_UNUSED, + void *data) +{ + virDomainMomentObjPtr obj = payload; + struct moment_set_relation *curr = data; + virDomainMomentObjPtr tmp; + virDomainMomentObjPtr parent; + + parent = virDomainMomentFindByName(curr->moments, obj->def->parent); + if (!parent) { + curr->err = -1; + parent = &curr->moments->metaroot; + VIR_WARN("moment %s lacks parent", obj->def->name); + } else { + tmp = parent; + while (tmp && tmp->def) { + if (tmp == obj) { + curr->err = -1; + parent = &curr->moments->metaroot; + VIR_WARN("moment %s in circular chain", obj->def->name); + break; + } + tmp = tmp->parent; + } + } + virDomainMomentSetParent(obj, parent); + return 0; +} + + +/* Populate parent link and child count of all moments, with all + * assigned defs having relations starting as 0/NULL. Return 0 on + * success, -1 if a parent is missing or if a circular relationship + * was requested. */ +int +virDomainMomentUpdateRelations(virDomainMomentObjListPtr moments) +{ + struct moment_set_relation act = { moments, 0 }; + + virDomainMomentDropChildren(&moments->metaroot); + virHashForEach(moments->objs, virDomainMomentSetRelations, &act); + if (act.err) + moments->current = NULL; + return act.err; +} diff --git a/src/conf/virdomainmomentobjlist.h b/src/conf/virdomainmomentobjlist.h index dceb55fca2..89a0df5a24 100644 --- a/src/conf/virdomainmomentobjlist.h +++ b/src/conf/virdomainmomentobjlist.h @@ -27,6 +27,10 @@ # include "virconftypes.h" # include "virhash.h" +/* Filter that returns true if a given moment matches the filter flags */ +typedef bool (*virDomainMomentObjListFilter)(virDomainMomentObjPtr obj, + unsigned int flags); + /* Struct that allows tracing hierarchical relationships between * multiple virDomainMoment objects. The opaque type * virDomainMomentObjList then maintains both a hash of these structs @@ -60,4 +64,33 @@ void virDomainMomentMoveChildren(virDomainMomentObjPtr from, void virDomainMomentSetParent(virDomainMomentObjPtr moment, virDomainMomentObjPtr parent); +virDomainMomentObjListPtr virDomainMomentObjListNew(void); +void virDomainMomentObjListFree(virDomainMomentObjListPtr moments); + +virDomainMomentObjPtr virDomainMomentAssignDef(virDomainMomentObjListPtr moments, + virDomainMomentDefPtr def); + +int virDomainMomentObjListGetNames(virDomainMomentObjListPtr moments, + virDomainMomentObjPtr from, + char **const names, + int maxnames, + unsigned int flags, + virDomainMomentObjListFilter filter); +virDomainMomentObjPtr virDomainMomentFindByName(virDomainMomentObjListPtr moments, + const char *name); +int virDomainMomentObjListSize(virDomainMomentObjListPtr moments); +virDomainMomentObjPtr virDomainMomentGetCurrent(virDomainMomentObjListPtr moments); +const char *virDomainMomentGetCurrentName(virDomainMomentObjListPtr moments); +bool virDomainMomentIsCurrentName(virDomainMomentObjListPtr moments, + const char *name); +void virDomainMomentSetCurrent(virDomainMomentObjListPtr moments, + virDomainMomentObjPtr moment); +bool virDomainMomentObjListRemove(virDomainMomentObjListPtr moments, + virDomainMomentObjPtr moment); +void virDomainMomentObjListRemoveAll(virDomainMomentObjListPtr moments); +int virDomainMomentForEach(virDomainMomentObjListPtr moments, + virHashIterator iter, + void *data); +int virDomainMomentUpdateRelations(virDomainMomentObjListPtr moments); + #endif /* LIBVIRT_VIRDOMAINMOMENTOBJLIST_H */