Prepare for future inflight region migration for vhost-user-blk.
We need to migrate size, queue_size, and inner buffer.
So firstly it migrate size and queue_size fields, then allocate memory for buffer with
migrated size, then migrate inner buffer itself.
Signed-off-by: Alexandr Moshkov <dtalexundeer@yandex-team.ru>
---
hw/virtio/vhost.c | 57 +++++++++++++++++++++++++++++++++++++++
include/hw/virtio/vhost.h | 6 +++++
2 files changed, 63 insertions(+)
diff --git a/hw/virtio/vhost.c b/hw/virtio/vhost.c
index c46203eb9c..f655c53b67 100644
--- a/hw/virtio/vhost.c
+++ b/hw/virtio/vhost.c
@@ -2028,6 +2028,63 @@ const VMStateDescription vmstate_backend_transfer_vhost_inflight = {
}
};
+static int vhost_inflight_buffer_pre_load(void *opaque, Error **errp)
+{
+ info_report("vhost_inflight_region_buffer_pre_load");
+ struct vhost_inflight *inflight = opaque;
+
+ int fd = -1;
+ void *addr = qemu_memfd_alloc("vhost-inflight", inflight->size,
+ F_SEAL_GROW | F_SEAL_SHRINK | F_SEAL_SEAL,
+ &fd, errp);
+ if (*errp) {
+ return -ENOMEM;
+ }
+
+ inflight->offset = 0;
+ inflight->addr = addr;
+ inflight->fd = fd;
+
+ return 0;
+}
+
+const VMStateDescription vmstate_vhost_inflight_region_buffer = {
+ .name = "vhost-inflight-region/buffer",
+ .pre_load_errp = vhost_inflight_buffer_pre_load,
+ .fields = (const VMStateField[]) {
+ VMSTATE_VBUFFER_UINT64(addr, struct vhost_inflight, 0, NULL, size),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static int vhost_inflight_region_post_load(void *opaque,
+ int version_id,
+ Error **errp)
+{
+ struct vhost_inflight *inflight = opaque;
+
+ if (inflight->addr == NULL) {
+ error_setg(errp, "inflight buffer subsection has not been loaded");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+const VMStateDescription vmstate_vhost_inflight_region = {
+ .name = "vhost-inflight-region",
+ .post_load_errp = vhost_inflight_region_post_load,
+ .fields = (const VMStateField[]) {
+ VMSTATE_UINT64(size, struct vhost_inflight),
+ VMSTATE_UINT16(queue_size, struct vhost_inflight),
+ VMSTATE_END_OF_LIST()
+ },
+ .subsections = (const VMStateDescription * const []) {
+ &vmstate_vhost_inflight_region_buffer,
+ NULL
+ }
+};
+
const VMStateDescription vmstate_vhost_virtqueue = {
.name = "vhost-virtqueue",
.fields = (const VMStateField[]) {
diff --git a/include/hw/virtio/vhost.h b/include/hw/virtio/vhost.h
index 13ca2c319f..dd552de91f 100644
--- a/include/hw/virtio/vhost.h
+++ b/include/hw/virtio/vhost.h
@@ -596,6 +596,12 @@ extern const VMStateDescription vmstate_backend_transfer_vhost_inflight;
vmstate_backend_transfer_vhost_inflight, \
struct vhost_inflight)
+extern const VMStateDescription vmstate_vhost_inflight_region;
+#define VMSTATE_VHOST_INFLIGHT_REGION(_field, _state) \
+ VMSTATE_STRUCT_POINTER(_field, _state, \
+ vmstate_vhost_inflight_region, \
+ struct vhost_inflight)
+
extern const VMStateDescription vmstate_vhost_dev;
#define VMSTATE_BACKEND_TRANSFER_VHOST(_field, _state) \
VMSTATE_STRUCT(_field, _state, 0, vmstate_vhost_dev, struct vhost_dev)
--
2.34.1
On Tue, Jan 13, 2026 at 02:58:17PM +0500, Alexandr Moshkov wrote:
> Prepare for future inflight region migration for vhost-user-blk.
> We need to migrate size, queue_size, and inner buffer.
>
> So firstly it migrate size and queue_size fields, then allocate memory for buffer with
> migrated size, then migrate inner buffer itself.
>
> Signed-off-by: Alexandr Moshkov <dtalexundeer@yandex-team.ru>
> ---
> hw/virtio/vhost.c | 57 +++++++++++++++++++++++++++++++++++++++
> include/hw/virtio/vhost.h | 6 +++++
> 2 files changed, 63 insertions(+)
>
> diff --git a/hw/virtio/vhost.c b/hw/virtio/vhost.c
> index c46203eb9c..f655c53b67 100644
> --- a/hw/virtio/vhost.c
> +++ b/hw/virtio/vhost.c
> @@ -2028,6 +2028,63 @@ const VMStateDescription vmstate_backend_transfer_vhost_inflight = {
> }
> };
>
> +static int vhost_inflight_buffer_pre_load(void *opaque, Error **errp)
> +{
> + info_report("vhost_inflight_region_buffer_pre_load");
> + struct vhost_inflight *inflight = opaque;
> +
> + int fd = -1;
> + void *addr = qemu_memfd_alloc("vhost-inflight", inflight->size,
> + F_SEAL_GROW | F_SEAL_SHRINK | F_SEAL_SEAL,
> + &fd, errp);
> + if (*errp) {
> + return -ENOMEM;
> + }
> +
> + inflight->offset = 0;
> + inflight->addr = addr;
> + inflight->fd = fd;
> +
> + return 0;
> +}
> +
> +const VMStateDescription vmstate_vhost_inflight_region_buffer = {
> + .name = "vhost-inflight-region/buffer",
> + .pre_load_errp = vhost_inflight_buffer_pre_load,
> + .fields = (const VMStateField[]) {
> + VMSTATE_VBUFFER_UINT64(addr, struct vhost_inflight, 0, NULL, size),
> + VMSTATE_END_OF_LIST()
> + }
> +};
> +
> +static int vhost_inflight_region_post_load(void *opaque,
> + int version_id,
> + Error **errp)
> +{
> + struct vhost_inflight *inflight = opaque;
> +
> + if (inflight->addr == NULL) {
IIUC this can never happen because pre_load() must trigger before
post_load(), and when reaching post_load() it means pre_load() must have
succeeded..
So, IIUC we can drop this post_load() completely (or assert addr in
pre_load instead).
> + error_setg(errp, "inflight buffer subsection has not been loaded");
> + return -EINVAL;
> + }
> +
> + return 0;
> +}
> +
> +const VMStateDescription vmstate_vhost_inflight_region = {
> + .name = "vhost-inflight-region",
> + .post_load_errp = vhost_inflight_region_post_load,
> + .fields = (const VMStateField[]) {
> + VMSTATE_UINT64(size, struct vhost_inflight),
> + VMSTATE_UINT16(queue_size, struct vhost_inflight),
> + VMSTATE_END_OF_LIST()
> + },
> + .subsections = (const VMStateDescription * const []) {
> + &vmstate_vhost_inflight_region_buffer,
> + NULL
> + }
> +};
> +
> const VMStateDescription vmstate_vhost_virtqueue = {
> .name = "vhost-virtqueue",
> .fields = (const VMStateField[]) {
> diff --git a/include/hw/virtio/vhost.h b/include/hw/virtio/vhost.h
> index 13ca2c319f..dd552de91f 100644
> --- a/include/hw/virtio/vhost.h
> +++ b/include/hw/virtio/vhost.h
> @@ -596,6 +596,12 @@ extern const VMStateDescription vmstate_backend_transfer_vhost_inflight;
> vmstate_backend_transfer_vhost_inflight, \
> struct vhost_inflight)
>
> +extern const VMStateDescription vmstate_vhost_inflight_region;
> +#define VMSTATE_VHOST_INFLIGHT_REGION(_field, _state) \
> + VMSTATE_STRUCT_POINTER(_field, _state, \
> + vmstate_vhost_inflight_region, \
> + struct vhost_inflight)
> +
> extern const VMStateDescription vmstate_vhost_dev;
> #define VMSTATE_BACKEND_TRANSFER_VHOST(_field, _state) \
> VMSTATE_STRUCT(_field, _state, 0, vmstate_vhost_dev, struct vhost_dev)
> --
> 2.34.1
>
--
Peter Xu
On Wed, Jan 14, 2026 at 02:15:27PM -0500, Peter Xu wrote:
> On Tue, Jan 13, 2026 at 02:58:17PM +0500, Alexandr Moshkov wrote:
> > Prepare for future inflight region migration for vhost-user-blk.
> > We need to migrate size, queue_size, and inner buffer.
> >
> > So firstly it migrate size and queue_size fields, then allocate memory for buffer with
> > migrated size, then migrate inner buffer itself.
> >
> > Signed-off-by: Alexandr Moshkov <dtalexundeer@yandex-team.ru>
> > ---
> > hw/virtio/vhost.c | 57 +++++++++++++++++++++++++++++++++++++++
> > include/hw/virtio/vhost.h | 6 +++++
> > 2 files changed, 63 insertions(+)
> >
> > diff --git a/hw/virtio/vhost.c b/hw/virtio/vhost.c
> > index c46203eb9c..f655c53b67 100644
> > --- a/hw/virtio/vhost.c
> > +++ b/hw/virtio/vhost.c
> > @@ -2028,6 +2028,63 @@ const VMStateDescription vmstate_backend_transfer_vhost_inflight = {
> > }
> > };
> >
> > +static int vhost_inflight_buffer_pre_load(void *opaque, Error **errp)
> > +{
> > + info_report("vhost_inflight_region_buffer_pre_load");
> > + struct vhost_inflight *inflight = opaque;
> > +
> > + int fd = -1;
> > + void *addr = qemu_memfd_alloc("vhost-inflight", inflight->size,
> > + F_SEAL_GROW | F_SEAL_SHRINK | F_SEAL_SEAL,
> > + &fd, errp);
> > + if (*errp) {
> > + return -ENOMEM;
> > + }
> > +
> > + inflight->offset = 0;
> > + inflight->addr = addr;
> > + inflight->fd = fd;
> > +
> > + return 0;
> > +}
> > +
> > +const VMStateDescription vmstate_vhost_inflight_region_buffer = {
> > + .name = "vhost-inflight-region/buffer",
> > + .pre_load_errp = vhost_inflight_buffer_pre_load,
> > + .fields = (const VMStateField[]) {
> > + VMSTATE_VBUFFER_UINT64(addr, struct vhost_inflight, 0, NULL, size),
> > + VMSTATE_END_OF_LIST()
> > + }
> > +};
> > +
> > +static int vhost_inflight_region_post_load(void *opaque,
> > + int version_id,
> > + Error **errp)
> > +{
> > + struct vhost_inflight *inflight = opaque;
> > +
> > + if (inflight->addr == NULL) {
>
> IIUC this can never happen because pre_load() must trigger before
> post_load(), and when reaching post_load() it means pre_load() must have
> succeeded..
>
> So, IIUC we can drop this post_load() completely (or assert addr in
> pre_load instead).
I asked for this input validation check. If the migration stream is
inconsistent (e.g. broken or malicious source QEMU), then the subsection
might be missing but size could be non-zero. The destination QEMU should
fail cleanly and not run into undefined behavior.
Stefan
On Wed, Jan 14, 2026 at 04:38:17PM -0500, Stefan Hajnoczi wrote:
> On Wed, Jan 14, 2026 at 02:15:27PM -0500, Peter Xu wrote:
> > On Tue, Jan 13, 2026 at 02:58:17PM +0500, Alexandr Moshkov wrote:
> > > Prepare for future inflight region migration for vhost-user-blk.
> > > We need to migrate size, queue_size, and inner buffer.
> > >
> > > So firstly it migrate size and queue_size fields, then allocate memory for buffer with
> > > migrated size, then migrate inner buffer itself.
> > >
> > > Signed-off-by: Alexandr Moshkov <dtalexundeer@yandex-team.ru>
> > > ---
> > > hw/virtio/vhost.c | 57 +++++++++++++++++++++++++++++++++++++++
> > > include/hw/virtio/vhost.h | 6 +++++
> > > 2 files changed, 63 insertions(+)
> > >
> > > diff --git a/hw/virtio/vhost.c b/hw/virtio/vhost.c
> > > index c46203eb9c..f655c53b67 100644
> > > --- a/hw/virtio/vhost.c
> > > +++ b/hw/virtio/vhost.c
> > > @@ -2028,6 +2028,63 @@ const VMStateDescription vmstate_backend_transfer_vhost_inflight = {
> > > }
> > > };
> > >
> > > +static int vhost_inflight_buffer_pre_load(void *opaque, Error **errp)
> > > +{
> > > + info_report("vhost_inflight_region_buffer_pre_load");
> > > + struct vhost_inflight *inflight = opaque;
> > > +
> > > + int fd = -1;
> > > + void *addr = qemu_memfd_alloc("vhost-inflight", inflight->size,
> > > + F_SEAL_GROW | F_SEAL_SHRINK | F_SEAL_SEAL,
> > > + &fd, errp);
> > > + if (*errp) {
> > > + return -ENOMEM;
> > > + }
> > > +
> > > + inflight->offset = 0;
> > > + inflight->addr = addr;
> > > + inflight->fd = fd;
> > > +
> > > + return 0;
> > > +}
> > > +
> > > +const VMStateDescription vmstate_vhost_inflight_region_buffer = {
> > > + .name = "vhost-inflight-region/buffer",
> > > + .pre_load_errp = vhost_inflight_buffer_pre_load,
> > > + .fields = (const VMStateField[]) {
> > > + VMSTATE_VBUFFER_UINT64(addr, struct vhost_inflight, 0, NULL, size),
> > > + VMSTATE_END_OF_LIST()
> > > + }
> > > +};
> > > +
> > > +static int vhost_inflight_region_post_load(void *opaque,
> > > + int version_id,
> > > + Error **errp)
> > > +{
> > > + struct vhost_inflight *inflight = opaque;
> > > +
> > > + if (inflight->addr == NULL) {
> >
> > IIUC this can never happen because pre_load() must trigger before
> > post_load(), and when reaching post_load() it means pre_load() must have
> > succeeded..
> >
> > So, IIUC we can drop this post_load() completely (or assert addr in
> > pre_load instead).
>
> I asked for this input validation check. If the migration stream is
> inconsistent (e.g. broken or malicious source QEMU), then the subsection
> might be missing but size could be non-zero. The destination QEMU should
> fail cleanly and not run into undefined behavior.
Ah I misread it as the one pairing with the pre_load(). It makes sense
indeed to have such post_load() in the parent VMSD.
Please ignore my comment, sorry for the noise.
--
Peter Xu
© 2016 - 2026 Red Hat, Inc.