Adds a separate VMStateDescription for virtio-net that uses the
.early_setup feature. With this feature, we can migrate a virtio-net
device's state earlier, before the stop-and-copy phase.

Future patches will utilize this to move control plane operations out of
the stop-and-copy phase to reduce the downtime latency caused by
migrating a virtio-net device.

A VirtIODevMigration migration data structure is also introduced here
for VirtIODevices to help track the current state of a migration.

The early_load member is used to signal that a VirtIODevice is being
loaded early and to not throw an error regarding vring indices.
Inconsistent indices shouldn't be an issue for a device so long as the
final indices are eventually loaded before the device starts.

Signed-off-by: Jonah Palmer <[email protected]>
---
 hw/net/virtio-net.c        | 53 ++++++++++++++++++++++++++++++++++++++
 hw/virtio/virtio.c         | 14 +++++++++-
 include/hw/virtio/virtio.h |  9 +++++++
 3 files changed, 75 insertions(+), 1 deletion(-)

diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c
index 12b3456ca2..ddd6ed6e62 100644
--- a/hw/net/virtio-net.c
+++ b/hw/net/virtio-net.c
@@ -3864,6 +3864,37 @@ static bool failover_hide_primary_device(DeviceListener 
*listener,
     return qatomic_read(&n->failover_primary_hidden);
 }
 
+static int virtio_net_early_pre_load(void *opaque)
+{
+    VirtIONet *n = opaque;
+    VirtIODevice *vdev = VIRTIO_DEVICE(n);
+
+    vdev->migration->early_load = true;
+    return 0;
+}
+
+static int virtio_net_early_post_load(void *opaque, int version_id)
+{
+    VirtIONet *n = opaque;
+    VirtIODevice *vdev = VIRTIO_DEVICE(n);
+
+    vdev->migration->early_load = false;
+    return 0;
+}
+
+static const VMStateDescription vmstate_virtio_net_early = {
+    .name = "virtio-net-early",
+    .minimum_version_id = VIRTIO_NET_VM_VERSION,
+    .version_id = VIRTIO_NET_VM_VERSION,
+    .early_setup = true,
+    .pre_load = virtio_net_early_pre_load,
+    .post_load = virtio_net_early_post_load,
+    .fields = (const VMStateField[]) {
+        VMSTATE_VIRTIO_DEVICE,
+        VMSTATE_END_OF_LIST()
+    },
+};
+
 static void virtio_net_device_realize(DeviceState *dev, Error **errp)
 {
     VirtIODevice *vdev = VIRTIO_DEVICE(dev);
@@ -4046,6 +4077,21 @@ static void virtio_net_device_realize(DeviceState *dev, 
Error **errp)
             n->rss_data.specified_hash_types.on_bits |
             n->rss_data.specified_hash_types.auto_bits;
     }
+
+    if (n->early_mig) {
+        if (nc->peer && nc->peer->info->type == NET_CLIENT_DRIVER_VHOST_USER) {
+            /*
+             * vhost-user backend is not currently supported for the early
+             * migration path.
+             */
+            n->early_mig = false;
+        } else {
+            vdev->migration = g_new0(VirtIODevMigration, 1);
+            vdev->migration->early_load = false;
+
+            vmstate_register_any(VMSTATE_IF(n), &vmstate_virtio_net_early, n);
+        }
+    }
 }
 
 static void virtio_net_device_unrealize(DeviceState *dev)
@@ -4090,6 +4136,13 @@ static void virtio_net_device_unrealize(DeviceState *dev)
     g_free(n->rss_data.indirections_table);
     net_rx_pkt_uninit(n->rx_pkt);
     virtio_cleanup(vdev);
+
+    if (n->early_mig) {
+        g_free(vdev->migration);
+        vdev->migration = NULL;
+
+        vmstate_unregister(VMSTATE_IF(n), &vmstate_virtio_net_early, n);
+    }
 }
 
 static void virtio_net_reset(VirtIODevice *vdev)
diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c
index 8fcf6cfd0b..48de4a430b 100644
--- a/hw/virtio/virtio.c
+++ b/hw/virtio/virtio.c
@@ -3323,6 +3323,7 @@ virtio_load(VirtIODevice *vdev, QEMUFile *f, int 
version_id)
     int32_t config_len;
     uint32_t num;
     uint32_t features;
+    bool inconsistent_indices;
     BusState *qbus = qdev_get_parent_bus(DEVICE(vdev));
     VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus);
     VirtioDeviceClass *vdc = VIRTIO_DEVICE_GET_CLASS(vdev);
@@ -3460,6 +3461,14 @@ virtio_load(VirtIODevice *vdev, QEMUFile *f, int 
version_id)
         if (vdev->vq[i].vring.desc) {
             uint16_t nheads;
 
+            /*
+             * Ring indices will be inconsistent for a VMStateDescription
+             * performing an early load. This shouldn't be an issue as the
+             * final indices will get sent later once the source has been
+             * stopped.
+             */
+            inconsistent_indices = vdev->migration && 
vdev->migration->early_load;
+
             /*
              * VIRTIO-1 devices migrate desc, used, and avail ring addresses so
              * only the region cache needs to be set up.  Legacy devices need
@@ -3481,12 +3490,15 @@ virtio_load(VirtIODevice *vdev, QEMUFile *f, int 
version_id)
 
             nheads = vring_avail_idx(&vdev->vq[i]) - 
vdev->vq[i].last_avail_idx;
             /* Check it isn't doing strange things with descriptor numbers. */
-            if (nheads > vdev->vq[i].vring.num) {
+            if (!inconsistent_indices && nheads > vdev->vq[i].vring.num) {
                 virtio_error(vdev, "VQ %d size 0x%x Guest index 0x%x "
                              "inconsistent with Host index 0x%x: delta 0x%x",
                              i, vdev->vq[i].vring.num,
                              vring_avail_idx(&vdev->vq[i]),
                              vdev->vq[i].last_avail_idx, nheads);
+                inconsistent_indices = true;
+            }
+            if (inconsistent_indices) {
                 vdev->vq[i].used_idx = 0;
                 vdev->vq[i].shadow_avail_idx = 0;
                 vdev->vq[i].inuse = 0;
diff --git a/include/hw/virtio/virtio.h b/include/hw/virtio/virtio.h
index 6344bd7b68..4c886eb48b 100644
--- a/include/hw/virtio/virtio.h
+++ b/include/hw/virtio/virtio.h
@@ -99,6 +99,14 @@ enum virtio_device_endian {
     VIRTIO_DEVICE_ENDIAN_BIG,
 };
 
+/**
+ * struct VirtIODevMigration - Common VirtIODevice migration structure
+ * @early_load: Flag to indicate an early virtio_load for the device.
+ */
+typedef struct VirtIODevMigration {
+    bool early_load;
+} VirtIODevMigration;
+
 /**
  * struct VirtIODevice - common VirtIO structure
  * @name: name of the device
@@ -168,6 +176,7 @@ struct VirtIODevice
      */
     EventNotifier config_notifier;
     bool device_iotlb_enabled;
+    VirtIODevMigration *migration;
 };
 
 struct VirtioDeviceClass {
-- 
2.51.0


Reply via email to