[PATCH v2 24/25] vhost-user-blk: support vhost backend migration

Vladimir Sementsov-Ogievskiy posted 25 patches 4 weeks, 1 day ago
[PATCH v2 24/25] vhost-user-blk: support vhost backend migration
Posted by Vladimir Sementsov-Ogievskiy 4 weeks, 1 day ago
Opt-out backend initialization code, and instead get the state
from migration channel (including inflight region).

Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
---
 hw/block/vhost-user-blk.c          | 129 ++++++++++++++++++++++++-----
 include/hw/virtio/vhost-user-blk.h |   2 +
 include/hw/virtio/vhost.h          |   3 +-
 3 files changed, 111 insertions(+), 23 deletions(-)

diff --git a/hw/block/vhost-user-blk.c b/hw/block/vhost-user-blk.c
index ffdd600526..a8fd90480a 100644
--- a/hw/block/vhost-user-blk.c
+++ b/hw/block/vhost-user-blk.c
@@ -17,6 +17,7 @@
  */
 
 #include "qemu/osdep.h"
+#include "qapi-types-run-state.h"
 #include "qapi/error.h"
 #include "qemu/error-report.h"
 #include "qemu/cutils.h"
@@ -31,7 +32,13 @@
 #include "hw/virtio/virtio-access.h"
 #include "system/system.h"
 #include "system/runstate.h"
+#include "chardev/char-backend-transfer.h"
 #include "trace.h"
