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
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
>
>
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
© 2016 - 2025 Red Hat, Inc.