On 2026/10/07 17:40, Andrey Drobyshev wrote:
vhost_dev_cleanup() closes the backend FD, but that doesn't release the
device ownership taken with VHOST_SET_OWNER if the same file is open
elsewhere.  That's the case on a CPR target: the source keeps the FD,
so if the target fails after SET_OWNER, either inside vhost_dev_init()
or later in its caller, the ownership stays with the target.  Once the
source is resumed it can't take the device back, SET_OWNER fails with
EBUSY.

Track the ownership with a new 'owner' flag in struct vhost_dev, set and
cleared by vhost_dev_set_owner() and vhost_dev_reset_owner(), and issue
VHOST_RESET_OWNER in vhost_dev_cleanup() if it's set, as the common
teardown for all those paths.  That should be safe in any case: the kernel
refuses it from a non-owner, and an owner is about to close the FD anyway.

Reported-by: Akihiko Odaki <[email protected]>
Suggested-by: Stefano Garzarella <[email protected]>
Signed-off-by: Andrey Drobyshev <[email protected]>
---
  hw/virtio/vhost.c         | 24 ++++++++++++++++++++++--
  include/hw/virtio/vhost.h |  5 +++++
  2 files changed, 27 insertions(+), 2 deletions(-)

diff --git a/hw/virtio/vhost.c b/hw/virtio/vhost.c
index c01ccc800cb..3432b9a43c8 100644
--- a/hw/virtio/vhost.c
+++ b/hw/virtio/vhost.c
@@ -1705,20 +1705,32 @@ int vhost_dev_init_backend(struct vhost_dev *hdev, void 
*opaque,
int vhost_dev_set_owner(struct vhost_dev *hdev)
  {
+    int r;
+
      assert(hdev->vhost_ops);
      if (!hdev->vhost_ops->vhost_set_owner) {
          return -ENOSYS;
      }
-    return hdev->vhost_ops->vhost_set_owner(hdev);
+    r = hdev->vhost_ops->vhost_set_owner(hdev);
+    if (!r) {
+        hdev->owner = true;
+    }
+    return r;
  }
int vhost_dev_reset_owner(struct vhost_dev *hdev)
  {
+    int r;
+
      assert(hdev->vhost_ops);
      if (!hdev->vhost_ops->vhost_reset_owner) {
          return -ENOSYS;
      }
-    return hdev->vhost_ops->vhost_reset_owner(hdev);
+    r = hdev->vhost_ops->vhost_reset_owner(hdev);
+    if (!r) {
+        hdev->owner = false;
+    }
+    return r;
  }
int vhost_dev_init(struct vhost_dev *hdev, void *opaque,
@@ -1864,6 +1876,14 @@ void vhost_dev_cleanup(struct vhost_dev *hdev)
      g_free(hdev->mem);
      g_free(hdev->mem_sections);
      if (hdev->vhost_ops) {
+        /*
+         * Release the device ownership if we hold it.  Needed e.g. for
+         * failing CPR target, so the source can re-acquire it.
+         */
+        if (hdev->owner && vhost_dev_reset_owner(hdev) < 0) {
+            error_report("vhost: cannot release device ownership on "
+                         "cleanup");

Normal teardown produces a misleading ownership error. vhost-user and vDPA set owner=true but do not implement vhost_reset_owner, so the helper returns -ENOSYS. The new cleanup diagnostic therefore reports ordinary cleanup as a failure. I reproduced the message with the following test command even though it passes:

meson test -C build --no-rebuild -v \
  --suite qtest-x86_64 '*qos-test*' \
--test-args '-p /x86_64/pc/i440FX-pcihost/pci-bus-pc/pci-bus/virtio-net-pci/virtio-net/virtio-net-tests/vhost-user/read-guest-mem/memfd'

Regards,
Akihiko Odaki

+        }
          hdev->vhost_ops->vhost_cleanup(hdev);
      }
      assert(!hdev->log);
diff --git a/include/hw/virtio/vhost.h b/include/hw/virtio/vhost.h
index 73ea130d7ee..f6e32b59ab2 100644
--- a/include/hw/virtio/vhost.h
+++ b/include/hw/virtio/vhost.h
@@ -111,6 +111,11 @@ struct vhost_dev {
      bool started;
      /* @initialized: has the full vhost_dev_init() completed? */
      bool initialized;
+    /*
+     * @owner: has this vhost_dev acquired the device ownership?
+     * Updated only by vhost_dev_set_owner()/vhost_dev_reset_owner()
+     */
+    bool owner;
      bool log_enabled;
      uint64_t log_size;
      Error *migration_blocker;



Reply via email to