All of lore.kernel.org
 help / color / mirror / Atom feed
From: Stefano Garzarella <sgarzare@redhat.com>
To: Andrey Drobyshev <andrey.drobyshev@virtuozzo.com>
Cc: qemu-devel@nongnu.org, mst@redhat.com, farosas@suse.de,
	 peterx@redhat.com, dongli.zhang@oracle.com,
	maciej.szmigiero@oracle.com,  bchaney@akamai.com,
	mark.kanda@oracle.com, den@openvz.org
Subject: Re: [PATCH v4 10/10] vhost-vsock: hand off device ownership across CPR
Date: Mon, 7 Sep 2026 17:13:45 +0200	[thread overview]
Message-ID: <ap7Lma3zpbr6QDb0@sgarzare-redhat> (raw)
In-Reply-To: <20260820113955.509478-11-andrey.drobyshev@virtuozzo.com>

On Thu, Aug 20, 2026 at 02:39:55PM +0300, Andrey Drobyshev wrote:
>The previous patches reuse the source vhost FD on the destination,
>however both source and target still call vhost_dev_init() (VHOST_SET_OWNER)
>in realize().  For cpr-transfer the destination realizes while the source
>still owns the shared FD, so its SET_OWNER would fail - ownership has to be
>handed over explicitly.
>
>Do this through the device's CPR vmstate hooks.  Namely, release device
>ownership in pre_save, reclaim in post_load, re-acquire on failure:
>
>  - .pre_save() releases ownership on the source (VHOST_RESET_OWNER) once
>    the VM is stopped, for the FD-preserving CPR modes (cpr-transfer and
>    cpr-exec).
>
>  - .realize(), for an incoming CPR, only sets up the virtio device and
>    queries the backend features by calling vhost_dev_init_backend().
>    It doesn't take device ownership and doesn't touch the VQs which
>    still-running source might use.  The full init is deferred to
>    .post_load().
>
>  - .post_load() reclaims it on the destination: the full vhost_dev_init()
>    (VHOST_SET_OWNER) on the preserved FD, plus sets the guest cid, before
>    the device is started at vm_start.
>
>  - A MIG_EVENT_FAILED notifier re-acquires ownership if the migration
>    fails after pre_save released it and the source VM is resumed.
>
>  - .set_status() refuses to start a device whose handoff hasn't
>    completed: not fully initialized yet, or left ownerless after a
>    failed CPR.
>
>With the handoff in place, lift the CPR blocker: only ID-less devices,
>which can't preserve their FDs, remain blocked.
>
>Suggested-by: Dongli Zhang <dongli.zhang@oracle.com>
>Signed-off-by: Andrey Drobyshev <andrey.drobyshev@virtuozzo.com>
>---
> hw/virtio/vhost-vsock.c         | 165 ++++++++++++++++++++++++++++----
> include/hw/virtio/vhost-vsock.h |   5 +-
> 2 files changed, 152 insertions(+), 18 deletions(-)
>
>diff --git a/hw/virtio/vhost-vsock.c b/hw/virtio/vhost-vsock.c
>index cec2c31f0ca..e25e5350905 100644
>--- a/hw/virtio/vhost-vsock.c
>+++ b/hw/virtio/vhost-vsock.c
>@@ -73,9 +73,23 @@ static int vhost_vsock_set_running(VirtIODevice *vdev, int start)
> static int vhost_vsock_set_status(VirtIODevice *vdev, uint8_t status)
> {
>     VHostVSockCommon *vvc = VHOST_VSOCK_COMMON(vdev);
>+    VHostVSock *vsock = VHOST_VSOCK(vdev);
>     bool should_start = virtio_device_should_start(vdev, status);
>+    bool initialized = vhost_dev_is_initialized(&vvc->vhost_dev);
>     int ret;
>
>+    /*
>+     * During CPR, on target the full vhost_dev_init() is deferred to
>+     * post_load.  On source the device is left without an owner if a
>+     * failed CPR couldn't re-acquire it.  Refuse to start in both cases
>+     * rather than issue vhost ioctls on a half-initialised or ownerless
>+     * device.
>+     */
>+    if (should_start && (!initialized || vsock->owner_reset)) {
>+        error_report("vhost-vsock: refusing to start, device init incomplete");
>+        return 0;

Why returning 0?

>+    }
>+
>     if (vhost_dev_is_started(&vvc->vhost_dev) == should_start) {
>         return 0;
>     }
>@@ -111,8 +125,67 @@ static uint64_t vhost_vsock_get_features(VirtIODevice *vdev,
>     return vhost_vsock_common_get_features(vdev, requested_features, errp);
> }
>
>+/*
>+ * Re-acquire device ownership if a CPR migration that released it (in
>+ * vhost_vsock_pre_save()) failed and the source VM is about to resume.
>+ * This runs before vm_start(), so the device is owned again before it is
>+ * restarted.
>+ */
>+static int vhost_vsock_cpr_notifier(NotifierWithReturn *notifier,
>+                                    MigrationEvent *e, Error **errp)
>+{
>+    VHostVSock *vsock = container_of(notifier, VHostVSock, cpr_notifier);
>+    VHostVSockCommon *vvc = VHOST_VSOCK_COMMON(vsock);
>+    int ret;
>+
>+    if (e->type == MIG_EVENT_FAILED && vsock->owner_reset) {

IIUC MIG_EVENT_FAILED only covers the source failing. If the source 
completes it successfully and something goes wrong afterwards (e.g. in 
the destination), what will happen?

Thinking if we can try to call vhost_dev_set_owner() in 
vhost_vsock_set_status(), expecting also -EBUSY, instead of relying on 
`vsock->owner_reset`.

>+        ret = vhost_dev_set_owner(&vvc->vhost_dev);
>+        if (ret < 0) {
>+            error_report("vhost-vsock: failed to re-acquire owner: %d", ret);

Is it okay to return 0 also in this case?

>+        } else {
>+            vsock->owner_reset = false;
>+        }
>+    }
>+
>+    return 0;
>+}
>+
>+static int vhost_vsock_pre_save(void *opaque)
>+{
>+    VHostVSockCommon *vvc = VHOST_VSOCK_COMMON(opaque);
>+    VHostVSock *vsock = VHOST_VSOCK(opaque);
>+    int ret;
>+
>+    ret = vhost_vsock_common_pre_save(opaque);
>+    if (ret) {
>+        return ret;
>+    }
>+
>+    /*
>+     * Release the device ownership now for CPR migration.  The device is
>+     * already stopped at pre_save, and destination reclaims it by calling
>+     * VHOST_SET_OWNER in post_load.
>+     */
>+    if (cpr_incoming_needed(NULL)) {

Am I missing something that keeps pre_save from running outside an 
actual CPR migration (savevm, or any save path taken while mode is still 
set from an earlier attempt), or should the condition be tied to a CPR 
migration actually being in flight rather than to the mode parameter 
alone?

>+        ret = vhost_dev_reset_owner(&vvc->vhost_dev);
>+        if (ret < 0) {
>+            error_report("vhost-vsock: vhost_reset_owner failed: %d", ret);

vhost-vsock devices that don't support `reset_owner` will fail here, 
right? Is that what we expect?

>+            return ret;
>+        }
>+        vsock->owner_reset = true;
>+    }
>+
>+    return 0;
>+}
>+
> static int vhost_vsock_post_load(void *opaque, int version_id)
> {
>+    VHostVSockCommon *vvc = VHOST_VSOCK_COMMON(opaque);
>+    VirtIODevice *vdev = VIRTIO_DEVICE(opaque);
>+    DeviceState *proxy = qdev_get_parent_bus(DEVICE(vdev))->parent;
>+    Error *local_err = NULL;
>+    int vhostfd, ret;
>+
>     /*
>      * Only reset vsock connections for non-CPR migration.  For CPR the
>      * guest cid is unchanged, and the cid-change reset would otherwise
>@@ -122,6 +195,32 @@ static int vhost_vsock_post_load(void *opaque, int version_id)
>         return vhost_vsock_common_post_load(opaque, version_id);
>     }
>
>+    /*
>+     * CPR restore case.  The source released device ownership in its
>+     * pre_save.  Complete the handoff here, before the device is started
>+     * at vm_start.  Init vhost device on preserved FD, issue
>+     * VHOST_SET_OWNER on it, and restore the guest cid.
>+     */
>+    vhostfd = cpr_find_fd(proxy->id, 0);
>+    if (vhostfd < 0) {
>+        error_report("vhost-vsock: could not find restored vhost FD");
>+        return -1;
>+    }
>+
>+    ret = vhost_dev_init(&vvc->vhost_dev, (void *)(uintptr_t)vhostfd,
>+                         VHOST_BACKEND_TYPE_KERNEL, 0, &local_err);
>+    if (ret < 0) {
>+        error_report_err(local_err);
>+        return ret;
>+    }
>+
>+    ret = vhost_vsock_set_guest_cid(vdev);
>+    if (ret < 0) {
>+        error_report("vhost-vsock: unable to set guest cid: %d", ret);
>+        vhost_dev_cleanup(&vvc->vhost_dev);
>+        return ret;
>+    }
>+
>     return 0;
> }
>
>@@ -133,7 +232,7 @@ static const VMStateDescription vmstate_virtio_vhost_vsock = {
>         VMSTATE_VIRTIO_DEVICE,
>         VMSTATE_END_OF_LIST()
>     },
>-    .pre_save = vhost_vsock_common_pre_save,
>+    .pre_save = vhost_vsock_pre_save,
>     .post_load = vhost_vsock_post_load,
> };
>
>@@ -144,6 +243,7 @@ static void vhost_vsock_device_realize(DeviceState *dev, Error **errp)
>     VirtIODevice *vdev = VIRTIO_DEVICE(dev);
>     VHostVSock *vsock = VHOST_VSOCK(dev);
>     DeviceState *proxy = qdev_get_parent_bus(DEVICE(vsock))->parent;
>+    bool cpr_incoming = cpr_is_incoming();
>     int vhostfd;
>     int ret;
>
>@@ -159,19 +259,30 @@ static void vhost_vsock_device_realize(DeviceState *dev, Error **errp)
>     }
>
>     /*
>-     * CPR migration of a vhost-vsock device is not supported yet: the
>-     * device ownership is not handed over, so the target fails to set
>-     * up its device.  Fail the migration early and gracefully instead.
>+     * With the ownership handoff in place CPR migration is now supported.
>+     * Having a unique ID is mandatory for FD preservation during it, thus
>+     * we only keep the migration blocker for the ID-less case.
>      */
>-    error_setg(&vsock->migration_blocker,
>-               "vhost-vsock: CPR migration is not supported");
>-    if (migrate_add_blocker_modes(&vsock->migration_blocker,
>-                                  BIT(MIG_MODE_CPR_TRANSFER) |
>-                                  BIT(MIG_MODE_CPR_EXEC), errp) < 0) {
>-        return;
>+    if (!proxy->id) {
>+        error_setg(&vsock->migration_blocker,
>+                   "vhost-vsock: device ID is required for CPR migration");
>+        if (migrate_add_blocker_modes(&vsock->migration_blocker,
>+                                      BIT(MIG_MODE_CPR_TRANSFER) |
>+                                      BIT(MIG_MODE_CPR_EXEC), errp) < 0) {
>+            return;
>+        }
>     }
>
>-    if (cpr_is_incoming()) {
>+    /*
>+     * Re-acquire ownership if a CPR migration releases it (in pre_save) but
>+     * then fails.
>+     */
>+    migration_add_notifier_modes(&vsock->cpr_notifier,
>+                                 vhost_vsock_cpr_notifier,
>+                                 BIT(MIG_MODE_CPR_TRANSFER) |
>+                                 BIT(MIG_MODE_CPR_EXEC));
>+
>+    if (cpr_incoming) {
>         /* Reuse the fd handed over from the source QEMU. */
>         if (!proxy->id) {
>             error_setg(errp, "vhost-vsock: device ID is required for "
>@@ -204,14 +315,32 @@ static void vhost_vsock_device_realize(DeviceState *dev, Error **errp)
>
>     vhost_vsock_common_realize(vdev);
>
>-    ret = vhost_dev_init(&vvc->vhost_dev, (void *)(uintptr_t)vhostfd,
>-                         VHOST_BACKEND_TYPE_KERNEL, 0, errp);
>-    if (ret < 0) {
>+    if (!cpr_incoming) {
>+        ret = vhost_dev_init(&vvc->vhost_dev, (void *)(uintptr_t)vhostfd,
>+                             VHOST_BACKEND_TYPE_KERNEL, 0, errp);
>+        if (ret < 0) {
>+            /*
>+             * vhostfd is closed by vhost_dev_cleanup, which is called
>+             * by vhost_dev_init on initialization error.
>+             */
>+            goto err_virtio;
>+        }
>+    } else {
>         /*
>-         * vhostfd is closed by vhost_dev_cleanup, which is called
>-         * by vhost_dev_init on initialization error.
>+         * CPR restore case: only learn the backend feature set now, but
>+         * defer taking ownership or touching VQs (the still-running source
>+         * might be using them).  The full vhost_dev_init()/VHOST_SET_OWNER
>+         * is done later in post_load.
>          */
>-        goto err_virtio;
>+        ret = vhost_dev_init_backend(&vvc->vhost_dev,
>+                                     (void *)(uintptr_t)vhostfd,
>+                                     VHOST_BACKEND_TYPE_KERNEL, errp);
>+        if (ret < 0) {
>+            /* vhost_dev_init_backend() does not close the fd on error */

Okay, but IIUC this is returned by cpr_find_fd(), is that funtion 
passing the ownership of the fd, even that the fd is not removed from 
the hash table?

Should we call cpr_delete_fd() when we take it?

Thanks,
Stefano

>+            goto err_vhost_dev;
>+        }
>+
>+        return;
>     }
>
>     ret = vhost_vsock_set_guest_cid(vdev);
>@@ -233,6 +362,7 @@ err_vhost_dev:
> err_virtio:
>     vhost_vsock_common_unrealize(vdev);
> err_blocker:
>+    migration_remove_notifier(&vsock->cpr_notifier);
>     migrate_del_blocker(&vsock->migration_blocker);
> }
>
>@@ -249,6 +379,7 @@ static void vhost_vsock_device_unrealize(DeviceState *dev)
>     if (proxy->id) {
>         cpr_delete_fd(proxy->id, 0);
>     }
>+    migration_remove_notifier(&vsock->cpr_notifier);
>     migrate_del_blocker(&vsock->migration_blocker);
>     vhost_dev_cleanup(&vvc->vhost_dev);
>     vhost_vsock_common_unrealize(vdev);
>diff --git a/include/hw/virtio/vhost-vsock.h b/include/hw/virtio/vhost-vsock.h
>index a964d57e1bc..6d0cff4fb93 100644
>--- a/include/hw/virtio/vhost-vsock.h
>+++ b/include/hw/virtio/vhost-vsock.h
>@@ -15,6 +15,7 @@
> #define QEMU_VHOST_VSOCK_H
>
> #include "hw/virtio/vhost-vsock-common.h"
>+#include "qemu/notify.h"
> #include "qom/object.h"
>
> #define TYPE_VHOST_VSOCK "vhost-vsock-device"
>@@ -29,7 +30,9 @@ struct VHostVSock {
>     /*< private >*/
>     VHostVSockCommon parent;
>     VHostVSockConf conf;
>-    Error *migration_blocker;   /* CPR migration is not supported */
>+    Error *migration_blocker;   /* set when the device has no ID */
>+    bool owner_reset;           /* CPR released ownership; needs re-acquire */
>+    NotifierWithReturn cpr_notifier;   /* re-acquires ownership if CPR fails */
>
>     /*< public >*/
> };
>-- 
>2.47.1
>



  reply	other threads:[~2026-09-07 15:14 UTC|newest]

Thread overview: 36+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-08-20 11:39 [PATCH v4 00/10] migration/cpr: support vhost-vsock devices Andrey Drobyshev
2026-08-20 11:39 ` [PATCH v4 01/10] vhost-vsock: block CPR migration modes Andrey Drobyshev
2026-09-07 14:54   ` Stefano Garzarella
2026-08-20 11:39 ` [PATCH v4 02/10] vhost: add vhost_reset_owner op Andrey Drobyshev
2026-09-07 13:58   ` Stefano Garzarella
2026-09-09 16:59     ` Andrey Drobyshev
2026-08-20 11:39 ` [PATCH v4 03/10] vhost-vsock: don't reset connections during CPR Andrey Drobyshev
2026-09-07 14:54   ` Stefano Garzarella
2026-08-20 11:39 ` [PATCH v4 04/10] vhost-vsock: fix FD leak in realize() Andrey Drobyshev
2026-09-07 14:54   ` Stefano Garzarella
2026-09-09 16:59     ` Andrey Drobyshev
2026-08-20 11:39 ` [PATCH v4 05/10] vhost-vsock: preserve vhost FD during CPR Andrey Drobyshev
2026-09-07 14:55   ` Stefano Garzarella
2026-09-09 16:59     ` Andrey Drobyshev
2026-09-11  7:37       ` Stefano Garzarella
2026-09-11 16:35         ` Andrey Drobyshev
2026-09-14  9:03           ` Stefano Garzarella
2026-09-14  9:11             ` Andrey Drobyshev
2026-08-20 11:39 ` [PATCH v4 06/10] vhost: factor out vhost_dev_init_backend() Andrey Drobyshev
2026-09-07 14:55   ` Stefano Garzarella
2026-08-20 11:39 ` [PATCH v4 07/10] vhost: make vhost_dev_cleanup() safe on a partially initialized device Andrey Drobyshev
2026-09-07 14:56   ` Stefano Garzarella
2026-09-09 17:06     ` Andrey Drobyshev
2026-08-20 11:39 ` [PATCH v4 08/10] vhost: add vhost_dev_set_owner() / vhost_dev_reset_owner() helpers Andrey Drobyshev
2026-09-07 14:56   ` Stefano Garzarella
2026-09-09 17:05     ` Andrey Drobyshev
2026-08-20 11:39 ` [PATCH v4 09/10] vhost: add vhost_dev_is_initialized() helper Andrey Drobyshev
2026-09-07 14:57   ` Stefano Garzarella
2026-08-20 11:39 ` [PATCH v4 10/10] vhost-vsock: hand off device ownership across CPR Andrey Drobyshev
2026-09-07 15:13   ` Stefano Garzarella [this message]
2026-09-09 17:45     ` Andrey Drobyshev
2026-09-11  7:45       ` Stefano Garzarella
2026-09-11 16:35         ` Andrey Drobyshev
2026-09-14  9:07           ` Stefano Garzarella
2026-08-31 11:16 ` [PATCH v4 00/10] migration/cpr: support vhost-vsock devices Andrey Drobyshev
2026-09-07 10:07   ` Stefano Garzarella

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=ap7Lma3zpbr6QDb0@sgarzare-redhat \
    --to=sgarzare@redhat.com \
    --cc=andrey.drobyshev@virtuozzo.com \
    --cc=bchaney@akamai.com \
    --cc=den@openvz.org \
    --cc=dongli.zhang@oracle.com \
    --cc=farosas@suse.de \
    --cc=maciej.szmigiero@oracle.com \
    --cc=mark.kanda@oracle.com \
    --cc=mst@redhat.com \
    --cc=peterx@redhat.com \
    --cc=qemu-devel@nongnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.