Patchwork [RFC,09/16] Visitor: Load path

login
register
mail settings
Submitter Dave Gilbert
Date March 25, 2014, 8:17 p.m.
Message ID <1395778647-30925-10-git-send-email-dgilbert@redhat.com>
Download mbox | patch
Permalink /patch/333703/
State New
Headers show

Comments

Dave Gilbert - March 25, 2014, 8:17 p.m.
From: "Dr. David Alan Gilbert" <dgilbert@redhat.com>

Hacks in piix4.c, pci.c, spapr_vscsi.c for now

Signed-off-by: Dr. David Alan Gilbert <dgilbert@redhat.com>
---
 arch_init.c                 |  72 ++++++++++++++---------
 hw/acpi/piix4.c             |   5 +-
 hw/pci/pci.c                |   3 +-
 hw/scsi/spapr_vscsi.c       |   3 +-
 include/migration/vmstate.h |   2 +-
 savevm.c                    | 133 +++++++++++++++++++++++++++++++------------
 vmstate.c                   | 135 +++++++++++++++++++++++++++++++++-----------
 7 files changed, 251 insertions(+), 102 deletions(-)

Patch

diff --git a/arch_init.c b/arch_init.c
index 02bf78a..145666d 100644
--- a/arch_init.c
+++ b/arch_init.c
@@ -1004,15 +1004,12 @@  static int load_xbzrle(QEMUFile *f, ram_addr_t addr, void *host)
     return rc;
 }
 