+#include "migration/qemu-file.h"
+#include "migration/migration.h"
+#include "migration/options.h"
+#include "qemu/event_notifier.h"
+#include <sys/mman.h>
 
 static const int user_feature_bits[] = {
     VIRTIO_BLK_F_SIZE_MAX,
@@ -160,32 +167,35 @@ static int vhost_user_blk_start(VirtIODevice *vdev, Error **errp)
 
     s->dev.acked_features = vdev->guest_features;
 
-    ret = vhost_dev_prepare_inflight(&s->dev, vdev);
-    if (ret < 0) {
-        error_setg_errno(errp, -ret, "Error setting inflight format");
-        goto err_guest_notifiers;
-    }
-
-    if (!s->inflight->addr) {
-        ret = vhost_dev_get_inflight(&s->dev, s->queue_size, s->inflight);
+    if (!s->dev.backend_transfer) {
+        ret = vhost_dev_prepare_inflight(&s->dev, vdev);
         if (ret < 0) {
-            error_setg_errno(errp, -ret, "Error getting inflight");
+            error_setg_errno(errp, -ret, "Error setting inflight format");
             goto err_guest_notifiers;
         }
-    }
 
-    ret = vhost_dev_set_inflight(&s->dev, s->inflight);
-    if (ret < 0) {
-        error_setg_errno(errp, -ret, "Error setting inflight");
-        goto err_guest_notifiers;
-    }
+        if (!s->inflight->addr) {
+            ret = vhost_dev_get_inflight(&s->dev, s->queue_size, s->inflight);
+            if (ret < 0) {
+                error_setg_errno(errp, -ret, "Error getting inflight");
+                goto err_guest_notifiers;
+            }
+        }
 
-    /* guest_notifier_mask/pending not used yet, so just unmask
-     * everything here. virtio-pci will do the right thing by
-     * enabling/disabling irqfd.
-     */
-    for (i = 0; i < s->dev.nvqs; i++) {
-        vhost_virtqueue_mask(&s->dev, vdev, i, false);
+        ret = vhost_dev_set_inflight(&s->dev, s->inflight);
+        if (ret < 0) {
+            error_setg_errno(errp, -ret, "Error setting inflight");
+            goto err_guest_notifiers;
+        }
+
+        /*
+         * guest_notifier_mask/pending not used yet, so just unmask
+         * everything here. virtio-pci will do the right thing by
+         * enabling/disabling irqfd.
+         */
+        for (i = 0; i < s->dev.nvqs; i++) {
+            vhost_virtqueue_mask(&s->dev, vdev, i, false);
+        }
     }
 
     s->dev.vq_index_end = s->dev.nvqs;
@@ -232,6 +242,10 @@ static int vhost_user_blk_stop(VirtIODevice *vdev)
     force_stop = s->skip_get_vring_base_on_force_shutdown &&
                  qemu_force_shutdown_requested();
 
+    s->dev.backend_transfer = s->dev.backend_transfer ||
+        (runstate_check(RUN_STATE_FINISH_MIGRATE) &&
+         migrate_local_vhost_user_blk());
+
     ret = force_stop ? vhost_dev_force_stop(&s->dev, vdev, true) :
                        vhost_dev_stop(&s->dev, vdev, true);
 
@@ -391,6 +405,7 @@ static int vhost_user_blk_connect(DeviceState *dev,
     trace_vhost_user_blk_connect_in(vdev);
 
     assert(!s->connected);
+    assert(!s->dev.backend_transfer);
 
     ret = vhost_dev_connect(&s->dev, errp);
     if (ret < 0) {
@@ -464,6 +479,9 @@ static int vhost_user_blk_realize_connect(VHostUserBlk *s, Error **errp)
     DeviceState *dev = DEVICE(s);
     int ret;
 
+    assert(!s->connected);
+    assert(!s->dev.backend_transfer);
+
     ret = qemu_chr_fe_wait_connected(&s->chardev, errp);
     if (ret < 0) {
         return ret;
@@ -642,7 +660,13 @@ static bool vhost_user_blk_pre_incoming(void *opaque, Error **errp)
 {
     VHostUserBlk *s = VHOST_USER_BLK(opaque);
 
-    return vhost_user_blk_realize_connect(s, errp) == 0;
+    s->dev.backend_transfer = migrate_local_vhost_user_blk();
+
+    if (!s->dev.backend_transfer) {
+        return vhost_user_blk_realize_connect_loop(s, errp) >= 0;
+    }
+
+    return true;
 }
 
 static const VMStateDescription vmstate_vhost_user_blk = {
@@ -656,6 +680,64 @@ static const VMStateDescription vmstate_vhost_user_blk = {
     },
 };
 
+static bool vhost_user_needed(void *opaque)
+{
+    return migrate_local_vhost_user_blk();
+}
+
+static const VMStateDescription vmstate_vhost_user_blk_device = {
+    .name = "vhost-user-blk-device",
+    .version_id = 1,
+    .needed = vhost_user_needed,
+    .fields = (const VMStateField[]) {
+        VMSTATE_BACKEND_TRANSFER_CHARDEV(chardev, VHostUserBlk),
+        VMSTATE_BACKEND_TRANSFER_VHOST_INFLIGHT(inflight, VHostUserBlk),
+        VMSTATE_BACKEND_TRANSFER_VHOST_USER(dev, VHostUserBlk),
+        VMSTATE_BACKEND_TRANSFER_VHOST(dev, VHostUserBlk),
+        VMSTATE_END_OF_LIST()
+    },
+};
+
+static int vhost_user_blk_post_load(VirtIODevice *vdev)
+{
+    VHostUserBlk *s = VHOST_USER_BLK(vdev);
+    struct vhost_dev *hdev = vhost_user_blk_get_vhost(vdev);
+    DeviceState *dev = &s->parent_obj.parent_obj;
+
+    if (!hdev->backend_transfer) {
+        return 0;
+    }
+
+    s->connected = true;
+
+    memcpy(&s->blkcfg, vdev->config, vdev->config_len);
+
+    if (virtio_device_started(vdev, vdev->status)) {
+        int ret;
+        ret = vhost_user_blk_start(vdev, NULL);
+        if (ret < 0) {
+            return ret;
+        }
+    }
+
+    /* we're fully initialized, now we can operate, so add the handler */
+    qemu_chr_fe_set_handlers(&s->chardev,  NULL, NULL,
+                             vhost_user_blk_event, NULL, (void *)dev,
+                             NULL, true);
+
+    return 0;
+}
+
+static bool vhost_user_blk_skip_migration_log(VirtIODevice *vdev)
+{
+    /*
+     * Note that hdev->migrating_backend is false at this moment,
+     * as logging is being setup during outging migration setup stage,
+     * which is far before vm stop.
+     */
+    return migrate_local_vhost_user_blk();
+}
+
 static const Property vhost_user_blk_properties[] = {
     DEFINE_PROP_CHR_NO_CONNECT("chardev", VHostUserBlk, chardev),
     DEFINE_PROP_UINT16("num-queues", VHostUserBlk, num_queues,
@@ -688,6 +770,9 @@ static void vhost_user_blk_class_init(ObjectClass *klass, const void *data)
     vdc->set_status = vhost_user_blk_set_status;
     vdc->reset = vhost_user_blk_reset;
     vdc->get_vhost = vhost_user_blk_get_vhost;
+    vdc->vmsd = &vmstate_vhost_user_blk_device;
+    vdc->post_load = vhost_user_blk_post_load,
+    vdc->skip_vhost_migration_log = vhost_user_blk_skip_migration_log;
 }
 
 static const TypeInfo vhost_user_blk_info = {
diff --git a/include/hw/virtio/vhost-user-blk.h b/include/hw/virtio/vhost-user-blk.h
index a10f785672..b06f55fd6f 100644
--- a/include/hw/virtio/vhost-user-blk.h
+++ b/include/hw/virtio/vhost-user-blk.h
@@ -52,6 +52,8 @@ struct VHostUserBlk {
     bool started_vu;
 
     bool skip_get_vring_base_on_force_shutdown;
+
+    bool incoming_backend;
 };
 
 #endif
diff --git a/include/hw/virtio/vhost.h b/include/hw/virtio/vhost.h
index 55ad822848..13ca2c319f 100644
--- a/include/hw/virtio/vhost.h
+++ b/include/hw/virtio/vhost.h
@@ -592,7 +592,8 @@ static inline int vhost_load_backend_state(struct vhost_dev *dev, QEMUFile *f,
 
 extern const VMStateDescription vmstate_backend_transfer_vhost_inflight;
 #define VMSTATE_BACKEND_TRANSFER_VHOST_INFLIGHT(_field, _state) \
-    VMSTATE_STRUCT_POINTER(_field, _state, vmstate_inflight, \
+    VMSTATE_STRUCT_POINTER(_field, _state, \
+                           vmstate_backend_transfer_vhost_inflight, \
                            struct vhost_inflight)
 
 extern const VMStateDescription vmstate_vhost_dev;
-- 
2.48.1
Re: [PATCH v2 24/25] vhost-user-blk: support vhost backend migration
Posted by Raphael Norwitz 3 weeks, 3 days ago
Overall looks ok. A couple comments

On Thu, Oct 16, 2025 at 7:49 AM Vladimir Sementsov-Ogievskiy
<vsementsov@yandex-team.ru> wrote:
>
> Opt-out backend initialization code, and instead get the state
> from migration channel (including inflight region).
>
> Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
> ---
>  hw/block/vhost-user-blk.c          | 129 ++++++++++++++++++++++++-----
>  include/hw/virtio/vhost-user-blk.h |   2 +
>  include/hw/virtio/vhost.h          |   3 +-
>  3 files changed, 111 insertions(+), 23 deletions(-)
>
> diff --git a/hw/block/vhost-user-blk.c b/hw/block/vhost-user-blk.c
> index ffdd600526..a8fd90480a 100644
> --- a/hw/block/vhost-user-blk.c
> +++ b/hw/block/vhost-user-blk.c
> @@ -17,6 +17,7 @@
>   */
>
>  #include "qemu/osdep.h"
> +#include "qapi-types-run-state.h"
>  #include "qapi/error.h"
>  #include "qemu/error-report.h"
>  #include "qemu/cutils.h"
> @@ -31,7 +32,13 @@
>  #include "hw/virtio/virtio-access.h"
>  #include "system/system.h"
>  #include "system/runstate.h"
> +#include "chardev/char-backend-transfer.h"
>  #include "trace.h"
> +#include "migration/qemu-file.h"
> +#include "migration/migration.h"
> +#include "migration/options.h"
> +#include "qemu/event_notifier.h"
> +#include <sys/mman.h>
>
>  static const int user_feature_bits[] = {
>      VIRTIO_BLK_F_SIZE_MAX,
> @@ -160,32 +167,35 @@ static int vhost_user_blk_start(VirtIODevice *vdev, Error **errp)
>
>      s->dev.acked_features = vdev->guest_features;
>
> -    ret = vhost_dev_prepare_inflight(&s->dev, vdev);
> -    if (ret < 0) {
> -        error_setg_errno(errp, -ret, "Error setting inflight format");
> -        goto err_guest_notifiers;
> -    }
> -
> -    if (!s->inflight->addr) {
> -        ret = vhost_dev_get_inflight(&s->dev, s->queue_size, s->inflight);
> +    if (!s->dev.backend_transfer) {
> +        ret = vhost_dev_prepare_inflight(&s->dev, vdev);
>          if (ret < 0) {
> -            error_setg_errno(errp, -ret, "Error getting inflight");
> +            error_setg_errno(errp, -ret, "Error setting inflight format");
>              goto err_guest_notifiers;
>          }
> -    }
>
> -    ret = vhost_dev_set_inflight(&s->dev, s->inflight);
> -    if (ret < 0) {
> -        error_setg_errno(errp, -ret, "Error setting inflight");
> -        goto err_guest_notifiers;
> -    }
> +        if (!s->inflight->addr) {
> +            ret = vhost_dev_get_inflight(&s->dev, s->queue_size, s->inflight);
> +            if (ret < 0) {
> +                error_setg_errno(errp, -ret, "Error getting inflight");
> +                goto err_guest_notifiers;
> +            }
> +        }
>
> -    /* guest_notifier_mask/pending not used yet, so just unmask
> -     * everything here. virtio-pci will do the right thing by
> -     * enabling/disabling irqfd.
> -     */
> -    for (i = 0; i < s->dev.nvqs; i++) {
> -        vhost_virtqueue_mask(&s->dev, vdev, i, false);
> +        ret = vhost_dev_set_inflight(&s->dev, s->inflight);
> +        if (ret < 0) {
> +            error_setg_errno(errp, -ret, "Error setting inflight");
> +            goto err_guest_notifiers;
> +        }
> +
> +        /*
> +         * guest_notifier_mask/pending not used yet, so just unmask
> +         * everything here. virtio-pci will do the right thing by
> +         * enabling/disabling irqfd.
> +         */
> +        for (i = 0; i < s->dev.nvqs; i++) {
> +            vhost_virtqueue_mask(&s->dev, vdev, i, false);
> +        }
>      }
>
>      s->dev.vq_index_end = s->dev.nvqs;
> @@ -232,6 +242,10 @@ static int vhost_user_blk_stop(VirtIODevice *vdev)
>      force_stop = s->skip_get_vring_base_on_force_shutdown &&
>                   qemu_force_shutdown_requested();
>
> +    s->dev.backend_transfer = s->dev.backend_transfer ||
> +        (runstate_check(RUN_STATE_FINISH_MIGRATE) &&
> +         migrate_local_vhost_user_blk());
> +
>      ret = force_stop ? vhost_dev_force_stop(&s->dev, vdev, true) :
>                         vhost_dev_stop(&s->dev, vdev, true);
>
> @@ -391,6 +405,7 @@ static int vhost_user_blk_connect(DeviceState *dev,
>      trace_vhost_user_blk_connect_in(vdev);
>
>      assert(!s->connected);
> +    assert(!s->dev.backend_transfer);
>
>      ret = vhost_dev_connect(&s->dev, errp);
>      if (ret < 0) {
> @@ -464,6 +479,9 @@ static int vhost_user_blk_realize_connect(VHostUserBlk *s, Error **errp)
>      DeviceState *dev = DEVICE(s);
>      int ret;
>
> +    assert(!s->connected);
> +    assert(!s->dev.backend_transfer);
> +
>      ret = qemu_chr_fe_wait_connected(&s->chardev, errp);
>      if (ret < 0) {
>          return ret;
> @@ -642,7 +660,13 @@ static bool vhost_user_blk_pre_incoming(void *opaque, Error **errp)
>  {
>      VHostUserBlk *s = VHOST_USER_BLK(opaque);
>
> -    return vhost_user_blk_realize_connect(s, errp) == 0;
> +    s->dev.backend_transfer = migrate_local_vhost_user_blk();
> +
> +    if (!s->dev.backend_transfer) {
> +        return vhost_user_blk_realize_connect_loop(s, errp) >= 0;
> +    }
> +
> +    return true;
>  }
>
>  static const VMStateDescription vmstate_vhost_user_blk = {
> @@ -656,6 +680,64 @@ static const VMStateDescription vmstate_vhost_user_blk = {
>      },
>  };
>

Rename vhost_user_blk_needed()?

> +static bool vhost_user_needed(void *opaque)
> +{
> +    return migrate_local_vhost_user_blk();
> +}
> +
> +static const VMStateDescription vmstate_vhost_user_blk_device = {
> +    .name = "vhost-user-blk-device",
> +    .version_id = 1,
> +    .needed = vhost_user_needed,
> +    .fields = (const VMStateField[]) {
> +        VMSTATE_BACKEND_TRANSFER_CHARDEV(chardev, VHostUserBlk),
> +        VMSTATE_BACKEND_TRANSFER_VHOST_INFLIGHT(inflight, VHostUserBlk),
> +        VMSTATE_BACKEND_TRANSFER_VHOST_USER(dev, VHostUserBlk),
> +        VMSTATE_BACKEND_TRANSFER_VHOST(dev, VHostUserBlk),
> +        VMSTATE_END_OF_LIST()
> +    },
> +};
> +
> +static int vhost_user_blk_post_load(VirtIODevice *vdev)
> +{
> +    VHostUserBlk *s = VHOST_USER_BLK(vdev);
> +    struct vhost_dev *hdev = vhost_user_blk_get_vhost(vdev);
> +    DeviceState *dev = &s->parent_obj.parent_obj;
> +
> +    if (!hdev->backend_transfer) {
> +        return 0;
> +    }
> +
> +    s->connected = true;
> +
> +    memcpy(&s->blkcfg, vdev->config, vdev->config_len);
> +
> +    if (virtio_device_started(vdev, vdev->status)) {
> +        int ret;
> +        ret = vhost_user_blk_start(vdev, NULL);
> +        if (ret < 0) {
> +            return ret;
> +        }
> +    }
> +
> +    /* we're fully initialized, now we can operate, so add the handler */
> +    qemu_chr_fe_set_handlers(&s->chardev,  NULL, NULL,
> +                             vhost_user_blk_event, NULL, (void *)dev,
> +                             NULL, true);
> +
> +    return 0;
> +}
> +
> +static bool vhost_user_blk_skip_migration_log(VirtIODevice *vdev)
> +{
> +    /*
> +     * Note that hdev->migrating_backend is false at this moment,
> +     * as logging is being setup during outging migration setup stage,
> +     * which is far before vm stop.
> +     */
> +    return migrate_local_vhost_user_blk();
> +}
> +
>  static const Property vhost_user_blk_properties[] = {
>      DEFINE_PROP_CHR_NO_CONNECT("chardev", VHostUserBlk, chardev),
>      DEFINE_PROP_UINT16("num-queues", VHostUserBlk, num_queues,
> @@ -688,6 +770,9 @@ static void vhost_user_blk_class_init(ObjectClass *klass, const void *data)
>      vdc->set_status = vhost_user_blk_set_status;
>      vdc->reset = vhost_user_blk_reset;
>      vdc->get_vhost = vhost_user_blk_get_vhost;
> +    vdc->vmsd = &vmstate_vhost_user_blk_device;
> +    vdc->post_load = vhost_user_blk_post_load,
> +    vdc->skip_vhost_migration_log = vhost_user_blk_skip_migration_log;
>  }
>
>  static const TypeInfo vhost_user_blk_info = {
> diff --git a/include/hw/virtio/vhost-user-blk.h b/include/hw/virtio/vhost-user-blk.h
> index a10f785672..b06f55fd6f 100644
> --- a/include/hw/virtio/vhost-user-blk.h
> +++ b/include/hw/virtio/vhost-user-blk.h
> @@ -52,6 +52,8 @@ struct VHostUserBlk {
>      bool started_vu;
>
>      bool skip_get_vring_base_on_force_shutdown;

Why do we need incoming_backend? Looks like it is unused.


> +
> +    bool incoming_backend;
>  };
>
>  #endif
> diff --git a/include/hw/virtio/vhost.h b/include/hw/virtio/vhost.h
> index 55ad822848..13ca2c319f 100644
> --- a/include/hw/virtio/vhost.h
> +++ b/include/hw/virtio/vhost.h
> @@ -592,7 +592,8 @@ static inline int vhost_load_backend_state(struct vhost_dev *dev, QEMUFile *f,
>
>  extern const VMStateDescription vmstate_backend_transfer_vhost_inflight;
>  #define VMSTATE_BACKEND_TRANSFER_VHOST_INFLIGHT(_field, _state) \
> -    VMSTATE_STRUCT_POINTER(_field, _state, vmstate_inflight, \
> +    VMSTATE_STRUCT_POINTER(_field, _state, \
> +                           vmstate_backend_transfer_vhost_inflight, \
>                             struct vhost_inflight)
>
>  extern const VMStateDescription vmstate_vhost_dev;
> --
> 2.48.1
>
>
Re: [PATCH v2 24/25] vhost-user-blk: support vhost backend migration
Posted by Vladimir Sementsov-Ogievskiy 3 weeks, 3 days ago
On 21.10.25 02:53, Raphael Norwitz wrote:
> Overall looks ok. A couple comments
> 
> On Thu, Oct 16, 2025 at 7:49 AM Vladimir Sementsov-Ogievskiy
> <vsementsov@yandex-team.ru> wrote:
>>
>> Opt-out backend initialization code, and instead get the state
>> from migration channel (including inflight region).
>>
>> Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
>> ---
>>   hw/block/vhost-user-blk.c          | 129 ++++++++++++++++++++++++-----
>>   include/hw/virtio/vhost-user-blk.h |   2 +
>>   include/hw/virtio/vhost.h          |   3 +-
>>   3 files changed, 111 insertions(+), 23 deletions(-)
>>
>> diff --git a/hw/block/vhost-user-blk.c b/hw/block/vhost-user-blk.c
>> index ffdd600526..a8fd90480a 100644
>> --- a/hw/block/vhost-user-blk.c
>> +++ b/hw/block/vhost-user-blk.c
>> @@ -17,6 +17,7 @@
>>    */

[..]

>> @@ -656,6 +680,64 @@ static const VMStateDescription vmstate_vhost_user_blk = {
>>       },
>>   };
>>
> 
> Rename vhost_user_blk_needed()?

Yes, will fix

> 
>> +static bool vhost_user_needed(void *opaque)
>> +{
>> +    return migrate_local_vhost_user_blk();
>> +}
>> +
>> +static const VMStateDescription vmstate_vhost_user_blk_device = {
>> +    .name = "vhost-user-blk-device",
>> +    .version_id = 1,
>> +    .needed = vhost_user_needed,
>> +    .fields = (const VMStateField[]) {
>> +        VMSTATE_BACKEND_TRANSFER_CHARDEV(chardev, VHostUserBlk),
>> +        VMSTATE_BACKEND_TRANSFER_VHOST_INFLIGHT(inflight, VHostUserBlk),
>> +        VMSTATE_BACKEND_TRANSFER_VHOST_USER(dev, VHostUserBlk),
>> +        VMSTATE_BACKEND_TRANSFER_VHOST(dev, VHostUserBlk),
>> +        VMSTATE_END_OF_LIST()
>> +    },
>> +};

[..]

>> diff --git a/include/hw/virtio/vhost-user-blk.h b/include/hw/virtio/vhost-user-blk.h
>> index a10f785672..b06f55fd6f 100644
>> --- a/include/hw/virtio/vhost-user-blk.h
>> +++ b/include/hw/virtio/vhost-user-blk.h
>> @@ -52,6 +52,8 @@ struct VHostUserBlk {
>>       bool started_vu;
>>
>>       bool skip_get_vring_base_on_force_shutdown;
> 
> Why do we need incoming_backend? Looks like it is unused.
> 

Oops right. Forget to delete, it was used in v1.

> 
>> +
>> +    bool incoming_backend;
>>   };
>>
>>   #endif


Thanks a lot for reviewing!

-- 
Best regards,
Vladimir