Commit c2f4030c42 for qemu.org

commit c2f4030c42da8344ac7c49de1c48d192f95a53ee
Author: Alexandr Moshkov <dtalexundeer@yandex-team.ru>
Date:   Mon Aug 3 12:28:53 2026 +0500

    vhost-user-blk: use GET_VRING_BASE_SKIP_DRAIN when inflight-migration is on

    Currently during live migration vhost-user-blk sends GET_VRING_BASE to
    stop vrings, which causes the back-end to drain all in-flight I/O before
    returning. This blocks the migration source until all I/O completes,
    adding significant downtime proportional to the I/O load.

    When inflight-migration is enabled, send GET_VRING_BASE_SKIP_DRAIN
    instead. This instructs the back-end to immediately suspend in-flight
    I/O and record it in the shared inflight region, which is then migrated
    to the destination host along with the rest of the device state.

    Since inflight-migration can only be toggled while the VM is running,
    i2ts value at stop time reliably reflects the intent set on a live VM.
    There is no need to check migration runstate to decide whether to skip
    draining. Using GET_VRING_BASE_SKIP_DRAIN on a regular VM stop is
    safe - the back-end records any in-flight requests in the shared
    inflight region, and they will be resubmitted when the VM starts again
    via SET_INFLIGHT_FD.

    If the back-end does not support
    VHOST_USER_PROTOCOL_F_GET_VRING_BASE_SKIP_DRAIN,
    migration is aborted with an error in pre_save rather than hitting an
    assert at runtime.

    Signed-off-by: Alexandr Moshkov <dtalexundeer@yandex-team.ru>
    Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
    Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
    Message-ID: <20260803072853.2920007-7-dtalexundeer@yandex-team.ru>

diff --git a/hw/block/vhost-user-blk.c b/hw/block/vhost-user-blk.c
index 3eaaa19719..3dade143c9 100644
--- a/hw/block/vhost-user-blk.c
+++ b/hw/block/vhost-user-blk.c
@@ -141,10 +141,7 @@ static bool vhost_user_blk_inflight_needed(void *opaque)
 {
     struct VHostUserBlk *s = opaque;

-    bool inflight_migration = virtio_has_feature(s->dev.protocol_features,
-                               VHOST_USER_PROTOCOL_F_GET_VRING_BASE_INFLIGHT);
-
-    return inflight_migration;
+    return s->inflight_migration;
 }


@@ -245,11 +242,13 @@ static int vhost_user_blk_stop(VirtIODevice *vdev)
         return 0;
     }

+    bool skip_drain = vhost_user_blk_inflight_needed(s);
+
     force_stop = s->skip_get_vring_base_on_force_shutdown &&
                  qemu_force_shutdown_requested();

     ret = force_stop ? vhost_dev_force_stop(&s->dev, vdev, true) :
-                       vhost_dev_stop(&s->dev, vdev, true, false);
+                       vhost_dev_stop(&s->dev, vdev, true, skip_drain);

     err = k->set_guest_notifiers(qbus->parent, s->dev.nvqs, false);
     if (err < 0) {
@@ -381,7 +380,6 @@ static int vhost_user_blk_connect(DeviceState *dev, Error **errp)
     vhost_dev_set_config_notifier(&s->dev, &blk_ops);

     s->vhost_user.supports_config = true;
-    s->vhost_user.supports_inflight_migration = s->inflight_migration;
     ret = vhost_dev_init(&s->dev, &s->vhost_user, VHOST_BACKEND_TYPE_USER, 0,
                          errp);
     if (ret < 0) {
@@ -608,10 +606,29 @@ static struct vhost_dev *vhost_user_blk_get_vhost(VirtIODevice *vdev)
     return &s->dev;
 }

+static bool vhost_user_blk_pre_save(void *opaque, Error **errp)
+{
+    VHostUserBlk *s = VHOST_USER_BLK(opaque);
+
+    bool inflight_migration_enabled = vhost_user_has_protocol_feature(&s->dev,
+                               VHOST_USER_PROTOCOL_F_GET_VRING_BASE_SKIP_DRAIN);
+
+    if (vhost_user_blk_inflight_needed(s) && !inflight_migration_enabled) {
+        error_setg(errp, "can't migrate vhost-user-blk device: "
+                         "backend doesn't support "
+                         "VHOST_USER_PROTOCOL_F_GET_VRING_BASE_SKIP_DRAIN "
+                         "protocol feature");
+        return false;
+    }
+
+    return true;
+}
+
 static const VMStateDescription vmstate_vhost_user_blk_inflight = {
     .name = "vhost-user-blk/inflight",
     .version_id = 1,
     .needed = vhost_user_blk_inflight_needed,
+    .pre_save_errp = vhost_user_blk_pre_save,
     .fields = (const VMStateField[]) {
         VMSTATE_VHOST_INFLIGHT_REGION(inflight, VHostUserBlk),
         VMSTATE_END_OF_LIST()
diff --git a/hw/virtio/vhost-user.c b/hw/virtio/vhost-user.c
index 5fb5d61afb..65889b0776 100644
--- a/hw/virtio/vhost-user.c
+++ b/hw/virtio/vhost-user.c
@@ -2596,8 +2596,7 @@ static int vhost_user_backend_init(struct vhost_dev *dev, void *opaque,
             }
         }

-        if (!u->user->supports_inflight_migration ||
-            !virtio_has_feature(protocol_features,
+        if (!virtio_has_feature(protocol_features,
                                 VHOST_USER_PROTOCOL_F_INFLIGHT_SHMFD)) {
             protocol_features &= ~(1ULL <<
                                VHOST_USER_PROTOCOL_F_GET_VRING_BASE_INFLIGHT);
diff --git a/include/hw/virtio/vhost-user.h b/include/hw/virtio/vhost-user.h
index 47c13f8677..78b4fba27f 100644
--- a/include/hw/virtio/vhost-user.h
+++ b/include/hw/virtio/vhost-user.h
@@ -73,7 +73,6 @@ typedef struct VhostUserState {
     GPtrArray *notifiers;
     int memory_slots;
     bool supports_config;
-    bool supports_inflight_migration;
 } VhostUserState;

 /**