-static inline void *host_from_stream_offset(QEMUFile *f,
-                                            ram_addr_t offset,
-                                            int flags)
+static inline void *host_from_stream_offset(ramsecentry_header *rse_hdr)
 {
     static RAMBlock *block = NULL;
-    char id[256];
-    uint8_t len;
+    ram_addr_t offset = rse_hdr->addr;
 
-    if (flags & RAM_SAVE_FLAG_CONTINUE) {
+    if (rse_hdr->flags & RAM_SAVE_FLAG_CONTINUE) {
         if (!block) {
             fprintf(stderr, "Ack, bad migration stream!\n");
             return NULL;
@@ -1021,16 +1018,13 @@  static inline void *host_from_stream_offset(QEMUFile *f,
         return memory_region_get_ram_ptr(block->mr) + offset;
     }
 
-    len = qemu_get_byte(f);
-    qemu_get_buffer(f, (uint8_t *)id, len);
-    id[len] = 0;
-
     QTAILQ_FOREACH(block, &ram_list.blocks, next) {
-        if (!strncmp(id, block->idstr, sizeof(id)))
+        if (!strncmp(rse_hdr->idstr, block->idstr, sizeof(rse_hdr->idstr))) {
             return memory_region_get_ram_ptr(block->mr) + offset;
+        }
     }
 
-    fprintf(stderr, "Can't find block %s!\n", id);
+    fprintf(stderr, "Can't find block %s!\n", rse_hdr->idstr);
     return NULL;
 }
 
@@ -1051,6 +1045,9 @@  static int ram_load(QEMUFile *f, void *opaque, int version_id)
     int flags, ret = 0;
     int error;
     static uint64_t seq_iter;
+    Error *local_err = NULL;
+    ramsecentry_header rse_hdr;
+    Visitor *v = qemu_file_get_tmp_visitor(f); // TODO
 
     seq_iter++;
 
@@ -1058,11 +1055,18 @@  static int ram_load(QEMUFile *f, void *opaque, int version_id)
         return -EINVAL;
     }
 
-    do {
-        addr = qemu_get_be64(f);
-
-        flags = addr & ~TARGET_PAGE_MASK;
-        addr &= TARGET_PAGE_MASK;
+    visit_start_sequence_compat(v, "ramseclist", VISIT_SEQ_COMPAT_RAMSECLIST,
+                                NULL, &local_err);
+    LOCAL_ERR_REPORT(return -EINVAL;);
+    while (visit_get_next_type(v, &flags, NULL, "ramseclist", &local_err),
+           (!local_err && !(flags & RAM_SAVE_FLAG_EOS))) {
+        LOCAL_ERR_REPORT(return -EINVAL;);
+        visit_start_sequence_compat(v, "ramsecentry",
+                                    VISIT_SEQ_COMPAT_RAMSECENTRY, &rse_hdr,
+                                    &local_err);
+        addr = rse_hdr.addr;
+        flags = rse_hdr.flags;
+        LOCAL_ERR_REPORT(return -EINVAL;);
 
         if (flags & RAM_SAVE_FLAG_MEM_SIZE) {
             if (version_id == 4) {
@@ -1071,14 +1075,17 @@  static int ram_load(QEMUFile *f, void *opaque, int version_id)
                 ram_addr_t length;
                 ram_addr_t total_ram_bytes = addr;
 
+                visit_start_list(v, "blocklist", &local_err);
                 while (total_ram_bytes) {
                     RAMBlock *block;
-                    uint8_t len;
+                    visit_start_struct(v, NULL, "blockid", "blockid", 0,
+                                       &local_err);
+                    visit_type_str256(v, id, "blockname", &local_err);
+                    visit_type_uint64(v, &length, "blocklen", &local_err);
+                    LOCAL_ERR_REPORT(ret = -EINVAL; goto done;);
+                    visit_end_struct(v, &local_err);
+                    LOCAL_ERR_REPORT(ret = -EINVAL; goto done;);
 
-                    len = qemu_get_byte(f);
-                    qemu_get_buffer(f, (uint8_t *)id, len);
-                    id[len] = 0;
-                    length = qemu_get_be64(f);
 
                     QTAILQ_FOREACH(block, &ram_list.blocks, next) {
                         if (!strncmp(id, block->idstr, sizeof(id))) {
@@ -1103,6 +1110,9 @@  static int ram_load(QEMUFile *f, void *opaque, int version_id)
 
                     total_ram_bytes -= length;
                 }
+                LOCAL_ERR_REPORT(ret = -EINVAL; goto done;);
+                visit_end_list(v, &local_err);
+                LOCAL_ERR_REPORT(ret = -EINVAL; goto done;);
             }
         }
 
@@ -1110,24 +1120,25 @@  static int ram_load(QEMUFile *f, void *opaque, int version_id)
             void *host;
             uint8_t ch;
 
-            host = host_from_stream_offset(f, addr, flags);
+            host = host_from_stream_offset(&rse_hdr);
             if (!host) {
                 return -EINVAL;
             }
 
-            ch = qemu_get_byte(f);
+            visit_type_uint8(v, &ch, "pagefill", &local_err);
             ram_handle_compressed(host, ch, TARGET_PAGE_SIZE);
         } else if (flags & RAM_SAVE_FLAG_PAGE) {
             void *host;
 
-            host = host_from_stream_offset(f, addr, flags);
+            host = host_from_stream_offset(&rse_hdr);
             if (!host) {
                 return -EINVAL;
             }
 
-            qemu_get_buffer(f, host, TARGET_PAGE_SIZE);
+            visit_type_buffer(v, host, TARGET_PAGE_SIZE, false, "page",
+                              &local_err);
         } else if (flags & RAM_SAVE_FLAG_XBZRLE) {
-            void *host = host_from_stream_offset(f, addr, flags);
+            void *host = host_from_stream_offset(&rse_hdr);
             if (!host) {
                 return -EINVAL;
             }
@@ -1144,7 +1155,12 @@  static int ram_load(QEMUFile *f, void *opaque, int version_id)
             ret = error;
             goto done;
         }
-    } while (!(flags & RAM_SAVE_FLAG_EOS));
+        visit_end_sequence_compat(v, "ramsecentry",
+                                  VISIT_SEQ_COMPAT_RAMSECENTRY, &local_err);
+    };
+    visit_end_sequence_compat(v, "ramseclist", VISIT_SEQ_COMPAT_RAMSECLIST,
+                              &local_err);
+    LOCAL_ERR_REPORT(return -EINVAL;);
 
 done:
     DPRINTF("Completed load of VM with exit code %d seq iteration "
diff --git a/hw/acpi/piix4.c b/hw/acpi/piix4.c
index 67dc075..a44f4b8 100644
--- a/hw/acpi/piix4.c
+++ b/hw/acpi/piix4.c
@@ -211,7 +211,8 @@  static int acpi_load_old(QEMUFile *f, void *opaque, int version_id)
     qemu_get_be16s(f, &s->ar.pm1.evt.en);
     qemu_get_be16s(f, &s->ar.pm1.cnt.cnt);
 
-    ret = vmstate_load_state(f, &vmstate_apm, &s->apm, 1);
+    ret = vmstate_load_state(qemu_file_get_tmp_visitor(f), &vmstate_apm,
+                             &s->apm, 1);
     if (ret) {
         return ret;
     }
@@ -229,7 +230,7 @@  static int acpi_load_old(QEMUFile *f, void *opaque, int version_id)
         qemu_get_be16s(f, &temp);
     }
 
-    ret = vmstate_load_state(f, &vmstate_pci_status,
+    ret = vmstate_load_state(qemu_file_get_tmp_visitor(f), &vmstate_pci_status,
         &s->acpi_pci_hotplug.acpi_pcihp_pci_status[ACPI_PCIHP_BSEL_DEFAULT], 1);
     return ret;
 }
diff --git a/hw/pci/pci.c b/hw/pci/pci.c
index e91f729..2009c0f 100644
--- a/hw/pci/pci.c
+++ b/hw/pci/pci.c
@@ -523,7 +523,8 @@  void pci_device_save(PCIDevice *s, QEMUFile *f)
 int pci_device_load(PCIDevice *s, QEMUFile *f)
 {
     int ret;
-    ret = vmstate_load_state(f, pci_get_vmstate(s), s, s->version_id);
+    ret = vmstate_load_state(qemu_file_get_tmp_visitor(f),
+                             pci_get_vmstate(s), s, s->version_id);
     /* Restore the interrupt status bit. */
     pci_update_irq_status(s);
     return ret;
diff --git a/hw/scsi/spapr_vscsi.c b/hw/scsi/spapr_vscsi.c
index 8e799e2..c50a741 100644
--- a/hw/scsi/spapr_vscsi.c
+++ b/hw/scsi/spapr_vscsi.c
@@ -643,7 +643,8 @@  static void *vscsi_load_request(QEMUFile *f, SCSIRequest *sreq)
     assert(!req->active);
 
     memset(req, 0, sizeof(*req));
-    rc = vmstate_load_state(f, &vmstate_spapr_vscsi_req, req, 1);
+    rc = vmstate_load_state(qemu_file_get_tmp_visitor(f),
+                            &vmstate_spapr_vscsi_req, req, 1);
     if (rc) {
         fprintf(stderr, "VSCSI: failed loading request tag#%u\n", sreq->tag);
         return NULL;
diff --git a/include/migration/vmstate.h b/include/migration/vmstate.h
index b66342b..d22c4e3 100644
--- a/include/migration/vmstate.h
+++ b/include/migration/vmstate.h
@@ -759,7 +759,7 @@  extern const VMStateInfo vmstate_info_bitmap;
 #define VMSTATE_END_OF_LIST()                                         \
     {}
 
-int vmstate_load_state(QEMUFile *f, const VMStateDescription *vmsd,
+int vmstate_load_state(Visitor *v, const VMStateDescription *vmsd,
                        void *opaque, int version_id);
 void vmstate_save_state(Visitor *v, const VMStateDescription *vmsd,
                         void *opaque);
diff --git a/savevm.c b/savevm.c
index 51efd4a..7431773 100644
--- a/savevm.c
+++ b/savevm.c
@@ -436,12 +436,41 @@  void vmstate_unregister(DeviceState *dev, const VMStateDescription *vmsd,
     }
 }
 
-static int vmstate_load(QEMUFile *f, SaveStateEntry *se, int version_id)
+static int vmstate_load(Visitor *v, SaveStateEntry *se, int version_id)
 {
+    int ret;
+    Error *local_err = NULL;
+
     if (!se->vmsd) {         /* Old style */
-        return se->ops->load_state(f, se->opaque, version_id);
+        QEMUFile *wrapperf;
+        bool is_iterative = se->ops->save_live_iterate != NULL;
+
+        /* Some visitors put old format things down separate QEMUFile's -
+         * but not the iterators which must use the visitors now -
+         * but TODO they need their interface changing
+         */
+
+        if (!is_iterative) {
+            visit_start_sequence_compat(v, "oldstate",
+                VISIT_SEQ_COMPAT_BLOB, &wrapperf, &local_err);
+        }
+        LOCAL_ERR_REPORT(return -EINVAL;);
+
+        ret = se->ops->load_state(is_iterative ? visitor_get_qemufile(v) :
+                                                 wrapperf,
+                                  se->opaque, version_id);
+        if (!is_iterative) {
+            visit_end_sequence_compat(v, "oldstate", VISIT_SEQ_COMPAT_BLOB,
+                                      &local_err);
+        }
+
+        LOCAL_ERR_REPORT(return -EINVAL;);
+
+        return ret;
     }
-    return vmstate_load_state(f, se->vmsd, se->opaque, version_id);
+    ret = vmstate_load_state(v, se->vmsd, se->opaque, version_id);
+
+    return ret;
 }
 
 static int vmstate_save(Visitor *v, SaveStateEntry *se, int version_id)
@@ -827,57 +856,69 @@  int qemu_loadvm_state(QEMUFile *f)
     QLIST_HEAD(, LoadStateEntry) loadvm_handlers =
         QLIST_HEAD_INITIALIZER(loadvm_handlers);
     LoadStateEntry *le, *new_le;
-    uint8_t section_type;
-    unsigned int v;
+    Error *local_err = NULL;
+    int32_t section_type;
+    unsigned int tmp;
     int ret;
 
     if (qemu_savevm_state_blocked(NULL)) {
         return -EINVAL;
     }
 
-    v = qemu_get_be32(f);
-    if (v != QEMU_VM_FILE_MAGIC) {
+    tmp = qemu_get_be32(f);
+    if (tmp != QEMU_VM_FILE_MAGIC) {
         return -EINVAL;
     }
 
-    v = qemu_get_be32(f);
-    if (v == QEMU_VM_FILE_VERSION_COMPAT) {
-        fprintf(stderr, "SaveVM v2 format is obsolete and don't work anymore\n");
+    tmp = qemu_get_be32(f);
+    if (tmp == QEMU_VM_FILE_VERSION_COMPAT) {
+        error_report("SaveVM v2 format is obsolete and don't work anymore");
         return -ENOTSUP;
     }
-    if (v != QEMU_VM_FILE_VERSION) {
+    if (tmp != QEMU_VM_FILE_VERSION) {
         return -ENOTSUP;
     }
 
-    while ((section_type = qemu_get_byte(f)) != QEMU_VM_EOF) {
-        uint32_t instance_id, version_id, section_id;
+    /* TODO: Here we should be able to figure out if it's a binary file
+     * or what and open the right type of visitor
+     */
+    QemuFileBinInputVisitor *qfbiv = qemu_file_bin_input_visitor_new(f);
+    Visitor *v = qemu_file_bin_input_get_visitor(qfbiv);
+    qemu_file_set_tmp_visitor(f, v);
+
+    visit_start_sequence_compat(v, "top", VISIT_SEQ_COMPAT_BYTE0TERM,
+                                NULL, &local_err);
+    while (visit_get_next_type(v, &section_type, NULL, "section", &local_err),
+           (!local_err && section_type != QEMU_VM_EOF)) {
         SaveStateEntry *se;
-        char idstr[257];
-        int len;
+        SectionHeader sh;
 
         switch (section_type) {
         case QEMU_VM_SECTION_START:
         case QEMU_VM_SECTION_FULL:
             /* Read section start */
-            section_id = qemu_get_be32(f);
-            len = qemu_get_byte(f);
-            qemu_get_buffer(f, (uint8_t *)idstr, len);
-            idstr[len] = 0;
-            instance_id = qemu_get_be32(f);
-            version_id = qemu_get_be32(f);
+            visit_start_sequence_compat(v,
+                  (section_type == QEMU_VM_SECTION_START) ?
+                      "secstart" : "secfull",
+                  VISIT_SEQ_COMPAT_SECTION_HEADER, &sh, &local_err);
+            if (local_err) {
+                ret = -EINVAL;
+                goto out;
+            }
 
             /* Find savevm section */
-            se = find_se(idstr, instance_id);
+            se = find_se(sh.idstr, sh.instance_id);
             if (se == NULL) {
-                fprintf(stderr, "Unknown savevm section or instance '%s' %d\n", idstr, instance_id);
+                error_report("Unknown savevm section or instance '%s' %d",
+                        sh.idstr, sh.instance_id);
                 ret = -EINVAL;
                 goto out;
             }
 
             /* Validate version */
-            if (version_id > se->version_id) {
+            if (sh.version_id > se->version_id) {
                 fprintf(stderr, "savevm: unsupported version %d for '%s' v%d\n",
-                        version_id, idstr, se->version_id);
+                        sh.version_id, sh.idstr, se->version_id);
                 ret = -EINVAL;
                 goto out;
             }
@@ -886,38 +927,51 @@  int qemu_loadvm_state(QEMUFile *f)
             le = g_malloc0(sizeof(*le));
 
             le->se = se;
-            le->section_id = section_id;
-            le->version_id = version_id;
+            le->section_id = sh.section_id;
+            le->version_id = sh.version_id;
             QLIST_INSERT_HEAD(&loadvm_handlers, le, entry);
 
-            ret = vmstate_load(f, le->se, le->version_id);
+            ret = vmstate_load(v, le->se, le->version_id);
             if (ret < 0) {
-                fprintf(stderr, "qemu: warning: error while loading state for instance 0x%x of device '%s'\n",
-                        instance_id, idstr);
+                error_report("warning: error while loading state for instance "
+                        "0x%x of device '%s'", sh.instance_id, sh.idstr);
                 goto out;
             }
+            visit_end_sequence_compat(v,
+                  (section_type == QEMU_VM_SECTION_START) ?
+                      "secstart" : "secfull",
+                  VISIT_SEQ_COMPAT_SECTION_HEADER, &local_err);
             break;
         case QEMU_VM_SECTION_PART:
         case QEMU_VM_SECTION_END:
-            section_id = qemu_get_be32(f);
+            visit_start_sequence_compat(v,
+                  (section_type == QEMU_VM_SECTION_PART) ? "secpart" : "secend",
+                  VISIT_SEQ_COMPAT_SECTION_MIN, &sh, &local_err);
+            if (local_err) {
+                ret = -EINVAL;
+                goto out;
+            }
 
             QLIST_FOREACH(le, &loadvm_handlers, entry) {
-                if (le->section_id == section_id) {
+                if (le->section_id == sh.section_id) {
                     break;
                 }
             }
             if (le == NULL) {
-                fprintf(stderr, "Unknown savevm section %d\n", section_id);
+                error_report("Unknown savevm section %d", sh.section_id);
                 ret = -EINVAL;
                 goto out;
             }
 
-            ret = vmstate_load(f, le->se, le->version_id);
+            ret = vmstate_load(v, le->se, le->version_id);
             if (ret < 0) {
-                fprintf(stderr, "qemu: warning: error while loading state section id %d\n",
-                        section_id);
+                error_report("warning: error while loading state section id %d",
+                             sh.section_id);
                 goto out;
             }
+            visit_end_sequence_compat(v,
+                  (section_type == QEMU_VM_SECTION_PART) ? "secpart" : "secend",
+                  VISIT_SEQ_COMPAT_SECTION_MIN, &local_err);
             break;
         default:
             fprintf(stderr, "Unknown savevm section type %d\n", section_type);
@@ -936,10 +990,17 @@  out:
         g_free(le);
     }
 
+    visit_end_sequence_compat(v, "top", VISIT_SEQ_COMPAT_BYTE0TERM, &local_err);
+    if (local_err) {
+        error_report("%s", error_get_pretty(local_err));
+        ret = -EINVAL;
+    }
+
     if (ret == 0) {
         ret = qemu_file_get_error(f);
     }
 
+    qemu_file_bin_input_visitor_cleanup(qfbiv);
     return ret;
 }
 
diff --git a/vmstate.c b/vmstate.c
index 46bf7b9..af332b7 100644
--- a/vmstate.c
+++ b/vmstate.c
@@ -9,7 +9,7 @@ 
 
 static void vmstate_subsection_save(Visitor *v, const VMStateDescription *vmsd,
                                     void *opaque);
-static int vmstate_subsection_load(QEMUFile *f, const VMStateDescription *vmsd,
+static int vmstate_subsection_load(Visitor *v, const VMStateDescription *vmsd,
                                    void *opaque);
 
 #define LOCAL_ERR_REPORT(fallout) \
@@ -19,11 +19,20 @@  static int vmstate_subsection_load(QEMUFile *f, const VMStateDescription *vmsd,
         fallout \
     }
 
-int vmstate_load_state(QEMUFile *f, const VMStateDescription *vmsd,
+int vmstate_load_state(Visitor *v, const VMStateDescription *vmsd,
                        void *opaque, int version_id)
 {
     VMStateField *field = vmsd->fields;
     int ret;
+    Error *local_err = NULL;
+    QEMUFile *wrapperf;
+    uint32_t tmp32;
+
+    /* BER type comes from the vmsd if it's set */
+    tmp32 = vmsd->ber_tag;
+    visit_start_sequence_compat(v, vmsd->name, VISIT_SEQ_COMPAT_VMSTATE,
+                                &tmp32, &local_err);
+    LOCAL_ERR_REPORT(return -EINVAL;);
 
     if (version_id > vmsd->version_id) {
         return -EINVAL;
@@ -32,7 +41,18 @@  int vmstate_load_state(QEMUFile *f, const VMStateDescription *vmsd,
         return -EINVAL;
     }
     if  (version_id < vmsd->minimum_version_id) {
-        return vmsd->load_state_old(f, opaque, version_id);
+        /* Some visitors put old format things down separate QEMUFile's */
+        visit_start_sequence_compat(v, vmsd->name, VISIT_SEQ_COMPAT_BLOB,
+                                    &wrapperf, &local_err);
+        LOCAL_ERR_REPORT(return -EINVAL;);
+
+        ret = vmsd->load_state_old(visitor_get_qemufile(v), opaque, version_id);
+
+        visit_end_sequence_compat(v, vmsd->name, VISIT_SEQ_COMPAT_BLOB,
+                                  &local_err);
+        LOCAL_ERR_REPORT(return -EINVAL;);
+
+        return ret;
     }
     if (vmsd->pre_load) {
         int ret = vmsd->pre_load(opaque);
@@ -69,6 +89,11 @@  int vmstate_load_state(QEMUFile *f, const VMStateDescription *vmsd,
             if (field->flags & VMS_POINTER) {
                 base_addr = *(void **)base_addr + field->start;
             }
+
+            if (n_elems > 1) {
+                visit_start_array(v, NULL, field->name, n_elems, 0, &local_err);
+            }
+
             for (i = 0; i < n_elems; i++) {
                 void *addr = base_addr + size * i;
 
@@ -76,30 +101,62 @@  int vmstate_load_state(QEMUFile *f, const VMStateDescription *vmsd,
                     addr = *(void **)addr;
                 }
                 if (field->flags & VMS_STRUCT) {
-                    ret = vmstate_load_state(f, field->vmsd, addr,
+                    ret = vmstate_load_state(v, field->vmsd, addr,
                                              field->vmsd->version_id);
                 } else {
-                    ret = field->info->get(f, addr, size);
+                    if (field->info->visit) {
+                        ret = field->info->visit(v, addr, field->name, size,
+                                                 &local_err);
+                    } else {
+                        /*
+                         * Some visitors put old format things down separate
+                         * QEMUFile's
+                         */
+                        visit_start_sequence_compat(v, field->name,
+                                                    VISIT_SEQ_COMPAT_BLOB,
+                                                    &wrapperf, &local_err);
+                        LOCAL_ERR_REPORT(return -EINVAL;);
 
+                        ret = field->info->get(wrapperf, addr, size);
+
+                        visit_end_sequence_compat(v, field->name,
+                                                  VISIT_SEQ_COMPAT_BLOB,
+                                                  &local_err);
+                        LOCAL_ERR_REPORT(return -EINVAL;);
+                    }
                 }
                 if (ret < 0) {
                     return ret;
                 }
+
+                if ((i+1) != n_elems) {
+                    visit_next_array(v, &local_err);
+                }
+            }
+
+            if (n_elems > 1) {
+                visit_end_array(v, &local_err);
             }
+
         }
         field++;
     }
-    ret = vmstate_subsection_load(f, vmsd, opaque);
+    ret = vmstate_subsection_load(v, vmsd, opaque);
     if (ret != 0) {
         return ret;
     }
     if (vmsd->post_load) {
-        return vmsd->post_load(opaque, version_id);
+        ret = vmsd->post_load(opaque, version_id);
     }
-    return 0;
+
+    visit_end_sequence_compat(v, vmsd->name, VISIT_SEQ_COMPAT_VMSTATE,
+                              &local_err);
+    LOCAL_ERR_REPORT(return -EINVAL;);
+
+    return ret;
 }
 
-void vmstate_save_state(QEMUFile *f, const VMStateDescription *vmsd,
+void vmstate_save_state(Visitor *v, const VMStateDescription *vmsd,
                         void *opaque)
 {
     VMStateField *field = vmsd->fields;
@@ -208,44 +265,56 @@  static const VMStateDescription *
     return NULL;
 }
 
-static int vmstate_subsection_load(QEMUFile *f, const VMStateDescription *vmsd,
+static int vmstate_subsection_load(Visitor *v, const VMStateDescription *vmsd,
                                    void *opaque)
 {
-    while (qemu_peek_byte(f, 0) == QEMU_VM_SUBSECTION) {
-        char idstr[256];
+    Error *local_err = NULL;
+    int32_t section_type;
+
+    if (!vmsd->subsections) {
+        /*
+         * If the type has no subsection defined at all then skip completely
+         * Note that this means if we have conditional subsections we will
+         * expect a subseclist, even if it's empty because the conditions are
+         * all false.
+         */
+        return 0;
+    }
+
+    visit_start_sequence_compat(v, "subseclist", VISIT_SEQ_COMPAT_SUBSECLIST,
+                                (char *)vmsd->name, &local_err);
+    while (visit_get_next_type(v, &section_type, NULL, "subsec", &local_err),
+           ((local_err == NULL) && (section_type == QEMU_VM_SUBSECTION))) {
         int ret;
-        uint8_t version_id, len, size;
         const VMStateDescription *sub_vmsd;
+        SectionHeader sh;
 
-        len = qemu_peek_byte(f, 1);
-        if (len < strlen(vmsd->name) + 1) {
-            /* subsection name has be be "section_name/a" */
-            return 0;
-        }
-        size = qemu_peek_buffer(f, (uint8_t *)idstr, len, 2);
-        if (size != len) {
-            return 0;
-        }
-        idstr[size] = 0;
+        visit_start_sequence_compat(v, "subsection",
+                                    VISIT_SEQ_COMPAT_SUBSECTION, &sh,
+                                    &local_err);
+        LOCAL_ERR_REPORT(return -EINVAL;);
 
-        if (strncmp(vmsd->name, idstr, strlen(vmsd->name)) != 0) {
-            /* it don't have a valid subsection name */
-            return 0;
-        }
-        sub_vmsd = vmstate_get_subsection(vmsd->subsections, idstr);
+        sub_vmsd = vmstate_subsection_name_lookup(vmsd->subsections, sh.idstr);
         if (sub_vmsd == NULL) {
+            error_report("vmstate_subsection_load: failed to find subsection "
+                         "%s in %s", sh.idstr, vmsd->name);
             return -ENOENT;
         }
-        qemu_file_skip(f, 1); /* subsection */
-        qemu_file_skip(f, 1); /* len */
-        qemu_file_skip(f, len); /* idstr */
-        version_id = qemu_get_be32(f);
 
-        ret = vmstate_load_state(f, sub_vmsd, opaque, version_id);
+        ret = vmstate_load_state(v, sub_vmsd, opaque, sh.version_id);
         if (ret) {
+            error_report("vmstate_subsection_load: load_state for %s failed "
+                         "in %s", sh.idstr, vmsd->name);
             return ret;
         }
+        visit_end_sequence_compat(v, "subsection", VISIT_SEQ_COMPAT_SUBSECTION,
+                                  &local_err);
     }
+    LOCAL_ERR_REPORT(return -EINVAL;);
+    visit_end_sequence_compat(v, "subseclist", VISIT_SEQ_COMPAT_SUBSECLIST,
+                              &local_err);
+    LOCAL_ERR_REPORT(return -EINVAL;);
+
     return 0;
 }