@@ -204,6 +204,11 @@ struct VMStateDescription {
*/
bool precreate;
+ /*
+ * This VMSD is a factory or a factory object.
+ */
+ bool factory;
+
int version_id;
int minimum_version_id;
MigrationPriority priority;
@@ -1228,6 +1233,17 @@ bool vmstate_section_needed(const VMStateDescription *vmsd, void *opaque);
typedef char (VMStateId)[256];
#define VMSTATE_INSTANCE_ID_ANY -1
+#define VMSTATE_INSTANCE_ID_FACTORY -2
+
+#include "qemu/queue.h"
+
+typedef struct FactoryObject {
+ char *factory_name;
+ char *instance_name;
+ int instance_id;
+ void *opaque;
+ QLIST_ENTRY(FactoryObject) next;
+} FactoryObject;
/* Returns: 0 on success, -1 on failure */
int vmstate_register_with_alias_id(VMStateIf *obj, uint32_t instance_id,
@@ -1266,6 +1282,10 @@ static void __attribute__((constructor)) vmstate_register_ ## _vmsd(void) \
vmstate_register_init_add(_obj, _id, &_vmsd, _opaque); \
}
+#define vmstate_register_init_factory(_vmsd, _type) \
+ vmstate_register_init(NULL, VMSTATE_INSTANCE_ID_FACTORY, \
+ _vmsd, (void *)sizeof(_type))
+
void vmstate_register_init_add(VMStateIf *obj, int instance_id,
const VMStateDescription *vmsd, void *opaque);
@@ -1301,8 +1321,20 @@ static inline int vmstate_register_any(VMStateIf *obj,
}
/**
+ * vmstate_register_factory() - register a factory name and size, needed to
+ * recognize incoming factory objects.
+ */
+static inline int vmstate_register_factory(const VMStateDescription *vmsd,
+ long size)
+{
+ return vmstate_register_with_alias_id(NULL, VMSTATE_INSTANCE_ID_FACTORY,
+ vmsd, (void *)size, -1, 0, NULL,
+ NULL);
+}
+
+/**
* vmstate_register_named() - pass an instance_name explicitly instead of
- * implicitly via VMStateIf get_id(). Needed to register a instance-specific
+ * implicitly via VMStateIf get_id(). Needed to register an instance-specific
* VMSD for objects that are not Objects.
*/
static inline int vmstate_register_named(const char *instance_name,
@@ -1332,4 +1364,36 @@ void vmstate_register_ram_global(struct MemoryRegion *memory);
bool vmstate_check_only_migratable(const VMStateDescription *vmsd);
+/*
+ * Add to the factory object list, called during loadvm.
+ */
+void vmstate_add_factory_object(const char *factory_name,
+ const char *instance_name,
+ int instance_id,
+ void *opaque);
+
+/*
+ * Search for and return a factory object.
+ */
+void *vmstate_find_factory_object(const char *factory_name,
+ const char *instance_name,
+ int instance_id);
+
+/*
+ * Search for and return a factory object, removing it from the list.
+ */
+void *vmstate_claim_factory_object(const char *factory_name,
+ const char *instance_name,
+ int instance_id);
+
+typedef int (*vmstate_walk_factory_cb)(FactoryObject *obj, void *opaque);
+
+/*
+ * Search for registered factory objects (ie, outgoing)
+ * and call cb passing opaque.
+ */
+int vmstate_walk_factory_outgoing(const char *factory_name,
+ vmstate_walk_factory_cb cb,
+ void *opaque);
+
#endif
@@ -5,6 +5,7 @@ migration_files = files(
'xbzrle.c',
'vmstate-types.c',
'vmstate.c',
+ 'vmstate-factory.c',
'qemu-file.c',
'yank_functions.c',
)
@@ -1407,7 +1407,8 @@ int qemu_savevm_precreate_save(QEMUFile *f, Error **errp)
qemu_put_be32(f, QEMU_VM_FILE_VERSION);
SAVEVM_FOREACH_ALL(se, entry) {
- if (se->vmsd && se->vmsd->precreate) {
+ if (se->vmsd && se->vmsd->precreate &&
+ se->instance_id != VMSTATE_INSTANCE_ID_FACTORY) {
ret = vmstate_save(f, se, NULL, errp);
if (ret) {
qemu_file_set_error(f, ret);
@@ -1951,6 +1952,45 @@ static SaveStateEntry *find_se(const char *idstr, uint32_t instance_id)
return NULL;
}
+int vmstate_walk_factory_outgoing(const char *factory_name,
+ vmstate_walk_factory_cb cb, void *cb_data)
+{
+ SaveStateEntry *se, *new_se;
+ int ret, instance_len;
+ FactoryObject obj;
+ VMStateId idstr;
+ char *se_factory_name;
+
+ SAVEVM_FOREACH_SAFE_ALL(se, entry, new_se) {
+ if (!se->vmsd || !se->vmsd->factory) {
+ continue;
+ }
+ if (se->instance_id == VMSTATE_INSTANCE_ID_FACTORY) {
+ /* This is the factory itself, not a generated instance */
+ continue;
+ }
+
+ se_factory_name = strrchr(se->idstr, '/');
+ if (factory_name && strcmp(se_factory_name + 1, factory_name)) {
+ continue;
+ }
+
+ strcpy(idstr, se->idstr);
+ instance_len = se_factory_name - se->idstr;
+ idstr[instance_len] = 0;
+ obj.factory_name = idstr + instance_len + 1;
+ obj.instance_name = idstr;
+ obj.instance_id = se->instance_id;
+ obj.opaque = se->opaque;
+
+ ret = cb(&obj, cb_data);
+ if (ret) {
+ return ret;
+ }
+ }
+ return 0;
+}
+
enum LoadVMExitCodes {
/* Allow a command to quit all layers of nested loadvm loops */
LOADVM_QUIT = 1,
@@ -2721,8 +2761,9 @@ qemu_loadvm_section_start_full(QEMUFile *f, uint8_t type)
bool trace_downtime = (type == QEMU_VM_SECTION_FULL);
uint32_t instance_id, version_id, section_id;
int64_t start_ts, end_ts;
- SaveStateEntry *se;
- VMStateId idstr;
+ SaveStateEntry *se, new_se;
+ VMStateId idstr, instance_name;
+ char *factory_name = NULL;
int ret;
/* Read section start */
@@ -2744,8 +2785,22 @@ qemu_loadvm_section_start_full(QEMUFile *f, uint8_t type)
trace_qemu_loadvm_state_section_startfull(section_id, idstr,
instance_id, version_id);
+
/* Find savevm section */
se = find_se(idstr, instance_id);
+
+ if (se == NULL) {
+ pstrcpy(instance_name, sizeof(idstr), idstr);
+ factory_name = strrchr(instance_name, '/');
+ if (factory_name) {
+ *factory_name++ = 0;
+ se = find_se(factory_name, VMSTATE_INSTANCE_ID_FACTORY);
+ new_se = *se;
+ new_se.opaque = g_malloc((long)se->opaque);
+ se = &new_se;
+ }
+ }
+
if (se == NULL) {
error_report("Unknown savevm section or instance '%s' %"PRIu32". "
"Make sure that your current VM setup matches your "
@@ -2780,6 +2835,11 @@ qemu_loadvm_section_start_full(QEMUFile *f, uint8_t type)
return ret;
}
+ if (factory_name) {
+ vmstate_add_factory_object(factory_name, instance_name, instance_id,
+ se->opaque);
+ }
+
if (trace_downtime) {
end_ts = qemu_clock_get_us(QEMU_CLOCK_REALTIME);
trace_vmstate_downtime_load("non-iterable", se->idstr,
@@ -72,6 +72,11 @@ vmstate_subsection_save_loop(const char *name, const char *sub) "%s/%s"
vmstate_subsection_save_top(const char *idstr) "%s"
vmstate_field_exists(const char *vmsd, const char *name, int field_version, int version, int result) "%s:%s field_version %d version %d result %d"
+# vmstate-factory.c
+vmstate_add_factory_object(const char *factory_name, const char *idstr, int instance_id, void *opaque) " %s, %s, %d, %p"
+vmstate_find_factory_object(const char *factory_name, const char *instance_name, int instance_id, void *opaque) "%s, %s, %d -> %p"
+vmstate_claim_factory_object(const char *factory_name, const char *instance_name, int instance_id, void *opaque) "%s, %s, %d -> %p"
+
# vmstate-types.c
get_qtailq(const char *name, int version_id) "%s v%d"
get_qtailq_end(const char *name, const char *reason, int val) "%s %s/%d"
new file mode 100644
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2024 Oracle and/or its affiliates.
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+#include "qemu/osdep.h"
+#include "migration/vmstate.h"
+#include "trace.h"
+
+static QLIST_HEAD(, FactoryObject) factory_objects;
+
+void vmstate_add_factory_object(const char *factory_name,
+ const char *instance_name,
+ int instance_id,
+ void *opaque)
+{
+ FactoryObject *obj = g_new0(FactoryObject, 1);
+
+ obj->opaque = opaque;
+ obj->factory_name = g_strdup(factory_name);
+ obj->instance_name = g_strdup(instance_name);
+ obj->instance_id = instance_id;
+ QLIST_INSERT_HEAD(&factory_objects, obj, next);
+ trace_vmstate_add_factory_object(factory_name, instance_name, instance_id,
+ opaque);
+
+}
+
+#define object_match(obj, _factory_name, _instance_name, _instance_id) \
+ (!strcmp(obj->factory_name, _factory_name) && \
+ !strcmp(obj->instance_name, _instance_name) && \
+ obj->instance_id == _instance_id)
+
+static FactoryObject *find_object(const char *factory_name,
+ const char *instance_name,
+ int instance_id)
+{
+ FactoryObject *obj;
+
+ QLIST_FOREACH(obj, &factory_objects, next) {
+ if (object_match(obj, factory_name, instance_name, instance_id)) {
+ return obj;
+ }
+ }
+
+ return NULL;
+}
+
+void *vmstate_find_factory_object(const char *factory_name,
+ const char *instance_name,
+ int instance_id)
+{
+ FactoryObject *obj = find_object(factory_name, instance_name, instance_id);
+ void *opaque = obj ? obj->opaque : NULL;
+
+ trace_vmstate_find_factory_object(factory_name, instance_name, instance_id,
+ opaque);
+ return opaque;
+}
+
+void *vmstate_claim_factory_object(const char *factory_name,
+ const char *instance_name,
+ int instance_id)
+{
+ FactoryObject *obj = find_object(factory_name, instance_name, instance_id);
+ void *opaque = obj ? obj->opaque : NULL;
+
+ if (obj) {
+ g_free(obj->factory_name);
+ g_free(obj->instance_name);
+ QLIST_REMOVE(obj, next);
+ }
+
+ trace_vmstate_claim_factory_object(factory_name, instance_name, instance_id,
+ opaque);
+ return opaque;
+}
@@ -24,6 +24,12 @@ void vmstate_unregister_named(const char *vmsd_name,
{
}
+int vmstate_walk_factory_outgoing(const char *factory_name,
+ vmstate_walk_factory_cb cb, void *cb_data)
+{
+ return 1;
+}
+
bool vmstate_check_only_migratable(const VMStateDescription *vmsd)
{
return true;
During the precreate phase, we will load migration state that will be used to create objects, but those objects have not been created and have not registered a vmstate handler yet. We don't know how many objects will be created, or what their names and ids will be, so we don't know what vmstate handlers to register. To solve this problem, define the factory object. A factory object is added to the outgoing migration stream as usual, by registering a vmsd and opaque pointer. During incoming migration, it is allocated on demand, without relying on a pre-registered object's opaque address. Instead, register a factory that knows the object's size. loadvm receives an idstr which contains a factory name, finds the factory, allocates the object, then loads the fields as usual. The object is added to a factory_objects list, tagged by name and id, to be found and claimed later by object-specific code. A factory is a registered VMStateDescription with factory=true and instance_id VMSTATE_INSTANCE_ID_FACTORY. A factory object is registered using the same VMStateDescription, but with its own instance_name and instance_id. Signed-off-by: Steve Sistare <steven.sistare@oracle.com> --- include/migration/vmstate.h | 66 +++++++++++++++++++++++++++++++++++++- migration/meson.build | 1 + migration/savevm.c | 66 ++++++++++++++++++++++++++++++++++++-- migration/trace-events | 5 +++ migration/vmstate-factory.c | 78 +++++++++++++++++++++++++++++++++++++++++++++ stubs/vmstate.c | 6 ++++ 6 files changed, 218 insertions(+), 4 deletions(-) create mode 100644 migration/vmstate-factory.c