diff mbox series

[V1,12/26] migration: vmstate factory object

Message ID 1714406135-451286-13-git-send-email-steven.sistare@oracle.com (mailing list archive)
State New, archived
Headers show
Series Live update: cpr-exec | expand

Commit Message

Steven Sistare April 29, 2024, 3:55 p.m. UTC
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
diff mbox series

Patch

diff --git a/include/migration/vmstate.h b/include/migration/vmstate.h
index 8cb3d2b..00ad864 100644
--- a/include/migration/vmstate.h
+++ b/include/migration/vmstate.h
@@ -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
diff --git a/migration/meson.build b/migration/meson.build
index 50e7cb2..e667b40 100644
--- a/migration/meson.build
+++ b/migration/meson.build
@@ -5,6 +5,7 @@  migration_files = files(
   'xbzrle.c',
   'vmstate-types.c',
   'vmstate.c',
+  'vmstate-factory.c',
   'qemu-file.c',
   'yank_functions.c',
 )
diff --git a/migration/savevm.c b/migration/savevm.c
index ec48da9..01ed78c 100644
--- a/migration/savevm.c
+++ b/migration/savevm.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,
diff --git a/migration/trace-events b/migration/trace-events
index 1e23238..3b9c292 100644
--- a/migration/trace-events
+++ b/migration/trace-events
@@ -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"
diff --git a/migration/vmstate-factory.c b/migration/vmstate-factory.c
new file mode 100644
index 0000000..e425666
--- /dev/null
+++ b/migration/vmstate-factory.c
@@ -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;
+}
diff --git a/stubs/vmstate.c b/stubs/vmstate.c
index eff8be4..0e977e2 100644
--- a/stubs/vmstate.c
+++ b/stubs/vmstate.c
@@ -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;