From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from eggs.gnu.org ([2001:4830:134:3::10]:59707) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1WmJ6K-0005YC-1I for qemu-devel@nongnu.org; Mon, 19 May 2014 04:39:24 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1WmJ6B-0006YY-SM for qemu-devel@nongnu.org; Mon, 19 May 2014 04:39:15 -0400 Received: from e06smtp16.uk.ibm.com ([195.75.94.112]:33824) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1WmJ6B-0006YP-Iu for qemu-devel@nongnu.org; Mon, 19 May 2014 04:39:07 -0400 Received: from /spool/local by e06smtp16.uk.ibm.com with IBM ESMTP SMTP Gateway: Authorized Use Only! Violators will be prosecuted for from ; Mon, 19 May 2014 09:39:06 +0100 Received: from b06cxnps4075.portsmouth.uk.ibm.com (d06relay12.portsmouth.uk.ibm.com [9.149.109.197]) by d06dlp01.portsmouth.uk.ibm.com (Postfix) with ESMTP id 69BD117D8056 for ; Mon, 19 May 2014 09:40:15 +0100 (BST) Received: from d06av05.portsmouth.uk.ibm.com (d06av05.portsmouth.uk.ibm.com [9.149.37.229]) by b06cxnps4075.portsmouth.uk.ibm.com (8.13.8/8.13.8/NCO v10.0) with ESMTP id s4J8d4bN131374 for ; Mon, 19 May 2014 08:39:04 GMT Received: from d06av05.portsmouth.uk.ibm.com (localhost [127.0.0.1]) by d06av05.portsmouth.uk.ibm.com (8.14.4/8.14.4/NCO v10.0 AVout) with ESMTP id s4J8d3Wn031416 for ; Mon, 19 May 2014 02:39:04 -0600 From: Greg Kurz Date: Mon, 19 May 2014 10:39:01 +0200 Message-ID: <20140519083901.22955.65323.stgit@bahia.local> In-Reply-To: <20140519063132.22955.63563.stgit@bahia.local> References: <20140519063132.22955.63563.stgit@bahia.local> MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: 7bit Subject: [Qemu-devel] [PATCH RFC 7/8] virtio: add subsections to the migration stream List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , To: Kevin Wolf , Anthony Liguori , "Michael S. Tsirkin" , Stefan Hajnoczi , Amit Shah , Paolo Bonzini Cc: Juan Quintela , Fam Zheng , Alexander Graf , Andreas =?utf-8?q?F=C3=A4rber?= , qemu-devel@nongnu.org There is a need to add some more fields to VirtIODevice that should be migrated (broken status, endianness). The problem is that we do not want to break compatibility while adding a new feature... This issue has been addressed in the generic VMState code with the use of optional subsections. As a *temporary* alternative to port the whole virtio migration code to VMState, this patch mimics a similar subsectionning ability for virtio. Since each virtio device is streamed in its own section, the idea is to stream subsections between the end of the device section and the start of the next sections. This allows an older QEMU to complain and exit when fed with subsections: Unknown savevm section type 5 Error -22 while loading VM state The virtio subsections honor the same protocol than the VMState ones: +-------------------------+ |QEMU_VM_SUBSECTION (byte)| 1 byte +-------------------------+ | subsection name length | 1 byte +-------------------------+ | | : subsection name buffer : length bytes | | +-------------------------+ | version_id | 4 bytes (be32) +-------------------------+ | | : subsection data : | | +-------------------------+ The model differs from VMState though as we don't describe the fields in the subsections. As a consequence, there is no "field exists" concept and this impacts backwards migration. Suggested-by: Alexander Graf Signed-off-by: Greg Kurz --- hw/virtio/virtio.c | 78 +++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 77 insertions(+), 1 deletion(-) diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c index cf87b44..7fbad29 100644 --- a/hw/virtio/virtio.c +++ b/hw/virtio/virtio.c @@ -19,6 +19,7 @@ #include "hw/virtio/virtio.h" #include "qemu/atomic.h" #include "hw/virtio/virtio-bus.h" +#include "migration/migration.h" /* * The alignment to use between consumer and producer parts of vring. @@ -833,6 +834,79 @@ void virtio_notify_config(VirtIODevice *vdev) virtio_notify_vector(vdev, vdev->config_vector); } +typedef struct VirtIOSubsection { + const char *name; + int version_id; + void (*save)(VirtIODevice *vdev, QEMUFile *f); + int (*load)(VirtIODevice *vdev, QEMUFile *f); + int (*needed)(VirtIODevice *vdev); +} VirtIOSubsection; + +static const VirtIOSubsection virtio_subsection[] = { + { .name = NULL } +}; + +static void virtio_save_subsections(VirtIODevice *vdev, QEMUFile *f) +{ + int i; + + for (i = 0; virtio_subsection[i].name; i++) { + VirtIOSubsection sub = virtio_subsection[i]; + + if (sub.needed != NULL && (*sub.needed)(vdev)) { + const char *name = sub.name; + uint8_t len = strlen(name); + + qemu_put_byte(f, QEMU_VM_SUBSECTION); + qemu_put_byte(f, len); + qemu_put_buffer(f, (uint8_t *) name, len); + qemu_put_be32(f, sub.version_id); + (*sub.save)(vdev, f); + } + } +} + +static int virtio_load_subsections(VirtIODevice *vdev, QEMUFile *f) +{ + while (qemu_peek_byte(f, 0) == QEMU_VM_SUBSECTION) { + char idstr[256]; + uint8_t len, size; + int i; + + len = qemu_peek_byte(f, 1); + size = qemu_peek_buffer(f, (uint8_t *) idstr, len, 2); + if (size != len) { + break; + } + + idstr[size] = 0; + + for (i = 0; virtio_subsection[i].name; i++) { + VirtIOSubsection sub = virtio_subsection[i]; + + if (strcmp(sub.name, idstr) == 0) { + uint32_t version_id; + int ret; + + qemu_file_skip(f, 1); /* subsection */ + qemu_file_skip(f, 1); /* len */ + qemu_file_skip(f, len); /* idstr */ + + version_id = qemu_get_be32(f); + if (version_id > sub.version_id) { + return -EINVAL; + } + ret = (*sub.load)(vdev, f); + if (ret) { + return ret; + } + } + } + } + + return 0; +} + void virtio_save(VirtIODevice *vdev, QEMUFile *f) { BusState *qbus = qdev_get_parent_bus(DEVICE(vdev)); @@ -876,6 +950,8 @@ void virtio_save(VirtIODevice *vdev, QEMUFile *f) if (vdc->save != NULL) { vdc->save(vdev, f); } + + virtio_save_subsections(vdev, f); } int virtio_set_features(VirtIODevice *vdev, uint32_t val) @@ -967,7 +1043,7 @@ int virtio_load(VirtIODevice *vdev, QEMUFile *f, int version_id) return vdc->load(vdev, f, version_id); } - return 0; + return virtio_load_subsections(vdev, f); } void virtio_cleanup(VirtIODevice *vdev)