* [PULL 00/78] pci, vhost, virtio, iommu, acpi: features, fixes
@ 2026-10-28 17:13 Michael S. Tsirkin
2026-10-28 17:13 ` [PULL 01/78] libvhost-user: protect against OOB memory table access Michael S. Tsirkin
` (78 more replies)
0 siblings, 79 replies; 84+ messages in thread
From: Michael S. Tsirkin @ 2026-10-28 17:13 UTC (permalink / raw)
To: qemu-devel; +Cc: Peter Maydell
The following changes since commit 29b1dc631d6ac6bfc39464e0be8bf4cad556a591:
Merge tag 'block-pull-request' of https://gitlab.com/stefanha/qemu into staging (2026-10-27 20:02:57 -0500)
are available in the Git repository at:
https://git.kernel.org/pub/scm/virt/kvm/mst/qemu.git tags/for_upstream
for you to fetch changes up to c155e7e9e025b60ce17e26a7c382d86f3f9c9fee:
virtio-net: add mtu-from-tap property for automatic MTU detection (2026-10-28 13:11:16 -0400)
----------------------------------------------------------------
pci, vhost, virtio, iommu, acpi: features, fixes
multiboot can again load 64 bit images
vdpa-dev now implements guest_notifier_mask/pending
pci bridge can now configure the negotiated bandwidth
tap can now auto-detect host mtu and expose to guest
amd iommu can now attach on non system bus
vhost-user spec now has its own mailing list
Fixes, cleanups all over the place.
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
----------------------------------------------------------------
Alexander Mikhalitsyn (1):
virtio-snd: don't use virtio_snd_pcm_get_stream() in virtio_snd_post_load()
Alexandr Moshkov (2):
vhost-user: add missing stub for vhost_user_has_protocol_feature()
vhost: move skip-drain feature check to vhost-user
Andrey Drobyshev (22):
migration/cpr: forbid saving duplicate CprFds into hash table
tests/unit: add test for duplicate keys in CPR FD table
migration: forbid snapshots in CPR migration modes
migration: forbid snapshots under Xen
tests/qtest/migration: add test for snapshot rejection in CPR modes
vhost-vsock: block CPR migration modes
vhost: add vhost_reset_owner op
vhost-vsock: don't reset connections during CPR
vhost-vsock: fix FD leak in realize()
vhost-vsock: preserve vhost FD during CPR
vhost: factor out vhost_dev_init_backend()
vhost: make vhost_dev_cleanup() safe on a partially initialized device
vhost: add vhost_dev_set_owner() / vhost_dev_reset_owner() helpers
vhost: add vhost_dev_is_initialized() helper
vhost: release ownership in vhost_dev_cleanup()
migration: add MIG_EVENT_INCOMING_FAILED
vhost: release ownership when the incoming migration fails
vhost: release ownership on exit
vhost-vsock: hand off device ownership across CPR
tests/qtest/migration: add vhost-vsock tests for cpr-transfer and cpr-exec
tests/functional: add vhost-vsock CPR test with a live guest
tests/functional: cover a cpr-transfer target going away with vhost-vsock
Connor Kite (1):
hw/virtio/vhost-shadow-virtqueue: fix range boundary in SVQ translation
Daniel P. Berrangé (2):
hw/arm: remove pci-bridge/pci_expander_bridge.h from virt.c
hw/pci-bridge: split CXL PXB into separate file
Daniel Paziyski (1):
pcie_sriov: register user created virtual function before realizing it
David Vrabel (4):
hw/pci-bridge: Add generic PCIe switch upstream and downstream ports
hw/pci-bridge: Make speed and width for PCIe ports non-experimental
hw/pci-bridge: Default the generic PCIe root port as Gen6 capable
hw/pci: Ensure PCIe link speed/width is consistent between both ends
Gavin Shan (1):
hw/virtio/virtio-mem: Fix error message on invalid NUMA node
Junjie Cao (7):
hw/cxl: destroy primary CCI before re-initialization on reset
hw/cxl: convert cxl-type3 to three-phase reset
hw/cxl: add cxl_destroy_mailbox_t3() as the mailbox teardown counterpart
hw/cxl: discard in-flight sanitize state on mailbox teardown
hw/cxl: clear event logs, scan media and interrupt policy on reset
hw/cxl: clear poison lists and feature transfer state on reset
MAINTAINERS: Update Junjie Cao's email address
Li RongQing (4):
vhost: fix off-by-one in vhost_svq_next_desc with IN_ORDER
pcie_sriov: fix wrong write width and off-by-one in pf_reset
virtio-qmp: Fix out-of-bounds vqs index in vhost queue status
pcie_aer: fix pci_set_long overflow into PCI_EXP_LNKCAP
Li Zhaoxin (1):
vdpa-dev: implement guest_notifier_mask/pending
Michael S. Tsirkin (2):
libvhost-user: protect against OOB memory table access
vhost-user: fix SET_MEM_TABLE number of regions
Nariman Sayed (3):
net/tap: add tap_fd_get_mtu() to query host tap MTU
net: add generic get_mtu NetClientInfo callback
virtio-net: add mtu-from-tap property for automatic MTU detection
Sairaj Kodilkar (8):
amd_iommu: Assign ioapic_as only when interrupt remapping is supported.
x86-iommu: Introduce x86_iommu_device_get() for x86-iommu retrieval
amd_iommu: Add support for get_x86_iommu callback
intel_iommu: Add support for get_x86_iommu callback
i386/kvm: Use x86_iommu_device_get() for per-device IOMMU lookup
amd_iommu: Introduce root bus
acpi-build: Create PCI topology using amd iommu root bus
amd_iommu: Allow IOMMU to attachment on non-system (pcie.0) bus
Samuel Thibault (1):
Revert 9696846600ca ("multiboot: Prevent loading of x86_64 images")
Stefano Garzarella (1):
vhost-user: add dedicated mailing list
Vladimir Sementsov-Ogievskiy (8):
tests/functional: add test_tap_migration
net/tap: tap_post_load(): poll only on success
hw/net: finish backend initialization earler for local migration
net/tap: move vmstate initialization to new_tap()
net/tap: net_init_tap_one(): fix failure path
qapi/virtio: Restore backend features in x-query-virtio-status
qapi/monitor: Handle missing vhost-user protocol features
util/event_notifier: more strict API
Yodel Eldar (2):
contrib/vhost-user-bridge: Use stderr for debug output
tests/functional/x86_64/test_vhost_user_bridge: Eliminate stdbuf usage
Yuho Choi (1):
vhost-user: remove shared object entries during cleanup
Zhenzhong Duan (6):
intel_iommu_accel: Guard VTDAccelPASIDCacheEntry definition
backends/iommufd: Introduce iommufd_backend_alloc_faultq
backends/iommufd: Extend iommufd_backend_alloc_hwpt() with fault_id
intel_iommu_accel: Add PRQ injection for passthrough device
intel_iommu_accel: Accept PRQ response for passthrough device
intel_iommu_accel: Teardown FAULTQ resources in bottom half
MAINTAINERS | 14 +-
docs/interop/vhost-user.rst | 8 +-
docs/pcie.txt | 6 +-
docs/specs/pci-ids.rst | 4 +
qapi/virtio.json | 5 +-
hw/i386/amd_iommu.h | 3 +
hw/i386/intel_iommu_accel.h | 16 +-
hw/i386/intel_iommu_internal.h | 3 +
hw/pci-bridge/pci_expander_bridge_common.h | 28 ++
include/hw/cxl/cxl_device.h | 3 +
include/hw/i386/intel_iommu.h | 6 +
include/hw/i386/x86-iommu.h | 6 +
...expander_bridge.h => pci_expander_bridge_cxl.h} | 0
include/hw/pci/pci.h | 14 +
include/hw/vfio/vfio-cpr.h | 7 +-
include/hw/virtio/vhost-backend.h | 2 +
include/hw/virtio/vhost.h | 80 ++++
include/hw/virtio/virtio-dmabuf.h | 8 +
include/hw/virtio/virtio-net.h | 1 +
include/migration/cpr.h | 2 +-
include/migration/misc.h | 5 +
include/net/net.h | 3 +
include/qemu/event_notifier.h | 1 +
include/system/iommufd.h | 7 +-
net/tap_int.h | 1 +
backends/hostmem-memfd.c | 5 +-
backends/hostmem-shm.c | 5 +-
backends/iommufd.c | 32 +-
contrib/vhost-user-bridge/vhost-user-bridge.c | 2 +-
hw/arm/smmuv3-accel.c | 6 +-
hw/arm/virt.c | 1 -
hw/audio/virtio-snd.c | 3 +-
hw/core/machine.c | 1 +
hw/cxl/cxl-host.c | 2 +-
hw/cxl/cxl-mailbox-utils.c | 22 +-
hw/display/virtio-dmabuf.c | 18 +
hw/i386/acpi-build.c | 152 ++++----
hw/i386/amd_iommu.c | 75 +++-
hw/i386/intel_iommu.c | 26 +-
hw/i386/intel_iommu_accel.c | 309 ++++++++++++++-
hw/i386/multiboot.c | 5 -
hw/i386/x86-iommu-stub.c | 5 +
hw/i386/x86-iommu.c | 16 +
hw/intc/ioapic.c | 3 +-
hw/mem/cxl_type3.c | 60 ++-
hw/net/virtio-net.c | 50 +--
hw/pci-bridge/gen_pcie_downstream_port.c | 202 ++++++++++
hw/pci-bridge/gen_pcie_root_port.c | 6 +-
hw/pci-bridge/gen_pcie_upstream_port.c | 177 +++++++++
hw/pci-bridge/pci_expander_bridge.c | 194 +---------
hw/pci-bridge/pci_expander_bridge_cxl.c | 201 ++++++++++
hw/pci-bridge/pci_expander_bridge_stubs.c | 14 -
hw/pci/pci.c | 42 +-
hw/pci/pcie.c | 26 +-
hw/pci/pcie_aer.c | 2 +-
hw/pci/pcie_sriov.c | 16 +-
hw/remote/iohub.c | 11 +-
hw/vfio/container-legacy.c | 7 +-
hw/vfio/cpr-legacy.c | 31 +-
hw/vfio/cpr.c | 6 +-
hw/vfio/iommufd.c | 2 +-
hw/vfio/pci.c | 5 +-
hw/virtio/vdpa-dev.c | 30 +-
hw/virtio/vhost-kernel.c | 6 +
hw/virtio/vhost-shadow-virtqueue.c | 44 ++-
hw/virtio/vhost-user-stub.c | 5 +
hw/virtio/vhost-user.c | 5 +-
hw/virtio/vhost-vsock.c | 193 +++++++++-
hw/virtio/vhost.c | 141 ++++++-
hw/virtio/virtio-hmp-cmds.c | 6 +-
hw/virtio/virtio-mem.c | 2 +-
hw/virtio/virtio-qmp.c | 21 +-
migration/cpr.c | 64 ++--
migration/migration.c | 10 +
migration/options.c | 13 +
net/net.c | 9 +
net/tap-bsd.c | 5 +
net/tap-linux.c | 30 ++
net/tap-solaris.c | 5 +
net/tap-stub.c | 5 +
net/tap.c | 19 +-
subprojects/libvhost-user/libvhost-user.c | 7 +
system/physmem.c | 5 +-
target/i386/kvm/kvm.c | 15 +-
tests/qtest/migration/cpr-tests.c | 133 ++++++-
tests/unit/test-cpr.c | 98 +++++
tests/unit/test-virtio-dmabuf.c | 43 +++
util/event_notifier-posix.c | 10 +
backends/trace-events | 3 +-
hw/i386/trace-events | 2 +
hw/pci-bridge/meson.build | 5 +-
tests/functional/x86_64/meson.build | 4 +
tests/functional/x86_64/test_pcie_switch.py | 98 +++++
tests/functional/x86_64/test_tap_migration.py | 425 +++++++++++++++++++++
tests/functional/x86_64/test_vhost_user_bridge.py | 6 -
tests/functional/x86_64/test_vhost_vsock_cpr.py | 347 +++++++++++++++++
tests/unit/meson.build | 1 +
97 files changed, 3256 insertions(+), 532 deletions(-)
create mode 100644 hw/pci-bridge/pci_expander_bridge_common.h
rename include/hw/pci-bridge/{pci_expander_bridge.h => pci_expander_bridge_cxl.h} (100%)
create mode 100644 hw/pci-bridge/gen_pcie_downstream_port.c
create mode 100644 hw/pci-bridge/gen_pcie_upstream_port.c
create mode 100644 hw/pci-bridge/pci_expander_bridge_cxl.c
delete mode 100644 hw/pci-bridge/pci_expander_bridge_stubs.c
create mode 100644 tests/unit/test-cpr.c
create mode 100755 tests/functional/x86_64/test_pcie_switch.py
create mode 100755 tests/functional/x86_64/test_tap_migration.py
create mode 100755 tests/functional/x86_64/test_vhost_vsock_cpr.py
^ permalink raw reply [flat|nested] 84+ messages in thread
* [PULL 01/78] libvhost-user: protect against OOB memory table access
2026-10-28 17:13 [PULL 00/78] pci, vhost, virtio, iommu, acpi: features, fixes Michael S. Tsirkin
@ 2026-10-28 17:13 ` Michael S. Tsirkin
2026-10-28 17:13 ` [PULL 02/78] vhost-user: fix SET_MEM_TABLE number of regions Michael S. Tsirkin
` (77 subsequent siblings)
78 siblings, 0 replies; 84+ messages in thread
From: Michael S. Tsirkin @ 2026-10-28 17:13 UTC (permalink / raw)
To: qemu-devel; +Cc: Peter Maydell, Stefano Garzarella, Omer Can Vural, vhost-user
SET_MEM_TABLE handler uses nregions from the message as a loop
bound without checking that it is below VHOST_MEMORY_BASELINE_NREGIONS,
so a malformed message causes an out-of-bounds access to the
regions[] and fds[] arrays.
Frontend is trusted so not a security problem, but
an OOB access is not a nice way to handle errors.
Check, and panic.
Fixes: 7b2e5c65f42f ("contrib: add libvhost-user")
Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/4403
Cc: Stefano Garzarella <sgarzare@redhat.com>
Reported-by: Omer Can Vural <can.omer.5306@outlook.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Message-ID: <8adc88f2f535ab0566a5df1c66db728112dd6f74.1789469161.git.mst@redhat.com>
---
subprojects/libvhost-user/libvhost-user.c | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/subprojects/libvhost-user/libvhost-user.c b/subprojects/libvhost-user/libvhost-user.c
index 2fed792e85..52817cc733 100644
--- a/subprojects/libvhost-user/libvhost-user.c
+++ b/subprojects/libvhost-user/libvhost-user.c
@@ -1105,6 +1105,13 @@ vu_set_mem_table_exec(VuDev *dev, VhostUserMsg *vmsg)
vu_remove_all_mem_regs(dev);
DPRINT("Nregions: %u\n", memory->nregions);
+
+ if (memory->nregions > VHOST_MEMORY_BASELINE_NREGIONS) {
+ vu_panic(dev, "Invalid set_mem_table nregions: %u > %u",
+ memory->nregions, VHOST_MEMORY_BASELINE_NREGIONS);
+ return false;
+ }
+
for (i = 0; i < memory->nregions; i++) {
_vu_add_mem_reg(dev, &memory->regions[i], vmsg->fds[i]);
close(vmsg->fds[i]);
--
MST
^ permalink raw reply related [flat|nested] 84+ messages in thread
* [PULL 02/78] vhost-user: fix SET_MEM_TABLE number of regions
2026-10-28 17:13 [PULL 00/78] pci, vhost, virtio, iommu, acpi: features, fixes Michael S. Tsirkin
2026-10-28 17:13 ` [PULL 01/78] libvhost-user: protect against OOB memory table access Michael S. Tsirkin
@ 2026-10-28 17:13 ` Michael S. Tsirkin
2026-10-28 17:14 ` [PULL 03/78] hw/virtio/vhost-shadow-virtqueue: fix range boundary in SVQ translation Michael S. Tsirkin
` (76 subsequent siblings)
78 siblings, 0 replies; 84+ messages in thread
From: Michael S. Tsirkin @ 2026-10-28 17:13 UTC (permalink / raw)
To: qemu-devel
Cc: Peter Maydell, Stefano Garzarella, luzhixing12345,
Pierrick Bouvier, vhost-user
vhost-user.rst says the legacy SET_MEM_TABLE contains 8 regions, but
this does not make sense - we have a num_regions field for this.
And it is not true in practice: if QEMU has less, it sends less.
The incorrect wording was introduced, without explanation, as
part of an editorial cleanup. Probably an oversight.
It's really "up to 8".
Fixes: b87ea798eb83 ("docs: fix vhost-user protocol doc")
Reviewed-by: Stefano Garzarella <sgarzare@redhat.com>
Cc: luzhixing12345 <luzhixing12345@gmail.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Message-ID: <712e301bf7f815412fb264de8159cb3475bd237a.1789469028.git.mst@redhat.com>
---
docs/interop/vhost-user.rst | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/docs/interop/vhost-user.rst b/docs/interop/vhost-user.rst
index c1e02ff7e9..488214a306 100644
--- a/docs/interop/vhost-user.rst
+++ b/docs/interop/vhost-user.rst
@@ -248,7 +248,7 @@ Multiple Memory regions description
:padding: 32-bit
-:regions: regions field contains 8 regions of type :ref:`Memory region description <memory_region_description>`.
+:regions: regions field contains up to 8 regions of type :ref:`Memory region description <memory_region_description>`.
Log description
^^^^^^^^^^^^^^^
--
MST
^ permalink raw reply related [flat|nested] 84+ messages in thread
* [PULL 03/78] hw/virtio/vhost-shadow-virtqueue: fix range boundary in SVQ translation
2026-10-28 17:13 [PULL 00/78] pci, vhost, virtio, iommu, acpi: features, fixes Michael S. Tsirkin
2026-10-28 17:13 ` [PULL 01/78] libvhost-user: protect against OOB memory table access Michael S. Tsirkin
2026-10-28 17:13 ` [PULL 02/78] vhost-user: fix SET_MEM_TABLE number of regions Michael S. Tsirkin
@ 2026-10-28 17:14 ` Michael S. Tsirkin
2026-10-28 17:14 ` [PULL 04/78] tests/functional: add test_tap_migration Michael S. Tsirkin
` (75 subsequent siblings)
78 siblings, 0 replies; 84+ messages in thread
From: Michael S. Tsirkin @ 2026-10-28 17:14 UTC (permalink / raw)
To: qemu-devel
Cc: Peter Maydell, Connor Kite, Stefan Hajnoczi, Stefano Garzarella,
Eugenio Pérez
From: Connor Kite <connorkite@gmail.com>
iova-tree expects inclusive range sizing when maps are allocated or searched.
Currently, svqs use exclusive sizing when searching their
vhost-iova-tree for a match to the region to be translated. This could
lead to errors if the region to be translated is at the edge of an iova
region.
Fix this by reducing `needle.size` by 1 in
vhost_svq_translate_addr to bring it in line with DMAMap and iova-tree
convention.
The only current user of svq->iova_tree is hw/virtio/vhost-vdpa.c, which
also treats size as inclusive when using the tree API. In progress work
on vhost-user isolation mode in hw/virtio/vhost-user.c also uses
inclusive sizing when interacting with the tree.
This patch is an updated version of one from the patch series "vhost-user:
isolated memory". The patch has been isolated from the series as it
is not tightly coupled with the rest of the series.
Signed-off-by: Connor Kite <connorkite@gmail.com>
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
Reviewed-by: Stefano Garzarella <sgarzare@redhat.com>
Reviewed-by: Eugenio Pérez <eperezma@redhat.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Fixes: 34e3c94edaef ("vdpa: Add custom IOTLB translations to SVQ")
Message-ID: <20260817233147.2867623-1-connorkite@gmail.com>
---
hw/virtio/vhost-shadow-virtqueue.c | 10 ++++++++--
1 file changed, 8 insertions(+), 2 deletions(-)
diff --git a/hw/virtio/vhost-shadow-virtqueue.c b/hw/virtio/vhost-shadow-virtqueue.c
index bcb7f2ffc7..c8831d52be 100644
--- a/hw/virtio/vhost-shadow-virtqueue.c
+++ b/hw/virtio/vhost-shadow-virtqueue.c
@@ -99,19 +99,25 @@ static bool vhost_svq_translate_addr(const VhostShadowVirtqueue *svq,
const DMAMap *map;
DMAMap needle;
+ if (unlikely(iovec[i].iov_len == 0)) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "Zero-sized buffer made available by guest");
+ return false;
+ }
+
/* Check if the descriptor is backed by guest memory */
if (gpas) {
/* Search the GPA->IOVA tree */
needle = (DMAMap) {
.translated_addr = gpas[i],
- .size = iovec[i].iov_len,
+ .size = iovec[i].iov_len - 1, /* Inclusive */
};
map = vhost_iova_tree_find_gpa(svq->iova_tree, &needle);
} else {
/* Search the IOVA->HVA tree */
needle = (DMAMap) {
.translated_addr = (hwaddr)(uintptr_t)iovec[i].iov_base,
- .size = iovec[i].iov_len,
+ .size = iovec[i].iov_len - 1, /* Inclusive */
};
map = vhost_iova_tree_find_iova(svq->iova_tree, &needle);
}
--
MST
^ permalink raw reply related [flat|nested] 84+ messages in thread
* [PULL 04/78] tests/functional: add test_tap_migration
2026-10-28 17:13 [PULL 00/78] pci, vhost, virtio, iommu, acpi: features, fixes Michael S. Tsirkin
` (2 preceding siblings ...)
2026-10-28 17:14 ` [PULL 03/78] hw/virtio/vhost-shadow-virtqueue: fix range boundary in SVQ translation Michael S. Tsirkin
@ 2026-10-28 17:14 ` Michael S. Tsirkin
2026-10-28 17:14 ` [PULL 05/78] net/tap: tap_post_load(): poll only on success Michael S. Tsirkin
` (74 subsequent siblings)
78 siblings, 0 replies; 84+ messages in thread
From: Michael S. Tsirkin @ 2026-10-28 17:14 UTC (permalink / raw)
To: qemu-devel
Cc: Peter Maydell, Vladimir Sementsov-Ogievskiy, Peter Xu,
Paolo Bonzini, Zhao Liu, Fabiano Rosas
From: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
Add test for a new local-migration migration of virtio-net/tap, with fd
passing through UNIX socket.
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
Acked-by: Peter Xu <peterx@redhat.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Message-ID: <20260914175351.393680-2-vsementsov@yandex-team.ru>
---
tests/functional/x86_64/meson.build | 1 +
tests/functional/x86_64/test_tap_migration.py | 425 ++++++++++++++++++
2 files changed, 426 insertions(+)
create mode 100755 tests/functional/x86_64/test_tap_migration.py
diff --git a/tests/functional/x86_64/meson.build b/tests/functional/x86_64/meson.build
index 0353b2af8e..6254149239 100644
--- a/tests/functional/x86_64/meson.build
+++ b/tests/functional/x86_64/meson.build
@@ -51,4 +51,5 @@ tests_x86_64_system_thorough = [
'virtio_balloon',
'virtio_gpu',
'rebuild_vmfd',
+ 'tap_migration',
]
diff --git a/tests/functional/x86_64/test_tap_migration.py b/tests/functional/x86_64/test_tap_migration.py
new file mode 100755
index 0000000000..a34bd94fd2
--- /dev/null
+++ b/tests/functional/x86_64/test_tap_migration.py
@@ -0,0 +1,425 @@
+#!/usr/bin/env python3
+#
+# Functional test that tests TAP local migration
+# with fd passing
+#
+# Copyright (c) Yandex Technologies LLC, 2026
+#
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+import os
+import time
+import subprocess
+from subprocess import run
+import signal
+import ctypes
+import ctypes.util
+import unittest
+from contextlib import contextmanager, ExitStack
+
+from qemu_test import (
+ LinuxKernelTest,
+ Asset,
+ exec_command_and_wait_for_pattern,
+ skipIfMissingCommands,
+)
+from qemu_test.decorators import skipWithoutSudo
+
+
+GUEST_IP = "192.168.100.2"
+GUEST_IP_MASK = f"{GUEST_IP}/24"
+GUEST_MAC = "d6:0d:75:f8:0f:b7"
+HOST_IP = "192.168.100.1"
+HOST_IP_MASK = f"{HOST_IP}/24"
+TAP_ID = "tap0"
+TAP_ID2 = "tap1"
+TAP_MAC = "e6:1d:44:b5:03:5d"
+NETNS = f"qemu_test_ns_{os.getpid()}"
+
+
+def ip(args, check=True) -> None:
+ """Run ip command with sudo"""
+ run(["sudo", "ip"] + args, check=check)
+
+
+@contextmanager
+def switch_netns(netns_name):
+ libc = ctypes.CDLL(ctypes.util.find_library("c"))
+ netns_path = f"/var/run/netns/{netns_name}"
+
+ def switch_to_fd(fd, check: bool = False):
+ """Switch to netns by file descriptor"""
+ sys_setns = 308
+ clone_newnet = 0x40000000
+ ret = libc.syscall(sys_setns, fd, clone_newnet)
+ if check and ret != 0:
+ raise RuntimeError("syscall SETNS failed")
+
+ with ExitStack() as stack:
+ original_netns_fd = os.open("/proc/self/ns/net", os.O_RDONLY)
+ stack.callback(os.close, original_netns_fd)
+
+ ip(["netns", "add", netns_name])
+ stack.callback(ip, ["netns", "del", netns_name], check=False)
+
+ new_netns_fd = os.open(netns_path, os.O_RDONLY)
+ stack.callback(os.close, new_netns_fd)
+
+ switch_to_fd(new_netns_fd)
+ stack.callback(switch_to_fd, original_netns_fd, check=False)
+
+ yield
+
+
+def del_tap(tap_name: str = TAP_ID) -> None:
+ ip(["tuntap", "del", tap_name, "mode", "tap", "multi_queue"], check=False)
+
+
+def init_tap(tap_name: str = TAP_ID, with_ip: bool = True) -> None:
+ ip(["tuntap", "add", "dev", tap_name, "mode", "tap", "multi_queue"])
+ if with_ip:
+ ip(["link", "set", "dev", tap_name, "address", TAP_MAC])
+ ip(["addr", "add", HOST_IP_MASK, "dev", tap_name])
+ ip(["link", "set", tap_name, "up"])
+
+
+def switch_network_to_tap2() -> None:
+ ip(["link", "set", TAP_ID2, "down"])
+ ip(["link", "set", TAP_ID, "down"])
+ ip(["addr", "delete", HOST_IP_MASK, "dev", TAP_ID])
+ ip(["link", "set", "dev", TAP_ID2, "address", TAP_MAC])
+ ip(["addr", "add", HOST_IP_MASK, "dev", TAP_ID2])
+ ip(["link", "set", TAP_ID2, "up"])
+
+
+def parse_ping_line(line: str) -> float:
+ # suspect lines like
+ # [1748524876.590509] 64 bytes from 94.245.155.3 \
+ # (94.245.155.3): icmp_seq=1 ttl=250 time=101 ms
+ spl = line.split()
+ return float(spl[0][1:-1])
+
+
+def parse_ping_output(out) -> float:
+ lines = [x for x in out.split("\n") if x.startswith("[")]
+
+ max_gap = 0.0
+ prev_timestamp = None
+
+ for line in lines:
+ if "no ans" not in line:
+ # This is a successful ping line
+ try:
+ timestamp = parse_ping_line(line)
+ if prev_timestamp is not None:
+ gap = timestamp - prev_timestamp
+ if gap > max_gap:
+ max_gap = gap
+ prev_timestamp = timestamp
+ except (IndexError, ValueError):
+ # Skip malformed lines
+ continue
+
+ assert prev_timestamp is not None, "No successful pings found"
+ assert max_gap > 0.0, "Need at least two successful pings to calculate gap"
+
+ return max_gap
+
+
+def wait_migration_finish(source_vm, target_vm):
+ migr_events = (
+ ("MIGRATION", {"data": {"status": "completed"}}),
+ ("MIGRATION", {"data": {"status": "failed"}}),
+ )
+
+ source_e = source_vm.events_wait(migr_events)["data"]
+ target_e = target_vm.events_wait(migr_events)["data"]
+
+ source_s = source_vm.cmd("query-status")["status"]
+ target_s = target_vm.cmd("query-status")["status"]
+
+ assert (
+ source_e["status"] == "completed"
+ and target_e["status"] == "completed"
+ and source_s == "postmigrate"
+ and target_s == "paused"
+ ), f"""Migration failed:
+ SRC status: {source_s}
+ SRC event: {source_e}
+ TGT status: {target_s}
+ TGT event:{target_e}"""
+
+
+@skipIfMissingCommands("ip", "ping")
+@skipWithoutSudo()
+class TAPFdMigration(LinuxKernelTest):
+ ASSET_ALPINE_ISO = Asset(
+ (
+ "https://dl-cdn.alpinelinux.org/"
+ "alpine/v3.22/releases/x86_64/alpine-standard-3.22.1-x86_64.iso"
+ ),
+ "96d1b44ea1b8a5a884f193526d92edb4676054e9fa903ad2f016441a0fe13089",
+ )
+
+ @classmethod
+ def setUpClass(cls):
+ super().setUpClass()
+
+ # The test namespace has no external connectivity.
+ cls.ASSET_ALPINE_ISO.fetch()
+
+ try:
+ cls.netns_context = switch_netns(NETNS)
+ cls.netns_context.__enter__()
+ except (OSError, subprocess.CalledProcessError) as e:
+ raise unittest.SkipTest(f"can't switch network namespace: {e}")
+
+ @classmethod
+ def tearDownClass(cls):
+ if hasattr(cls, "netns_context"):
+ cls.netns_context.__exit__(None, None, None)
+ super().tearDownClass()
+
+ def setUp(self):
+ super().setUp()
+
+ self.require_accelerator("kvm")
+ self.set_machine("q35")
+
+ self.setup_shared_memory()
+
+ init_tap()
+
+ self.outer_ping_proc = None
+
+ def tearDown(self):
+ with ExitStack() as stack:
+ stack.callback(super().tearDown)
+
+ if self.shm_path:
+ stack.callback(lambda p: (os.unlink(p) if os.path.exists(p)
+ else None),
+ self.shm_path)
+ self.shm_path = None
+
+ stack.callback(del_tap, TAP_ID2)
+ stack.callback(del_tap, TAP_ID)
+
+ if self.outer_ping_proc:
+ self.stop_outer_ping()
+
+ def start_outer_ping(self) -> None:
+ assert self.outer_ping_proc is None
+ self.outer_ping_log = self.scratch_file("ping.log")
+ with open(self.outer_ping_log, "w", encoding="utf-8") as f:
+ self.outer_ping_proc = subprocess.Popen(
+ ["sudo", "ping", "-i", "0", "-O", "-D", GUEST_IP],
+ text=True,
+ stdout=f,
+ )
+
+ def stop_outer_ping(self) -> str:
+ assert self.outer_ping_proc
+ self.outer_ping_proc.send_signal(signal.SIGINT)
+
+ self.outer_ping_proc.communicate(timeout=5)
+ self.outer_ping_proc = None
+
+ with open(self.outer_ping_log, encoding="utf-8") as f:
+ return f.read()
+
+ def stop_ping_and_check(self):
+ ping_res = self.stop_outer_ping()
+
+ disconnect_time = parse_ping_output(ping_res)
+ if disconnect_time > 0.5:
+ self.fail(f"long disconnect time: {disconnect_time}")
+
+ def one_ping_from_guest(self, vm) -> None:
+ exec_command_and_wait_for_pattern(
+ self,
+ f"ping -c 1 -W 1 {HOST_IP}",
+ "1 packets transmitted, 1 packets received",
+ "1 packets transmitted, 0 packets received",
+ vm=vm,
+ )
+ self.wait_for_console_pattern("# ", vm=vm)
+
+ def one_ping_from_host(self) -> None:
+ run(
+ ["ping", "-c", "1", "-W", "1", GUEST_IP],
+ stdout=subprocess.DEVNULL,
+ check=True,
+ )
+
+ def setup_shared_memory(self):
+ self.shm_path = f"/dev/shm/qemu_test_{os.getpid()}"
+
+ try:
+ with open(self.shm_path, "wb") as f:
+ f.write(b"\0" * (1024 * 1024 * 1024)) # 1GB
+ except OSError as e:
+ self.fail(f"Failed to create shared memory file: {e}")
+
+ def prepare_vm(self, vm, shm_path, incoming):
+ if not vm:
+ vm = self.vm
+
+ vm.set_console()
+ vm.add_args("-accel", "kvm")
+ vm.add_args("-device", "pcie-pci-bridge,id=pci.1,bus=pcie.0")
+ vm.add_args("-m", "1G")
+ vm.add_args("-net", "none")
+
+ vm.add_args(
+ "-object",
+ f"memory-backend-file,id=ram0,size=1G,mem-path={shm_path},share=on",
+ )
+ vm.add_args("-machine", "memory-backend=ram0")
+
+ vm.add_args(
+ "-drive",
+ f"file={self.ASSET_ALPINE_ISO.fetch()},media=cdrom,format=raw",
+ )
+
+ vm.add_args("-S")
+
+ if incoming:
+ vm.add_args("-incoming", "defer")
+
+ def add_virtio_net(
+ self, vm, vhost: bool, tap_name: str, local: bool, incoming: bool
+ ):
+ netdev_params = {
+ "id": "netdev.1",
+ "vhost": vhost,
+ "type": "tap",
+ "queues": 4,
+ "script": "no",
+ "downscript": "no",
+ "x-permit-local-migration": local,
+ }
+
+ if not (local and incoming):
+ netdev_params["vnet_hdr"] = True
+ netdev_params["ifname"] = tap_name
+
+ vm.cmd("netdev_add", netdev_params)
+
+ vm.cmd(
+ "device_add",
+ driver="virtio-net-pci",
+ romfile="",
+ id="vnet.1",
+ netdev="netdev.1",
+ mq=True,
+ vectors=18,
+ bus="pci.1",
+ mac=GUEST_MAC,
+ disable_legacy="off",
+ )
+
+ def set_migration_capabilities(self, vm, local):
+ vm.cmd(
+ "migrate-set-capabilities",
+ {
+ "capabilities": [
+ {"capability": "events", "state": True},
+ {"capability": "x-ignore-shared", "state": True},
+ ]
+ },
+ )
+ vm.cmd("migrate-set-parameters", {"local": local})
+
+ def setup_guest_network(self) -> None:
+ exec_command_and_wait_for_pattern(self, "ip addr", "# ")
+ exec_command_and_wait_for_pattern(
+ self,
+ f"ip addr add {GUEST_IP_MASK} dev eth0 && "
+ "ip link set eth0 up && echo OK",
+ "OK",
+ )
+ self.wait_for_console_pattern("# ")
+
+ def migrate(self, vm, mig_sock):
+ vm.cmd("migrate", uri=f"unix:{mig_sock}")
+
+ def do_test_tap_fd_migration(self, vhost, local=True):
+ socket_dir = self.socket_dir()
+ mig_sock = os.path.join(socket_dir.name, "mig.sock")
+
+ # Setup second TAP if needed
+ if not local:
+ del_tap(TAP_ID2)
+ init_tap(TAP_ID2, with_ip=False)
+
+ self.prepare_vm(self.vm, self.shm_path, False)
+ self.vm.launch()
+ self.set_migration_capabilities(self.vm, local=local)
+ self.add_virtio_net(self.vm, vhost, TAP_ID, local, incoming=False)
+
+ self.vm.cmd("cont")
+ self.wait_for_console_pattern("login:")
+ exec_command_and_wait_for_pattern(self, "root", "# ")
+
+ self.setup_guest_network()
+
+ self.one_ping_from_guest(self.vm)
+ self.one_ping_from_host()
+ self.start_outer_ping()
+
+ # Get some successful pings before migration
+ time.sleep(0.5)
+
+ target_vm = self.get_vm(name="target")
+ self.prepare_vm(target_vm, self.shm_path, True)
+
+ target_vm.launch()
+ if not local:
+ tap_name = TAP_ID2
+ else:
+ tap_name = TAP_ID
+ self.set_migration_capabilities(target_vm, local=local)
+ self.add_virtio_net(target_vm, vhost, tap_name, local, incoming=True)
+
+ target_vm.cmd("migrate-incoming", {"uri": f"unix:{mig_sock}"})
+
+ self.log.info("Starting migration")
+ self.migrate(self.vm, mig_sock)
+
+ self.log.info("Waiting for migration completion")
+ wait_migration_finish(self.vm, target_vm)
+
+ # Switch network to tap1 if not using local-migration
+ if not local:
+ switch_network_to_tap2()
+
+ target_vm.cmd("cont")
+
+ self.log.info("Verifying PING on target VM after migration")
+
+ # Keep the stopped source alive while checking the target.
+ time.sleep(0.3)
+ self.stop_ping_and_check()
+
+ self.one_ping_from_guest(target_vm)
+ self.one_ping_from_host()
+
+ self.vm.shutdown()
+ target_vm.shutdown()
+
+ def test_tap_fd_migration(self):
+ self.do_test_tap_fd_migration(False)
+
+ def test_tap_fd_migration_vhost(self):
+ self.do_test_tap_fd_migration(True)
+
+ def test_tap_new_tap_migration(self):
+ self.do_test_tap_fd_migration(False, local=False)
+
+ def test_tap_new_tap_migration_vhost(self):
+ self.do_test_tap_fd_migration(True, local=False)
+
+
+if __name__ == "__main__":
+ LinuxKernelTest.main()
--
MST
^ permalink raw reply related [flat|nested] 84+ messages in thread
* [PULL 05/78] net/tap: tap_post_load(): poll only on success
2026-10-28 17:13 [PULL 00/78] pci, vhost, virtio, iommu, acpi: features, fixes Michael S. Tsirkin
` (3 preceding siblings ...)
2026-10-28 17:14 ` [PULL 04/78] tests/functional: add test_tap_migration Michael S. Tsirkin
@ 2026-10-28 17:14 ` Michael S. Tsirkin
2026-10-28 17:14 ` [PULL 06/78] hw/net: finish backend initialization earler for local migration Michael S. Tsirkin
` (73 subsequent siblings)
78 siblings, 0 replies; 84+ messages in thread
From: Michael S. Tsirkin @ 2026-10-28 17:14 UTC (permalink / raw)
To: qemu-devel; +Cc: Peter Maydell, Vladimir Sementsov-Ogievskiy, Jason Wang
From: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
We shouldn't call tap_read_poll() if s->fd < 0.
Let's move tap_read_poll() to the end, after check
and vhost initialization.
Fixes: c4d884bb5444 ("net/tap: support local migration with virtio-net")
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Message-ID: <20260914175351.393680-3-vsementsov@yandex-team.ru>
---
net/tap.c | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/net/tap.c b/net/tap.c
index d71e3dd117..8039b4e388 100644
--- a/net/tap.c
+++ b/net/tap.c
@@ -490,8 +490,6 @@ static bool tap_post_load(void *opaque, int version_id, Error **errp)
ERRP_GUARD();
TAPState *s = opaque;
- tap_read_poll(s, true);
-
if (s->fd < 0) {
error_setg(errp, "FD was not loaded during incoming migration");
return false;
@@ -503,6 +501,7 @@ static bool tap_post_load(void *opaque, int version_id, Error **errp)
return false;
}
+ tap_read_poll(s, true);
return true;
}
--
MST
^ permalink raw reply related [flat|nested] 84+ messages in thread
* [PULL 06/78] hw/net: finish backend initialization earler for local migration
2026-10-28 17:13 [PULL 00/78] pci, vhost, virtio, iommu, acpi: features, fixes Michael S. Tsirkin
` (4 preceding siblings ...)
2026-10-28 17:14 ` [PULL 05/78] net/tap: tap_post_load(): poll only on success Michael S. Tsirkin
@ 2026-10-28 17:14 ` Michael S. Tsirkin
2026-10-28 17:14 ` [PULL 07/78] net/tap: move vmstate initialization to new_tap() Michael S. Tsirkin
` (72 subsequent siblings)
78 siblings, 0 replies; 84+ messages in thread
From: Michael S. Tsirkin @ 2026-10-28 17:14 UTC (permalink / raw)
To: qemu-devel; +Cc: Peter Maydell, Vladimir Sementsov-Ogievskiy, Jason Wang
From: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
Thanks to pririty, backend is migrated before the virtio-net itself.
So we can finish the releate initialization already in .pre_load,
not postponing it to .post_load.
Fixes: bb71b1bffe58 ("virtio-net: support local migration of backend")
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Message-ID: <20260914175351.393680-4-vsementsov@yandex-team.ru>
---
hw/net/virtio-net.c | 40 +++++++++++++++++-----------------------
1 file changed, 17 insertions(+), 23 deletions(-)
diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c
index 19f453fb7d..4794496b96 100644
--- a/hw/net/virtio-net.c
+++ b/hw/net/virtio-net.c
@@ -3250,13 +3250,28 @@ static void virtio_net_get_features(VirtIODevice *vdev, uint64_t *features,
}
}
+static bool virtio_net_pre_load_device(void *opaque, Error **errp)
+{
+ ERRP_GUARD();
+ VirtIONet *n = opaque;
+ VirtIODevice *vdev = VIRTIO_DEVICE(n);
+
+ if (n->peers_wait_incoming) {
+ /* MIG_PRI_BACKEND ensures the peers have already been loaded. */
+ n->peers_wait_incoming = false;
+ peer_test_vnet_hdr(n);
+ virtio_net_get_features(vdev, vdev->host_features_ex, errp);
+ }
+
+ return !*errp;
+}
+
static int virtio_net_post_load_device(void *opaque, int version_id)
{
VirtIONet *n = opaque;
VirtIODevice *vdev = VIRTIO_DEVICE(n);
int i, link_down;
bool has_tunnel_hdr = virtio_has_tunnel_hdr(vdev->guest_features_ex);
- Error *local_err = NULL;
trace_virtio_net_post_load_device();
virtio_net_set_mrg_rx_bufs(n, n->mergeable_rx_bufs,
@@ -3314,20 +3329,6 @@ static int virtio_net_post_load_device(void *opaque, int version_id)
}
virtio_net_commit_rss_config(n);
-
- /*
- * If live-migration is enabled for some backend, than backend
- * has already been migrated at higher priority (MIG_PRI_BACKEND)
- * and virtio_net_vnet_post_load() has already called
- * peer_test_vnet_hdr(). Recompute host_features so that virtio-net
- * reflects the capabilities of the restored backend.
- */
- virtio_net_get_features(vdev, &vdev->host_features, &local_err);
- if (local_err) {
- error_report_err(local_err);
- return -EINVAL;
- }
-
return 0;
}
@@ -3478,14 +3479,6 @@ static int virtio_net_vnet_post_load(void *opaque, int version_id)
{
struct VirtIONetMigTmp *tmp = opaque;
- /*
- * If live-migration is enabled for some backend, than backend
- * has already been migrated at higher priority (MIG_PRI_BACKEND),
- * so n->has_vnet_hdr can be refreshed from the live backend right
- * here.
- */
- peer_test_vnet_hdr(tmp->parent);
-
if (tmp->has_vnet_hdr && !peer_has_vnet_hdr(tmp->parent)) {
error_report("virtio-net: saved image requires vnet_hdr=on");
return -EINVAL;
@@ -3674,6 +3667,7 @@ static const VMStateDescription vmstate_virtio_net_device = {
.name = "virtio-net-device",
.version_id = VIRTIO_NET_VM_VERSION,
.minimum_version_id = VIRTIO_NET_VM_VERSION,
+ .pre_load_errp = virtio_net_pre_load_device,
.post_load = virtio_net_post_load_device,
.fields = (const VMStateField[]) {
VMSTATE_UINT8_ARRAY(mac, VirtIONet, ETH_ALEN),
--
MST
^ permalink raw reply related [flat|nested] 84+ messages in thread
* [PULL 07/78] net/tap: move vmstate initialization to new_tap()
2026-10-28 17:13 [PULL 00/78] pci, vhost, virtio, iommu, acpi: features, fixes Michael S. Tsirkin
` (5 preceding siblings ...)
2026-10-28 17:14 ` [PULL 06/78] hw/net: finish backend initialization earler for local migration Michael S. Tsirkin
@ 2026-10-28 17:14 ` Michael S. Tsirkin
2026-10-28 17:14 ` [PULL 08/78] net/tap: net_init_tap_one(): fix failure path Michael S. Tsirkin
` (71 subsequent siblings)
78 siblings, 0 replies; 84+ messages in thread
From: Michael S. Tsirkin @ 2026-10-28 17:14 UTC (permalink / raw)
To: qemu-devel; +Cc: Peter Maydell, Vladimir Sementsov-Ogievskiy, Jason Wang
From: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
Otherwise, incoming_fds case in net_init_tap() is not covered,
and the mechanism will not work for outgoing local migration,
if VM start life from incoming local migration.
Fixes: 98a0c2127070 ("net/tap: disable read polling for stopped VM")
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Message-ID: <20260914175351.393680-5-vsementsov@yandex-team.ru>
---
net/tap.c | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/net/tap.c b/net/tap.c
index 8039b4e388..9f829f49c1 100644
--- a/net/tap.c
+++ b/net/tap.c
@@ -623,6 +623,9 @@ static TAPState *new_tap(NetClientState *peer,
s->queue_index = queue_index;
+ s->enable_poll_on_resume = false;
+ s->vmstate = qemu_add_vm_change_state_handler(tap_vm_state_change, s);
+
if (has_permit_local_migration) {
s->permit_local_migration = permit_local_migration;
}
@@ -968,9 +971,6 @@ static bool net_init_tap_one(const NetdevTapOptions *tap, NetClientState *peer,
int sndbuf =
(tap->has_sndbuf && tap->sndbuf) ? MIN(tap->sndbuf, INT_MAX) : INT_MAX;
- s->enable_poll_on_resume = false;
- s->vmstate = qemu_add_vm_change_state_handler(tap_vm_state_change, s);
-
if (!tap_set_sndbuf(fd, sndbuf, sndbuf_required ? errp : NULL) &&
sndbuf_required) {
goto failed;
--
MST
^ permalink raw reply related [flat|nested] 84+ messages in thread
* [PULL 08/78] net/tap: net_init_tap_one(): fix failure path
2026-10-28 17:13 [PULL 00/78] pci, vhost, virtio, iommu, acpi: features, fixes Michael S. Tsirkin
` (6 preceding siblings ...)
2026-10-28 17:14 ` [PULL 07/78] net/tap: move vmstate initialization to new_tap() Michael S. Tsirkin
@ 2026-10-28 17:14 ` Michael S. Tsirkin
2026-10-28 17:14 ` [PULL 09/78] Revert 9696846600ca ("multiboot: Prevent loading of x86_64 images") Michael S. Tsirkin
` (70 subsequent siblings)
78 siblings, 0 replies; 84+ messages in thread
From: Michael S. Tsirkin @ 2026-10-28 17:14 UTC (permalink / raw)
To: qemu-devel; +Cc: Peter Maydell, Vladimir Sementsov-Ogievskiy, Jason Wang
From: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
We should "goto failed" here, to delete net client, same
as for previous failure path in the function.
Fixes: 69036a855492 ("net/tap: move vhost initialization to tap_setup_vhost()")
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Message-ID: <20260914175351.393680-6-vsementsov@yandex-team.ru>
---
net/tap.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/net/tap.c b/net/tap.c
index 9f829f49c1..acccabca56 100644
--- a/net/tap.c
+++ b/net/tap.c
@@ -996,7 +996,7 @@ static bool net_init_tap_one(const NetdevTapOptions *tap, NetClientState *peer,
s->vhostfd = vhostfd;
s->vhost_busyloop_timeout = tap->has_poll_us ? tap->poll_us : 0;
if (!tap_setup_vhost(s, errp)) {
- return false;
+ goto failed;
}
return true;
--
MST
^ permalink raw reply related [flat|nested] 84+ messages in thread
* [PULL 09/78] Revert 9696846600ca ("multiboot: Prevent loading of x86_64 images")
2026-10-28 17:13 [PULL 00/78] pci, vhost, virtio, iommu, acpi: features, fixes Michael S. Tsirkin
` (7 preceding siblings ...)
2026-10-28 17:14 ` [PULL 08/78] net/tap: net_init_tap_one(): fix failure path Michael S. Tsirkin
@ 2026-10-28 17:14 ` Michael S. Tsirkin
2026-10-28 17:14 ` [PULL 10/78] migration/cpr: forbid saving duplicate CprFds into hash table Michael S. Tsirkin
` (69 subsequent siblings)
78 siblings, 0 replies; 84+ messages in thread
From: Michael S. Tsirkin @ 2026-10-28 17:14 UTC (permalink / raw)
To: qemu-devel
Cc: Peter Maydell, Samuel Thibault, Paolo Bonzini, Richard Henderson
From: Samuel Thibault <samuel.thibault@ens-lyon.org>
Loading x86_64 images in multiboot was disabled on the ground that since
multiboot actually boots 64bit kernels in 32bit mode, the binary would
have to be 32bit.
https://lists.gnu.org/archive/html/qemu-devel/2010-08/msg00913.html
But making the binary 32bit makes using debugging tools such as gdb
unnecessarily complex since they don't understand what architecture the
kernel actually is. Letting qemu load x86_64 images and boot them in 32bit
mode is completely fine, works, and is what the multiboot standard actually
expects.
This notably fixes loading gnumach in x86_64 mode.
Signed-off-by: Samuel Thibault <samuel.thibault@ens-lyon.org>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Message-ID: <20260829175047.2710519-1-samuel.thibault@ens-lyon.org>
---
hw/i386/multiboot.c | 5 -----
1 file changed, 5 deletions(-)
diff --git a/hw/i386/multiboot.c b/hw/i386/multiboot.c
index d544b62afb..004d5185fb 100644
--- a/hw/i386/multiboot.c
+++ b/hw/i386/multiboot.c
@@ -196,11 +196,6 @@ int load_multiboot(X86MachineState *x86ms,
int kernel_size;
fclose(f);
- if (((struct elf64_hdr*)header)->e_machine == EM_X86_64) {
- error_report("Cannot load x86-64 image, give a 32bit one.");
- exit(1);
- }
-
kernel_size = load_elf(kernel_filename, NULL, NULL, NULL, &elf_entry,
&elf_low, &elf_high, NULL,
ELFDATA2LSB, I386_ELF_MACHINE, 0, 0);
--
MST
^ permalink raw reply related [flat|nested] 84+ messages in thread
* [PULL 10/78] migration/cpr: forbid saving duplicate CprFds into hash table
2026-10-28 17:13 [PULL 00/78] pci, vhost, virtio, iommu, acpi: features, fixes Michael S. Tsirkin
` (8 preceding siblings ...)
2026-10-28 17:14 ` [PULL 09/78] Revert 9696846600ca ("multiboot: Prevent loading of x86_64 images") Michael S. Tsirkin
@ 2026-10-28 17:14 ` Michael S. Tsirkin
2026-10-28 17:14 ` [PULL 11/78] tests/unit: add test for duplicate keys in CPR FD table Michael S. Tsirkin
` (68 subsequent siblings)
78 siblings, 0 replies; 84+ messages in thread
From: Michael S. Tsirkin @ 2026-10-28 17:14 UTC (permalink / raw)
To: qemu-devel
Cc: Peter Maydell, Andrey Drobyshev, Peter Xu, Cédric Le Goater,
Akihiko Odaki, David Hildenbrand, Igor Mammedov, Yi Liu,
Eric Auger, Zhenzhong Duan, Alex Williamson, Maciej S. Szmigiero,
Fabiano Rosas, Mark Kanda, Ben Chaney, Paolo Bonzini,
Philippe Mathieu-Daudé
From: Andrey Drobyshev <andrey.drobyshev@virtuozzo.com>
In its current form, the mechanism for saving CPR FDs into hash table
has flaws.
The CPR fd hash table uses the same CprFd for both key and value, and
is created with a key destroy function. Inserting a second element
with the same (name, id) key leads to a use after free. According to
GLib docs [1], g_hash_table_insert() keeps the old key, replaces the
value with the new element, and destroys the new key. Thus when
inserting 2 CprFd elements fd1 and fd2 with equal keys we get:
g_hash_table_insert(table, fd1, fd1); /* table has fd1 -> fd1 */
g_hash_table_insert(table, fd2, fd2); /* table has fd1 -> fd2, fd2 freed */
Any subsequent cpr_find_fd() for that key then returns freed memory.
This is reachable whenever two CPR-aware objects register fds under the
same name, and it happens long before an actual CPR, since cpr_save_fd()
is typically called from .realize(), i.e. at boot time.
Apart from the UAF itself, when the user decides to perform CPR migration,
on CPR target we'd get corrupted FD from the hash table and fail somewhere
in .realize() for affected devices.
As a solution, let's altogether prohibit adding CprFds with duplicate
keys. Since FDs are being registered for a subsequent CPR right at the
startup in .realize(), that'll allow us to error out and exit QEMU (or
fail a device hotplug op) gracefully instead of delaying potential
problems for later. To do so, let's make cpr_save_fd() return 'bool'
instead of 'void' and provide an 'errp' argument to it. That way caller
becomes responsible for checking the return value and propagating the
error.
Also update all the callers to use the adjusted interface to keep things
compilable and bisectable.
[1] https://docs.gtk.org/glib/type_func.HashTable.insert.html
Fixes: a181df93bb32 ("migration/cpr: use hashtable for cpr fds")
Reviewed-by: Peter Xu <peterx@redhat.com>
Reviewed-by: Cédric Le Goater <clg@redhat.com>
Signed-off-by: Andrey Drobyshev <andrey.drobyshev@virtuozzo.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Reviewed-by: Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Message-ID: <20261012-up-vsock-vhost-v5-v16-1-36c38bd15ffc@virtuozzo.com>
---
include/hw/vfio/vfio-cpr.h | 7 +++--
include/migration/cpr.h | 2 +-
backends/hostmem-memfd.c | 5 ++-
backends/hostmem-shm.c | 5 ++-
backends/iommufd.c | 4 ++-
hw/vfio/container-legacy.c | 7 +----
hw/vfio/cpr-legacy.c | 31 +++++++++++++-----
hw/vfio/cpr.c | 6 ++--
hw/vfio/pci.c | 5 ++-
migration/cpr.c | 64 ++++++++++++++++++++++++--------------
system/physmem.c | 5 +--
11 files changed, 92 insertions(+), 49 deletions(-)
diff --git a/include/hw/vfio/vfio-cpr.h b/include/hw/vfio/vfio-cpr.h
index ecabe0c747..9157a4ac64 100644
--- a/include/hw/vfio/vfio-cpr.h
+++ b/include/hw/vfio/vfio-cpr.h
@@ -60,7 +60,8 @@ void vfio_iommufd_cpr_register_device(struct VFIODevice *vbasedev);
void vfio_iommufd_cpr_unregister_device(struct VFIODevice *vbasedev);
void vfio_cpr_load_device(struct VFIODevice *vbasedev);
-int vfio_cpr_group_get_device_fd(int d, const char *name);
+int vfio_cpr_group_get_device_fd(struct VFIOGroup *group, const char *name,
+ Error **errp);
bool vfio_cpr_container_match(struct VFIOLegacyContainer *container,
struct VFIOGroup *group, int fd);
@@ -71,8 +72,8 @@ void vfio_cpr_giommu_remap(struct VFIOContainer *bcontainer,
bool vfio_cpr_ram_discard_replay_populated(
struct VFIOContainer *bcontainer, const MemoryRegionSection *section);
-void vfio_cpr_save_vector_fd(struct VFIOPCIDevice *vdev, const char *name,
- int nr, int fd);
+bool vfio_cpr_save_vector_fd(struct VFIOPCIDevice *vdev, const char *name,
+ int nr, int fd, Error **errp);
int vfio_cpr_load_vector_fd(struct VFIOPCIDevice *vdev, const char *name,
int nr);
void vfio_cpr_delete_vector_fd(struct VFIOPCIDevice *vdev, const char *name,
diff --git a/include/migration/cpr.h b/include/migration/cpr.h
index 56fb67e6b4..3ad084d5ba 100644
--- a/include/migration/cpr.h
+++ b/include/migration/cpr.h
@@ -28,7 +28,7 @@ typedef struct CprState {
extern CprState cpr_state;
-void cpr_save_fd(const char *name, int id, int fd);
+bool cpr_save_fd(const char *name, int id, int fd, Error **errp);
void cpr_delete_fd(const char *name, int id);
int cpr_find_fd(const char *name, int id);
void cpr_resave_fd(const char *name, int id, int fd);
diff --git a/backends/hostmem-memfd.c b/backends/hostmem-memfd.c
index 666593ff6c..67f1924a36 100644
--- a/backends/hostmem-memfd.c
+++ b/backends/hostmem-memfd.c
@@ -84,7 +84,10 @@ memfd_backend_memory_alloc(HostMemoryBackend *backend, Error **errp)
if (fd < 0) {
return false;
}
- cpr_save_fd(name, 0, fd);
+ if (!cpr_save_fd(name, 0, fd, errp)) {
+ close(fd);
+ return false;
+ }
have_fd:
backend->aligned = true;
diff --git a/backends/hostmem-shm.c b/backends/hostmem-shm.c
index 4766db6aad..adf60957c3 100644
--- a/backends/hostmem-shm.c
+++ b/backends/hostmem-shm.c
@@ -48,7 +48,10 @@ shm_backend_memory_alloc(HostMemoryBackend *backend, Error **errp)
if (fd < 0) {
return false;
}
- cpr_save_fd(backend_name, 0, fd);
+ if (!cpr_save_fd(backend_name, 0, fd, errp)) {
+ close(fd);
+ return false;
+ }
have_fd:
/* Let's do the same as memory-backend-ram,share=on would do. */
diff --git a/backends/iommufd.c b/backends/iommufd.c
index 15f2a51350..45fd2e44c7 100644
--- a/backends/iommufd.c
+++ b/backends/iommufd.c
@@ -85,7 +85,9 @@ static void iommufd_backend_complete(UserCreatable *uc, Error **errp)
if (cpr_is_incoming()) {
be->fd = cpr_find_fd(name, 0);
} else {
- cpr_save_fd(name, 0, be->fd);
+ if (!cpr_save_fd(name, 0, be->fd, errp)) {
+ return;
+ }
}
} else if (!g_file_test("/dev/iommu", G_FILE_TEST_EXISTS)) {
error_setg(errp, "/dev/iommu does not exist"
diff --git a/hw/vfio/container-legacy.c b/hw/vfio/container-legacy.c
index 7ac7b37214..50ca5a3a7e 100644
--- a/hw/vfio/container-legacy.c
+++ b/hw/vfio/container-legacy.c
@@ -847,13 +847,8 @@ static bool vfio_device_get(VFIOGroup *group, const char *name,
g_autofree struct vfio_device_info *info = NULL;
int fd;
- fd = vfio_cpr_group_get_device_fd(group->fd, name);
+ fd = vfio_cpr_group_get_device_fd(group, name, errp);
if (fd < 0) {
- error_setg_errno(errp, errno, "error getting device from group %d",
- group->groupid);
- error_append_hint(errp,
- "Verify all devices in group %d are bound to vfio-<bus> "
- "or pci-stub and not already in use\n", group->groupid);
return false;
}
diff --git a/hw/vfio/cpr-legacy.c b/hw/vfio/cpr-legacy.c
index 2d40d8baea..807ffacdbc 100644
--- a/hw/vfio/cpr-legacy.c
+++ b/hw/vfio/cpr-legacy.c
@@ -254,17 +254,33 @@ bool vfio_cpr_ram_discard_replay_populated(VFIOContainer *bcontainer,
&vrdl->listener) == 0;
}
-int vfio_cpr_group_get_device_fd(int d, const char *name)
+int vfio_cpr_group_get_device_fd(VFIOGroup *group, const char *name,
+ Error **errp)
{
+ ERRP_GUARD();
const int id = 0;
int fd = cpr_find_fd(name, id);
- if (fd < 0) {
- fd = ioctl(d, VFIO_GROUP_GET_DEVICE_FD, name);
- if (fd >= 0) {
- cpr_save_fd(name, id, fd);
- }
+ if (fd >= 0) {
+ return fd;
}
+
+ fd = ioctl(group->fd, VFIO_GROUP_GET_DEVICE_FD, name);
+ if (fd < 0) {
+ error_setg_errno(errp, errno, "error getting device from group %d",
+ group->groupid);
+ error_append_hint(errp,
+ "Verify all devices in group %d are bound to "
+ "vfio-<bus> or pci-stub and not already in use\n",
+ group->groupid);
+ return -1;
+ }
+
+ if (!cpr_save_fd(name, id, fd, errp)) {
+ close(fd);
+ return -1;
+ }
+
return fd;
}
@@ -291,6 +307,7 @@ bool vfio_cpr_container_match(VFIOLegacyContainer *container, VFIOGroup *group,
*/
cpr_delete_fd("vfio_container_for_group", group->groupid);
close(fd);
- cpr_save_fd("vfio_container_for_group", group->groupid, container->fd);
+ cpr_save_fd("vfio_container_for_group", group->groupid, container->fd,
+ &error_abort);
return true;
}
diff --git a/hw/vfio/cpr.c b/hw/vfio/cpr.c
index ffa4f8e099..6e1659dcdd 100644
--- a/hw/vfio/cpr.c
+++ b/hw/vfio/cpr.c
@@ -32,11 +32,11 @@ int vfio_cpr_reboot_notifier(NotifierWithReturn *notifier,
#define STRDUP_VECTOR_FD_NAME(vdev, name) \
g_strdup_printf("%s_%s", (vdev)->vbasedev.name, (name))
-void vfio_cpr_save_vector_fd(VFIOPCIDevice *vdev, const char *name, int nr,
- int fd)
+bool vfio_cpr_save_vector_fd(VFIOPCIDevice *vdev, const char *name, int nr,
+ int fd, Error **errp)
{
g_autofree char *fdname = STRDUP_VECTOR_FD_NAME(vdev, name);
- cpr_save_fd(fdname, nr, fd);
+ return cpr_save_fd(fdname, nr, fd, errp);
}
int vfio_cpr_load_vector_fd(VFIOPCIDevice *vdev, const char *name, int nr)
diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c
index 428ab2f069..ad13f2306f 100644
--- a/hw/vfio/pci.c
+++ b/hw/vfio/pci.c
@@ -76,7 +76,10 @@ static bool vfio_notifier_init(VFIOPCIDevice *vdev, EventNotifier *e,
}
fd = event_notifier_get_fd(e);
- vfio_cpr_save_vector_fd(vdev, name, nr, fd);
+ if (!vfio_cpr_save_vector_fd(vdev, name, nr, fd, errp)) {
+ event_notifier_cleanup(e);
+ return false;
+ }
return true;
}
diff --git a/migration/cpr.c b/migration/cpr.c
index bca43e9bf3..cb027bba50 100644
--- a/migration/cpr.c
+++ b/migration/cpr.c
@@ -82,10 +82,29 @@ static GHashTable *get_cpr_fds_hash(void)
return cpr_fds_hash;
}
+static CprFd *find_fd(const char *name, int id)
+{
+ CprFd key = {
+ .name = (char *)name,
+ .id = id,
+ };
+
+ return g_hash_table_lookup(get_cpr_fds_hash(), &key);
+}
+
static void cpr_fd_hash_insert(CprFd *elem)
{
- /* Use the same CprFd as key and value. */
- g_hash_table_insert(get_cpr_fds_hash(), elem, elem);
+ GHashTable *hash = get_cpr_fds_hash();
+
+ /*
+ * We prohibit inserting duplicate keys into the hash table. All callers
+ * are responsible for checking that each added key is unique.
+ *
+ * 'elem' serves as both key and value, and a replacing insert would
+ * silently free it. Therefore we assert, not overwrite.
+ */
+ g_assert(!g_hash_table_contains(hash, elem));
+ g_hash_table_insert(hash, elem, elem);
}
static int cpr_fd_pre_save(void *opaque)
@@ -127,26 +146,24 @@ static int cpr_fd_post_load(void *opaque, int version_id)
return 0;
}
-void cpr_save_fd(const char *name, int id, int fd)
+bool cpr_save_fd(const char *name, int id, int fd, Error **errp)
{
- CprFd *elem = g_new0(CprFd, 1);
+ CprFd *elem;
+ if (find_fd(name, id)) {
+ error_setg(errp, "cpr fd '%s' id %d is already registered", name, id);
+ return false;
+ }
+
+ elem = g_new0(CprFd, 1);
trace_cpr_save_fd(name, id, fd);
elem->name = g_strdup(name);
elem->namelen = strlen(name) + 1;
elem->id = id;
elem->fd = fd;
cpr_fd_hash_insert(elem);
-}
-static CprFd *find_fd(const char *name, int id)
-{
- CprFd key = {
- .name = (char *)name,
- .id = id,
- };
-
- return g_hash_table_lookup(get_cpr_fds_hash(), &key);
+ return true;
}
void cpr_delete_fd(const char *name, int id)
@@ -173,14 +190,13 @@ int cpr_find_fd(const char *name, int id)
void cpr_resave_fd(const char *name, int id, int fd)
{
CprFd *elem = find_fd(name, id);
- int old_fd = elem ? elem->fd : -1;
- if (old_fd < 0) {
- cpr_save_fd(name, id, fd);
- } else if (old_fd != fd) {
+ if (!elem) {
+ cpr_save_fd(name, id, fd, &error_abort);
+ } else if (elem->fd != fd) {
error_report("internal error: cpr fd '%s' id %d value %d "
"already saved with a different value %d",
- name, id, fd, old_fd);
+ name, id, fd, elem->fd);
g_assert_not_reached();
}
}
@@ -192,8 +208,9 @@ int cpr_open_fd(const char *path, int flags, const char *name, int id,
if (fd < 0) {
fd = qemu_open(path, flags, errp);
- if (fd >= 0) {
- cpr_save_fd(name, id, fd);
+ if (fd >= 0 && !cpr_save_fd(name, id, fd, errp)) {
+ close(fd);
+ fd = -1;
}
}
return fd;
@@ -400,10 +417,11 @@ int cpr_get_fd_param(const char *name, const char *fdname, int index,
}
} else {
fd = monitor_fd_param(monitor_cur(), fdname, errp);
- if (fd >= 0) {
- cpr_save_fd(name, index, fd);
- } else {
+ if (fd < 0) {
error_prepend(errp, "Could not parse object fd %s:", fdname);
+ } else if (!cpr_save_fd(name, index, fd, errp)) {
+ close(fd);
+ fd = -1;
}
}
return fd;
diff --git a/system/physmem.c b/system/physmem.c
index db3dda563d..a7300bb9d9 100644
--- a/system/physmem.c
+++ b/system/physmem.c
@@ -2456,8 +2456,9 @@ static int qemu_ram_get_shared_fd(const char *name, bool *reused, Error **errp)
fd = qemu_shm_alloc(0, errp);
}
- if (fd >= 0) {
- cpr_save_fd(name, 0, fd);
+ if (fd >= 0 && !cpr_save_fd(name, 0, fd, errp)) {
+ close(fd);
+ fd = -1;
}
*reused = false;
return fd;
--
MST
^ permalink raw reply related [flat|nested] 84+ messages in thread
* [PULL 11/78] tests/unit: add test for duplicate keys in CPR FD table
2026-10-28 17:13 [PULL 00/78] pci, vhost, virtio, iommu, acpi: features, fixes Michael S. Tsirkin
` (9 preceding siblings ...)
2026-10-28 17:14 ` [PULL 10/78] migration/cpr: forbid saving duplicate CprFds into hash table Michael S. Tsirkin
@ 2026-10-28 17:14 ` Michael S. Tsirkin
2026-10-28 17:14 ` [PULL 12/78] migration: forbid snapshots in CPR migration modes Michael S. Tsirkin
` (67 subsequent siblings)
78 siblings, 0 replies; 84+ messages in thread
From: Michael S. Tsirkin @ 2026-10-28 17:14 UTC (permalink / raw)
To: qemu-devel
Cc: Peter Maydell, Andrey Drobyshev, Akihiko Odaki,
Marc-André Lureau, Stefan Hajnoczi, Denis V. Lunev,
Daniel P. Berrangé, Philippe Mathieu-Daudé
From: Andrey Drobyshev <andrey.drobyshev@virtuozzo.com>
cpr_save_fd() now refuses to register an FD under a (name, id) key which
is already taken. Add a test which checks that 2nd registration fails
with an error while the 1st one is left intact, that different names
or ids don't clash, and that the key can be reused once deleted.
Signed-off-by: Andrey Drobyshev <andrey.drobyshev@virtuozzo.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Reviewed-by: Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Message-ID: <20261012-up-vsock-vhost-v5-v16-2-36c38bd15ffc@virtuozzo.com>
---
tests/unit/test-cpr.c | 98 ++++++++++++++++++++++++++++++++++++++++++
tests/unit/meson.build | 1 +
2 files changed, 99 insertions(+)
create mode 100644 tests/unit/test-cpr.c
diff --git a/tests/unit/test-cpr.c b/tests/unit/test-cpr.c
new file mode 100644
index 0000000000..dfc6b1f2ec
--- /dev/null
+++ b/tests/unit/test-cpr.c
@@ -0,0 +1,98 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Unit tests for the CPR fd table
+ */
+
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "migration/cpr.h"
+#include "migration/vmstate.h"
+#include "monitor/monitor.h"
+#include "hw/vfio/vfio-cpr.h"
+
+/* Stubs for the parts of migration/cpr.c that these tests don't exercise */
+
+const VMStateDescription vmstate_cpr_vfio_devices = {
+ .name = "cpr vfio devices",
+ .fields = (const VMStateField[]) {
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+QEMUFile *cpr_transfer_output(MigrationChannel *channel, Error **errp)
+{
+ g_assert_not_reached();
+}
+
+QEMUFile *cpr_transfer_input(MigrationChannel *channel, Error **errp)
+{
+ g_assert_not_reached();
+}
+
+QEMUFile *cpr_exec_output(Error **errp)
+{
+ g_assert_not_reached();
+}
+
+QEMUFile *cpr_exec_input(Error **errp)
+{
+ g_assert_not_reached();
+}
+
+bool cpr_exec_persist_state(QEMUFile *f, Error **errp)
+{
+ g_assert_not_reached();
+}
+
+bool cpr_exec_has_state(void)
+{
+ return false;
+}
+
+void cpr_exec_unpreserve_fds(void)
+{
+}
+
+int monitor_fd_param(Monitor *mon, const char *fdname, Error **errp)
+{
+ g_assert_not_reached();
+}
+
+static void test_cpr_fd_duplicate(void)
+{
+ Error *err = NULL;
+
+ g_assert_true(cpr_save_fd("dup", 0, 10, &error_abort));
+
+ /* A second registration of the same (name, id) key must be refused */
+ g_assert_false(cpr_save_fd("dup", 0, 11, &err));
+ g_assert_nonnull(err);
+ g_assert_nonnull(strstr(error_get_pretty(err), "already registered"));
+ error_free(err);
+
+ /* and the first registration must be left intact */
+ g_assert_cmpint(cpr_find_fd("dup", 0), ==, 10);
+
+ /* A different id or name is a different key */
+ g_assert_true(cpr_save_fd("dup", 1, 12, &error_abort));
+ g_assert_true(cpr_save_fd("other", 0, 13, &error_abort));
+ g_assert_cmpint(cpr_find_fd("dup", 1), ==, 12);
+ g_assert_cmpint(cpr_find_fd("other", 0), ==, 13);
+
+ /* Once deleted, the key can be registered again */
+ cpr_delete_fd("dup", 0);
+ g_assert_cmpint(cpr_find_fd("dup", 0), ==, -1);
+ g_assert_true(cpr_save_fd("dup", 0, 14, &error_abort));
+ g_assert_cmpint(cpr_find_fd("dup", 0), ==, 14);
+
+ cpr_delete_fd("dup", 0);
+ cpr_delete_fd("dup", 1);
+ cpr_delete_fd("other", 0);
+}
+
+int main(int argc, char **argv)
+{
+ g_test_init(&argc, &argv, NULL);
+ g_test_add_func("/cpr/fd/duplicate", test_cpr_fd_duplicate);
+ return g_test_run();
+}
diff --git a/tests/unit/meson.build b/tests/unit/meson.build
index d17435c187..5d2b413fca 100644
--- a/tests/unit/meson.build
+++ b/tests/unit/meson.build
@@ -140,6 +140,7 @@ if have_system
'test-bufferiszero': [],
'test-smp-parse': [qom, meson.project_source_root() / 'hw/core/machine-smp.c'],
'test-vmstate': [migration, io],
+ 'test-cpr': [migration, io, meson.project_source_root() / 'migration/cpr.c'],
'test-yank': ['socket-helpers.c', qom, io, chardev],
'test-ram-discard-manager': [
'test-ram-discard-manager.c',
--
MST
^ permalink raw reply related [flat|nested] 84+ messages in thread
* [PULL 12/78] migration: forbid snapshots in CPR migration modes
2026-10-28 17:13 [PULL 00/78] pci, vhost, virtio, iommu, acpi: features, fixes Michael S. Tsirkin
` (10 preceding siblings ...)
2026-10-28 17:14 ` [PULL 11/78] tests/unit: add test for duplicate keys in CPR FD table Michael S. Tsirkin
@ 2026-10-28 17:14 ` Michael S. Tsirkin
2026-10-28 17:14 ` [PULL 13/78] migration: forbid snapshots under Xen Michael S. Tsirkin
` (66 subsequent siblings)
78 siblings, 0 replies; 84+ messages in thread
From: Michael S. Tsirkin @ 2026-10-28 17:14 UTC (permalink / raw)
To: qemu-devel
Cc: Peter Maydell, Andrey Drobyshev, Peter Xu, Fabiano Rosas,
Akihiko Odaki
From: Andrey Drobyshev <andrey.drobyshev@virtuozzo.com>
The savevm/loadvm operation doesn't make any sense in any of the CPR
modes. Saving VM state implies saving its RAM entirely, while the whole
point of CPR is avoiding that: in cpr-transfer and cpr-exec modes RAM is
preserved in place and never put into the migration stream (see
migrate_ram_is_ignored() in migration/ram.c). As a result, a snapshot
taken with such a mode set has no RAM in it, and loading a snapshot with
such a mode set fails in the RAM loader.
Currently nothing stops these operations from firing with a CPR mode
still set, e.g. after an earlier CPR attempt. Reject them early in
migrate_can_snapshot(), which guards 'savevm'/'loadvm' HMP commands as
well as 'snapshot-save'/'snapshot-load' QMP commands.
Reviewed-by: Peter Xu <peterx@redhat.com>
Reviewed-by: Fabiano Rosas <farosas@suse.de>
Reviewed-by: Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp>
Signed-off-by: Andrey Drobyshev <andrey.drobyshev@virtuozzo.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Message-ID: <20261012-up-vsock-vhost-v5-v16-3-36c38bd15ffc@virtuozzo.com>
---
migration/options.c | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/migration/options.c b/migration/options.c
index 46d040fc3f..92eda90114 100644
--- a/migration/options.c
+++ b/migration/options.c
@@ -560,8 +560,15 @@ static bool migrate_incoming_started(void)
bool migrate_can_snapshot(Error **errp)
{
MigrationState *s = migrate_get_current();
+ MigMode mode = migrate_mode();
int i;
+ if (mode != MIG_MODE_NORMAL) {
+ error_setg(errp, "Snapshots are not compatible with migration mode %s",
+ MigMode_str(mode));
+ return false;
+ }
+
for (i = 0; i < check_caps_savevm.size; i++) {
int incomp_cap = check_caps_savevm.caps[i];
--
MST
^ permalink raw reply related [flat|nested] 84+ messages in thread
* [PULL 13/78] migration: forbid snapshots under Xen
2026-10-28 17:13 [PULL 00/78] pci, vhost, virtio, iommu, acpi: features, fixes Michael S. Tsirkin
` (11 preceding siblings ...)
2026-10-28 17:14 ` [PULL 12/78] migration: forbid snapshots in CPR migration modes Michael S. Tsirkin
@ 2026-10-28 17:14 ` Michael S. Tsirkin
2026-10-28 17:14 ` [PULL 14/78] tests/qtest/migration: add test for snapshot rejection in CPR modes Michael S. Tsirkin
` (65 subsequent siblings)
78 siblings, 0 replies; 84+ messages in thread
From: Michael S. Tsirkin @ 2026-10-28 17:14 UTC (permalink / raw)
To: qemu-devel
Cc: Peter Maydell, Andrey Drobyshev, Akihiko Odaki, Peter Xu,
Fabiano Rosas
From: Andrey Drobyshev <andrey.drobyshev@virtuozzo.com>
The snapshot RAM-saving path does not support Xen's memory mappings.
Refuse 'savevm' and 'loadvm' when running as a Xen device model, the
same way they are refused for CPR migration modes.
Suggested-by: Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp>
Acked-by: Peter Xu <peterx@redhat.com>
Reviewed-by: Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp>
Signed-off-by: Andrey Drobyshev <andrey.drobyshev@virtuozzo.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Message-ID: <20261012-up-vsock-vhost-v5-v16-4-36c38bd15ffc@virtuozzo.com>
---
migration/options.c | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/migration/options.c b/migration/options.c
index 92eda90114..0b7f393492 100644
--- a/migration/options.c
+++ b/migration/options.c
@@ -37,6 +37,7 @@
#include "ram.h"
#include "options.h"
#include "system/kvm.h"
+#include "system/xen.h"
/* Maximum migrate downtime set to 2000 seconds */
#define MAX_MIGRATE_DOWNTIME_SECONDS 2000
@@ -563,6 +564,11 @@ bool migrate_can_snapshot(Error **errp)
MigMode mode = migrate_mode();
int i;
+ if (xen_enabled()) {
+ error_setg(errp, "Snapshots are not compatible with Xen");
+ return false;
+ }
+
if (mode != MIG_MODE_NORMAL) {
error_setg(errp, "Snapshots are not compatible with migration mode %s",
MigMode_str(mode));
--
MST
^ permalink raw reply related [flat|nested] 84+ messages in thread
* [PULL 14/78] tests/qtest/migration: add test for snapshot rejection in CPR modes
2026-10-28 17:13 [PULL 00/78] pci, vhost, virtio, iommu, acpi: features, fixes Michael S. Tsirkin
` (12 preceding siblings ...)
2026-10-28 17:14 ` [PULL 13/78] migration: forbid snapshots under Xen Michael S. Tsirkin
@ 2026-10-28 17:14 ` Michael S. Tsirkin
2026-10-28 17:14 ` [PULL 15/78] vhost-vsock: block CPR migration modes Michael S. Tsirkin
` (64 subsequent siblings)
78 siblings, 0 replies; 84+ messages in thread
From: Michael S. Tsirkin @ 2026-10-28 17:14 UTC (permalink / raw)
To: qemu-devel
Cc: Peter Maydell, Andrey Drobyshev, Peter Xu, Akihiko Odaki,
Maciej S. Szmigiero, Fabiano Rosas, Mark Kanda, Ben Chaney,
Laurent Vivier, Paolo Bonzini
From: Andrey Drobyshev <andrey.drobyshev@virtuozzo.com>
Snapshots are refused whenever a CPR migration mode is set. Add a test
which sets each of the CPR modes in turn and checks that 'savevm' and
'loadvm' fail with the corresponding error, then switches back to
'normal' mode and checks that the error is gone.
Reviewed-by: Peter Xu <peterx@redhat.com>
Signed-off-by: Andrey Drobyshev <andrey.drobyshev@virtuozzo.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Reviewed-by: Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Message-ID: <20261012-up-vsock-vhost-v5-v16-5-36c38bd15ffc@virtuozzo.com>
---
tests/qtest/migration/cpr-tests.c | 40 +++++++++++++++++++++++++++++++
1 file changed, 40 insertions(+)
diff --git a/tests/qtest/migration/cpr-tests.c b/tests/qtest/migration/cpr-tests.c
index 0bec753b4a..05c2ac1336 100644
--- a/tests/qtest/migration/cpr-tests.c
+++ b/tests/qtest/migration/cpr-tests.c
@@ -290,6 +290,44 @@ static void test_mode_exec(char *name, MigrateCommon *args)
test_cpr_exec(args);
}
+/* Snapshots must be refused while a CPR migration mode is set */
+static void test_mode_no_snapshot(char *name, MigrateCommon *args)
+{
+ static const char *const modes[] = {
+ "cpr-reboot", "cpr-transfer", "cpr-exec",
+ };
+ QTestState *vm = qtest_init("-nodefaults -machine none");
+ char *out;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(modes); i++) {
+ g_autofree char *expected = g_strdup_printf(
+ "Snapshots are not compatible with migration mode %s", modes[i]);
+
+ migrate_set_parameter_str(vm, "mode", modes[i]);
+
+ out = qtest_hmp(vm, "savevm snap");
+ g_assert_nonnull(strstr(out, expected));
+ g_free(out);
+
+ out = qtest_hmp(vm, "loadvm snap");
+ g_assert_nonnull(strstr(out, expected));
+ g_free(out);
+ }
+
+ /*
+ * The check should no longer fire in normal mode. savevm still fails
+ * (there's no block device for storing the snapshot). We just check
+ * that it's not the mode error any more.
+ */
+ migrate_set_parameter_str(vm, "mode", "normal");
+ out = qtest_hmp(vm, "savevm snap");
+ g_assert_null(strstr(out, "not compatible with migration mode"));
+ g_free(out);
+
+ qtest_quit(vm);
+}
+
void migration_test_add_cpr(MigrationTestEnv *env)
{
tmpfs = env->tmpfs;
@@ -300,6 +338,8 @@ void migration_test_add_cpr(MigrationTestEnv *env)
return;
}
+ migration_test_add("/migration/mode/no-snapshot", test_mode_no_snapshot);
+
/*
* Our CI system has problems with shared memory.
* Don't run this test until we find a workaround.
--
MST
^ permalink raw reply related [flat|nested] 84+ messages in thread
* [PULL 15/78] vhost-vsock: block CPR migration modes
2026-10-28 17:13 [PULL 00/78] pci, vhost, virtio, iommu, acpi: features, fixes Michael S. Tsirkin
` (13 preceding siblings ...)
2026-10-28 17:14 ` [PULL 14/78] tests/qtest/migration: add test for snapshot rejection in CPR modes Michael S. Tsirkin
@ 2026-10-28 17:14 ` Michael S. Tsirkin
2026-10-28 17:14 ` [PULL 16/78] vhost: add vhost_reset_owner op Michael S. Tsirkin
` (63 subsequent siblings)
78 siblings, 0 replies; 84+ messages in thread
From: Michael S. Tsirkin @ 2026-10-28 17:14 UTC (permalink / raw)
To: qemu-devel
Cc: Peter Maydell, Andrey Drobyshev, Stefano Garzarella,
Akihiko Odaki
From: Andrey Drobyshev <andrey.drobyshev@virtuozzo.com>
CPR migration with a vhost-vsock device currently crashes the target
QEMU. The device ownership is not handed over, so while the source
QEMU still holds the guest CID hashed in the kernel, the target's freshly
opened vhost-vsock device fails to set the same CID in realize():
qemu-system-x86_64: -device vhost-vsock-pci,id=vsock0,guest-cid=3:
vhost-vsock: unable to set guest cid: Address already in use
Add an unconditional migration blocker for the CPR modes, so that such
a migration fails early and gracefully instead. The following patches
implement the actual vhost-vsock CPR support, and the blocker is lifted
once it's fully working.
Signed-off-by: Andrey Drobyshev <andrey.drobyshev@virtuozzo.com>
Acked-by: Stefano Garzarella <sgarzare@redhat.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Reviewed-by: Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Message-ID: <20261012-up-vsock-vhost-v5-v16-6-36c38bd15ffc@virtuozzo.com>
---
include/hw/virtio/vhost-vsock.h | 1 +
hw/virtio/vhost-vsock.c | 27 +++++++++++++++++++++++----
2 files changed, 24 insertions(+), 4 deletions(-)
diff --git a/include/hw/virtio/vhost-vsock.h b/include/hw/virtio/vhost-vsock.h
index 84f4e727c7..a964d57e1b 100644
--- a/include/hw/virtio/vhost-vsock.h
+++ b/include/hw/virtio/vhost-vsock.h
@@ -29,6 +29,7 @@ struct VHostVSock {
/*< private >*/
VHostVSockCommon parent;
VHostVSockConf conf;
+ Error *migration_blocker; /* CPR migration is not supported */
/*< public >*/
};
diff --git a/hw/virtio/vhost-vsock.c b/hw/virtio/vhost-vsock.c
index da244eb165..503d248a61 100644
--- a/hw/virtio/vhost-vsock.c
+++ b/hw/virtio/vhost-vsock.c
@@ -20,6 +20,8 @@
#include "hw/core/qdev-properties.h"
#include "hw/virtio/vhost-vsock.h"
#include "monitor/monitor.h"
+#include "migration/blocker.h"
+#include "migration/misc.h"
static void vhost_vsock_get_config(VirtIODevice *vdev, uint8_t *config)
{
@@ -140,25 +142,38 @@ static void vhost_vsock_device_realize(DeviceState *dev, Error **errp)
return;
}
+ /*
+ * 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.
+ */
+ 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 (vsock->conf.vhostfd) {
vhostfd = monitor_fd_param(monitor_cur(), vsock->conf.vhostfd, errp);
if (vhostfd == -1) {
error_prepend(errp, "vhost-vsock: unable to parse vhostfd: ");
- return;
+ goto err_blocker;
}
if (!qemu_set_blocking(vhostfd, false, errp)) {
- return;
+ goto err_blocker;
}
} else {
vhostfd = open("/dev/vhost-vsock", O_RDWR);
if (vhostfd < 0) {
error_setg_file_open(errp, errno, "/dev/vhost-vsock");
- return;
+ goto err_blocker;
}
if (!qemu_set_blocking(vhostfd, false, errp)) {
- return;
+ goto err_blocker;
}
}
@@ -187,16 +202,20 @@ err_vhost_dev:
vhost_dev_cleanup(&vvc->vhost_dev);
err_virtio:
vhost_vsock_common_unrealize(vdev);
+err_blocker:
+ migrate_del_blocker(&vsock->migration_blocker);
}
static void vhost_vsock_device_unrealize(DeviceState *dev)
{
VHostVSockCommon *vvc = VHOST_VSOCK_COMMON(dev);
VirtIODevice *vdev = VIRTIO_DEVICE(dev);
+ VHostVSock *vsock = VHOST_VSOCK(dev);
/* This will stop vhost backend if appropriate. */
vhost_vsock_set_status(vdev, 0);
+ migrate_del_blocker(&vsock->migration_blocker);
vhost_dev_cleanup(&vvc->vhost_dev);
vhost_vsock_common_unrealize(vdev);
}
--
MST
^ permalink raw reply related [flat|nested] 84+ messages in thread
* [PULL 16/78] vhost: add vhost_reset_owner op
2026-10-28 17:13 [PULL 00/78] pci, vhost, virtio, iommu, acpi: features, fixes Michael S. Tsirkin
` (14 preceding siblings ...)
2026-10-28 17:14 ` [PULL 15/78] vhost-vsock: block CPR migration modes Michael S. Tsirkin
@ 2026-10-28 17:14 ` Michael S. Tsirkin
2026-10-28 17:14 ` [PULL 17/78] vhost-vsock: don't reset connections during CPR Michael S. Tsirkin
` (62 subsequent siblings)
78 siblings, 0 replies; 84+ messages in thread
From: Michael S. Tsirkin @ 2026-10-28 17:14 UTC (permalink / raw)
To: qemu-devel
Cc: Peter Maydell, Andrey Drobyshev, Mark Kanda, Steve Sistare,
Ben Chaney, Stefano Garzarella, Akihiko Odaki
From: Andrey Drobyshev <andrey.drobyshev@virtuozzo.com>
Add a VhostOps callback issuing VHOST_RESET_OWNER, wired up for the kernel
backend. The CPR (checkpoint-restore) path uses it to release device
ownership on the source so the destination can reclaim it later with
VHOST_SET_OWNER.
Originally-by: Mark Kanda <mark.kanda@oracle.com>
Originally-by: Steve Sistare <steven.sistare@oracle.com>
Originally-by: Ben Chaney <bchaney@akamai.com>
Reviewed-by: Stefano Garzarella <sgarzare@redhat.com>
Signed-off-by: Andrey Drobyshev <andrey.drobyshev@virtuozzo.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Reviewed-by: Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Message-ID: <20261012-up-vsock-vhost-v5-v16-7-36c38bd15ffc@virtuozzo.com>
---
include/hw/virtio/vhost-backend.h | 2 ++
hw/virtio/vhost-kernel.c | 6 ++++++
2 files changed, 8 insertions(+)
diff --git a/include/hw/virtio/vhost-backend.h b/include/hw/virtio/vhost-backend.h
index daa979a1aa..fb580aa417 100644
--- a/include/hw/virtio/vhost-backend.h
+++ b/include/hw/virtio/vhost-backend.h
@@ -104,6 +104,7 @@ typedef int (*vhost_get_features_op)(struct vhost_dev *dev,
uint64_t *features);
typedef int (*vhost_set_backend_cap_op)(struct vhost_dev *dev);
typedef int (*vhost_set_owner_op)(struct vhost_dev *dev);
+typedef int (*vhost_reset_owner_op)(struct vhost_dev *dev);
typedef int (*vhost_reset_device_op)(struct vhost_dev *dev);
typedef int (*vhost_get_vq_index_op)(struct vhost_dev *dev, int idx);
typedef int (*vhost_set_vring_enable_op)(struct vhost_dev *dev,
@@ -205,6 +206,7 @@ typedef struct VhostOps {
vhost_get_features_op vhost_get_features;
vhost_set_backend_cap_op vhost_set_backend_cap;
vhost_set_owner_op vhost_set_owner;
+ vhost_reset_owner_op vhost_reset_owner;
vhost_reset_device_op vhost_reset_device;
vhost_get_vq_index_op vhost_get_vq_index;
vhost_set_vring_enable_op vhost_set_vring_enable;
diff --git a/hw/virtio/vhost-kernel.c b/hw/virtio/vhost-kernel.c
index 3390b48c6f..55d316b88e 100644
--- a/hw/virtio/vhost-kernel.c
+++ b/hw/virtio/vhost-kernel.c
@@ -261,6 +261,11 @@ static int vhost_kernel_set_owner(struct vhost_dev *dev)
return vhost_kernel_call(dev, VHOST_SET_OWNER, NULL);
}
+static int vhost_kernel_reset_owner(struct vhost_dev *dev)
+{
+ return vhost_kernel_call(dev, VHOST_RESET_OWNER, NULL);
+}
+
static int vhost_kernel_get_vq_index(struct vhost_dev *dev, int idx)
{
assert(idx >= dev->vq_index && idx < dev->vq_index + dev->nvqs);
@@ -385,6 +390,7 @@ const VhostOps kernel_ops = {
.vhost_get_features_ex = vhost_kernel_get_features,
.vhost_set_backend_cap = vhost_kernel_set_backend_cap,
.vhost_set_owner = vhost_kernel_set_owner,
+ .vhost_reset_owner = vhost_kernel_reset_owner,
.vhost_get_vq_index = vhost_kernel_get_vq_index,
.vhost_vsock_set_guest_cid = vhost_kernel_vsock_set_guest_cid,
.vhost_vsock_set_running = vhost_kernel_vsock_set_running,
--
MST
^ permalink raw reply related [flat|nested] 84+ messages in thread
* [PULL 17/78] vhost-vsock: don't reset connections during CPR
2026-10-28 17:13 [PULL 00/78] pci, vhost, virtio, iommu, acpi: features, fixes Michael S. Tsirkin
` (15 preceding siblings ...)
2026-10-28 17:14 ` [PULL 16/78] vhost: add vhost_reset_owner op Michael S. Tsirkin
@ 2026-10-28 17:14 ` Michael S. Tsirkin
2026-10-28 17:14 ` [PULL 18/78] vhost-vsock: fix FD leak in realize() Michael S. Tsirkin
` (61 subsequent siblings)
78 siblings, 0 replies; 84+ messages in thread
From: Michael S. Tsirkin @ 2026-10-28 17:14 UTC (permalink / raw)
To: qemu-devel
Cc: Peter Maydell, Andrey Drobyshev, Stefano Garzarella,
Akihiko Odaki
From: Andrey Drobyshev <andrey.drobyshev@virtuozzo.com>
Migration target with a vhost-vsock device in the .post_load() hook issues
VIRTIO_VSOCK_EVENT_TRANSPORT_RESET event to the guest. This results into
the guest tearing down all its vsock connections. That reset exists
because after a normal migration the cid may change.
However it's not true for CPR-style migration. In this case cid remains
the same, and we want the connections to persist. Thus, let's customize
the .post_load() hook to skip the common transport reset part in this case.
Acked-by: Stefano Garzarella <sgarzare@redhat.com>
Signed-off-by: Andrey Drobyshev <andrey.drobyshev@virtuozzo.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Reviewed-by: Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Message-ID: <20261012-up-vsock-vhost-v5-v16-8-36c38bd15ffc@virtuozzo.com>
---
hw/virtio/vhost-vsock.c | 17 ++++++++++++++++-
1 file changed, 16 insertions(+), 1 deletion(-)
diff --git a/hw/virtio/vhost-vsock.c b/hw/virtio/vhost-vsock.c
index 503d248a61..c582ac9fcc 100644
--- a/hw/virtio/vhost-vsock.c
+++ b/hw/virtio/vhost-vsock.c
@@ -20,6 +20,7 @@
#include "hw/core/qdev-properties.h"
#include "hw/virtio/vhost-vsock.h"
#include "monitor/monitor.h"
+#include "migration/cpr.h"
#include "migration/blocker.h"
#include "migration/misc.h"
@@ -110,6 +111,20 @@ static uint64_t vhost_vsock_get_features(VirtIODevice *vdev,
return vhost_vsock_common_get_features(vdev, requested_features, errp);
}
+static int vhost_vsock_post_load(void *opaque, int version_id)
+{
+ /*
+ * Only reset vsock connections for non-CPR migration. For CPR the
+ * guest cid is unchanged, and the cid-change reset would otherwise
+ * tear the vsock connections down.
+ */
+ if (!cpr_is_incoming()) {
+ return vhost_vsock_common_post_load(opaque, version_id);
+ }
+
+ return 0;
+}
+
static const VMStateDescription vmstate_virtio_vhost_vsock = {
.name = "virtio-vhost_vsock",
.minimum_version_id = VHOST_VSOCK_SAVEVM_VERSION,
@@ -119,7 +134,7 @@ static const VMStateDescription vmstate_virtio_vhost_vsock = {
VMSTATE_END_OF_LIST()
},
.pre_save = vhost_vsock_common_pre_save,
- .post_load = vhost_vsock_common_post_load,
+ .post_load = vhost_vsock_post_load,
};
static void vhost_vsock_device_realize(DeviceState *dev, Error **errp)
--
MST
^ permalink raw reply related [flat|nested] 84+ messages in thread
* [PULL 18/78] vhost-vsock: fix FD leak in realize()
2026-10-28 17:13 [PULL 00/78] pci, vhost, virtio, iommu, acpi: features, fixes Michael S. Tsirkin
` (16 preceding siblings ...)
2026-10-28 17:14 ` [PULL 17/78] vhost-vsock: don't reset connections during CPR Michael S. Tsirkin
@ 2026-10-28 17:14 ` Michael S. Tsirkin
2026-10-28 17:14 ` [PULL 19/78] vhost-vsock: preserve vhost FD during CPR Michael S. Tsirkin
` (60 subsequent siblings)
78 siblings, 0 replies; 84+ messages in thread
From: Michael S. Tsirkin @ 2026-10-28 17:14 UTC (permalink / raw)
To: qemu-devel
Cc: Peter Maydell, Andrey Drobyshev, Stefano Garzarella,
Akihiko Odaki
From: Andrey Drobyshev <andrey.drobyshev@virtuozzo.com>
In vhost_vsock_device_realize(), after unsuccessful call to
qemu_set_blocking(vhostfd, false), vhostfd gets leaked. Let's close it
explicitly in this case. CPR-saved FDs don't get automatically closed,
thus it is safe both for vhostfd obtained from cpr_find_fd() and from
open(/dev/vhost-vsock).
Fixes: 384c2561bddf ("vhost-vsock: set vhostfd to non-blocking mode")
Fixes: 701544cfaf46 ("hw: replace qemu_set_nonblock()")
Reviewed-by: Stefano Garzarella <sgarzare@redhat.com>
Signed-off-by: Andrey Drobyshev <andrey.drobyshev@virtuozzo.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Reviewed-by: Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Message-ID: <20261012-up-vsock-vhost-v5-v16-9-36c38bd15ffc@virtuozzo.com>
---
hw/virtio/vhost-vsock.c | 11 ++++-------
1 file changed, 4 insertions(+), 7 deletions(-)
diff --git a/hw/virtio/vhost-vsock.c b/hw/virtio/vhost-vsock.c
index c582ac9fcc..3ec20d8760 100644
--- a/hw/virtio/vhost-vsock.c
+++ b/hw/virtio/vhost-vsock.c
@@ -176,20 +176,17 @@ static void vhost_vsock_device_realize(DeviceState *dev, Error **errp)
error_prepend(errp, "vhost-vsock: unable to parse vhostfd: ");
goto err_blocker;
}
-
- if (!qemu_set_blocking(vhostfd, false, errp)) {
- goto err_blocker;
- }
} else {
vhostfd = open("/dev/vhost-vsock", O_RDWR);
if (vhostfd < 0) {
error_setg_file_open(errp, errno, "/dev/vhost-vsock");
goto err_blocker;
}
+ }
- if (!qemu_set_blocking(vhostfd, false, errp)) {
- goto err_blocker;
- }
+ if (!qemu_set_blocking(vhostfd, false, errp)) {
+ close(vhostfd);
+ goto err_blocker;
}
vhost_vsock_common_realize(vdev);
--
MST
^ permalink raw reply related [flat|nested] 84+ messages in thread
* [PULL 19/78] vhost-vsock: preserve vhost FD during CPR
2026-10-28 17:13 [PULL 00/78] pci, vhost, virtio, iommu, acpi: features, fixes Michael S. Tsirkin
` (17 preceding siblings ...)
2026-10-28 17:14 ` [PULL 18/78] vhost-vsock: fix FD leak in realize() Michael S. Tsirkin
@ 2026-10-28 17:14 ` Michael S. Tsirkin
2026-10-28 17:14 ` [PULL 20/78] vhost: factor out vhost_dev_init_backend() Michael S. Tsirkin
` (59 subsequent siblings)
78 siblings, 0 replies; 84+ messages in thread
From: Michael S. Tsirkin @ 2026-10-28 17:14 UTC (permalink / raw)
To: qemu-devel
Cc: Peter Maydell, Andrey Drobyshev, Stefano Garzarella,
Akihiko Odaki
From: Andrey Drobyshev <andrey.drobyshev@virtuozzo.com>
During CPR (checkpoint-restore) migration the guest keeps running on the
same host, so instead of reopening /dev/vhost-vsock on the destination,
we should reuse the FD from the source. The FD is saved in the CPR
namespace (hash table) with cpr_save_fd() and then reclaimed on the
target via cpr_find_fd().
Use the (suffixed) device path as the key in CPR hash table. Device
path is obtained with qdev_get_dev_path() and is essentially the
device's address on the underlying bus. As any vhost-vsock device is
attached to a virtio bus, whose .get_dev_path() forwards to the proxy
device, and every proxy bus (PCI, MMIO, CCW) implements it, we should
always be getting a unique non-empty path with qdev_get_dev_path().
vhost_dev_init() (and thus VHOST_SET_OWNER) still runs in realize() here.
Deferring the ownership handoff to pre_save/post_load is done in a
following patch. Until then CPR remains blocked, so the restore path
added here is not reachable yet.
Reviewed-by: Stefano Garzarella <sgarzare@redhat.com>
Signed-off-by: Andrey Drobyshev <andrey.drobyshev@virtuozzo.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Reviewed-by: Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Message-ID: <20261012-up-vsock-vhost-v5-v16-10-36c38bd15ffc@virtuozzo.com>
---
hw/virtio/vhost-vsock.c | 37 ++++++++++++++++++++++++++++++++++++-
1 file changed, 36 insertions(+), 1 deletion(-)
diff --git a/hw/virtio/vhost-vsock.c b/hw/virtio/vhost-vsock.c
index 3ec20d8760..6e89007b2c 100644
--- a/hw/virtio/vhost-vsock.c
+++ b/hw/virtio/vhost-vsock.c
@@ -69,6 +69,18 @@ static int vhost_vsock_set_running(VirtIODevice *vdev, int start)
return 0;
}
+/*
+ * Any vhost-vsock device is attached to a virtio bus, whose .get_dev_path()
+ * forwards to the proxy device, and every proxy bus (PCI, MMIO, CCW)
+ * implements it. Thus qdev_get_dev_path() always returns a non-empty string,
+ * which guarantees us a unique CPR name.
+ */
+static char *vhost_vsock_cpr_name(DeviceState *dev)
+{
+ g_autofree char *path = qdev_get_dev_path(dev);
+ g_assert(path && path[0]);
+ return g_strdup_printf("%s/vhost-vsock", path);
+}
static int vhost_vsock_set_status(VirtIODevice *vdev, uint8_t status)
{
@@ -143,6 +155,8 @@ static void vhost_vsock_device_realize(DeviceState *dev, Error **errp)
VHostVSockCommon *vvc = VHOST_VSOCK_COMMON(dev);
VirtIODevice *vdev = VIRTIO_DEVICE(dev);
VHostVSock *vsock = VHOST_VSOCK(dev);
+ g_autofree char *cpr_name = vhost_vsock_cpr_name(dev);
+ bool cpr_incoming = cpr_is_incoming();
int vhostfd;
int ret;
@@ -170,7 +184,14 @@ static void vhost_vsock_device_realize(DeviceState *dev, Error **errp)
return;
}
- if (vsock->conf.vhostfd) {
+ if (cpr_incoming) {
+ /* Reuse the fd handed over from the source QEMU */
+ vhostfd = cpr_find_fd(cpr_name, 0);
+ if (vhostfd < 0) {
+ error_setg(errp, "vhost-vsock: could not find restored vhost FD");
+ goto err_blocker;
+ }
+ } else if (vsock->conf.vhostfd) {
vhostfd = monitor_fd_param(monitor_cur(), vsock->conf.vhostfd, errp);
if (vhostfd == -1) {
error_prepend(errp, "vhost-vsock: unable to parse vhostfd: ");
@@ -186,6 +207,9 @@ static void vhost_vsock_device_realize(DeviceState *dev, Error **errp)
if (!qemu_set_blocking(vhostfd, false, errp)) {
close(vhostfd);
+ if (cpr_incoming) {
+ cpr_delete_fd(cpr_name, 0);
+ }
goto err_blocker;
}
@@ -207,11 +231,20 @@ static void vhost_vsock_device_realize(DeviceState *dev, Error **errp)
goto err_vhost_dev;
}
+ /* Register the fd for a future CPR after a fully successful realize */
+ if (!cpr_incoming && !cpr_save_fd(cpr_name, 0, vhostfd, errp)) {
+ goto err_vhost_dev;
+ }
+
return;
err_vhost_dev:
/* vhost_dev_cleanup() closes the vhostfd passed to vhost_dev_init() */
vhost_dev_cleanup(&vvc->vhost_dev);
+ if (cpr_incoming) {
+ /* The fd came from cpr_find_fd() and is closed now, drop the entry */
+ cpr_delete_fd(cpr_name, 0);
+ }
err_virtio:
vhost_vsock_common_unrealize(vdev);
err_blocker:
@@ -223,10 +256,12 @@ static void vhost_vsock_device_unrealize(DeviceState *dev)
VHostVSockCommon *vvc = VHOST_VSOCK_COMMON(dev);
VirtIODevice *vdev = VIRTIO_DEVICE(dev);
VHostVSock *vsock = VHOST_VSOCK(dev);
+ g_autofree char *cpr_name = vhost_vsock_cpr_name(dev);
/* This will stop vhost backend if appropriate. */
vhost_vsock_set_status(vdev, 0);
+ cpr_delete_fd(cpr_name, 0);
migrate_del_blocker(&vsock->migration_blocker);
vhost_dev_cleanup(&vvc->vhost_dev);
vhost_vsock_common_unrealize(vdev);
--
MST
^ permalink raw reply related [flat|nested] 84+ messages in thread
* [PULL 20/78] vhost: factor out vhost_dev_init_backend()
2026-10-28 17:13 [PULL 00/78] pci, vhost, virtio, iommu, acpi: features, fixes Michael S. Tsirkin
` (18 preceding siblings ...)
2026-10-28 17:14 ` [PULL 19/78] vhost-vsock: preserve vhost FD during CPR Michael S. Tsirkin
@ 2026-10-28 17:14 ` Michael S. Tsirkin
2026-10-28 17:14 ` [PULL 21/78] vhost: make vhost_dev_cleanup() safe on a partially initialized device Michael S. Tsirkin
` (58 subsequent siblings)
78 siblings, 0 replies; 84+ messages in thread
From: Michael S. Tsirkin @ 2026-10-28 17:14 UTC (permalink / raw)
To: qemu-devel
Cc: Peter Maydell, Andrey Drobyshev, Stefano Garzarella,
Akihiko Odaki
From: Andrey Drobyshev <andrey.drobyshev@virtuozzo.com>
Split the first part of vhost_dev_init(): selecting the backend, calling
its .vhost_init() and reading the supported features - into a new
vhost_dev_init_backend() helper, and call it from vhost_dev_init().
This is in preparation for CPR restore of vhost-vsock, which needs to learn
the backend's features at realize time to negotiate them when loading the
incoming virtio state, but also must defer taking ownership of the device
to post_load. vhost_dev_init_backend() does exactly the pre-ownership part.
As a result VHOST_SET_OWNER now follows the feature query rather than
precedes it. This should be safe, as no backend requires ownership before
VHOST_GET_FEATURES - the kernel and vdpa backends do not check ownership
for it, and vhost-user already does query features from its .vhost_init()
before set_owner().
Reviewed-by: Stefano Garzarella <sgarzare@redhat.com>
Signed-off-by: Andrey Drobyshev <andrey.drobyshev@virtuozzo.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Reviewed-by: Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Message-ID: <20261012-up-vsock-vhost-v5-v16-11-36c38bd15ffc@virtuozzo.com>
---
include/hw/virtio/vhost.h | 22 ++++++++++++++++++++++
hw/virtio/vhost.c | 33 +++++++++++++++++++++++----------
2 files changed, 45 insertions(+), 10 deletions(-)
diff --git a/include/hw/virtio/vhost.h b/include/hw/virtio/vhost.h
index 0a322efec1..b2b644c0d1 100644
--- a/include/hw/virtio/vhost.h
+++ b/include/hw/virtio/vhost.h
@@ -156,6 +156,28 @@ int vhost_dev_init(struct vhost_dev *hdev, void *opaque,
VhostBackendType backend_type,
uint32_t busyloop_timeout, Error **errp);
+/**
+ * vhost_dev_init_backend() - set up the backend and query its features
+ * @hdev: the common vhost_dev structure
+ * @opaque: opaque ptr passed to backend (vhost/vhost-user/vdpa)
+ * @backend_type: type of backend
+ * @errp: error handle
+ *
+ * Select the backend, initialise the backend instance and read its supported
+ * features into @hdev, without issuing VHOST_SET_OWNER, setting up the
+ * virtqueues or registering the memory listener. This is the part of
+ * vhost_dev_init() that precedes taking ownership; it can be used on its own
+ * so feature negotiation can happen before ownership is acquired (e.g. by CPR
+ * restore).
+ *
+ * On failure the backend may be left partially initialised; the caller must
+ * still call vhost_dev_cleanup() to release it.
+ *
+ * Return: 0 on success, non-zero on error while setting errp.
+ */
+int vhost_dev_init_backend(struct vhost_dev *hdev, void *opaque,
+ VhostBackendType backend_type, Error **errp);
+
/**
* vhost_dev_cleanup() - tear down and cleanup vhost interface
* @hdev: the common vhost_dev structure
diff --git a/hw/virtio/vhost.c b/hw/virtio/vhost.c
index 68a8e40481..99561ac586 100644
--- a/hw/virtio/vhost.c
+++ b/hw/virtio/vhost.c
@@ -1681,6 +1681,28 @@ static int vhost_dev_init_features(struct vhost_dev *hdev)
return r;
}
+int vhost_dev_init_backend(struct vhost_dev *hdev, void *opaque,
+ VhostBackendType backend_type, Error **errp)
+{
+ int r;
+
+ r = vhost_set_backend_type(hdev, backend_type);
+ assert(r >= 0);
+
+ r = hdev->vhost_ops->vhost_init(hdev, opaque, errp);
+ if (r < 0) {
+ return r;
+ }
+
+ r = vhost_dev_init_features(hdev);
+ if (r < 0) {
+ error_setg_errno(errp, -r, "vhost_init_features failed");
+ return r;
+ }
+
+ return 0;
+}
+
int vhost_dev_init(struct vhost_dev *hdev, void *opaque,
VhostBackendType backend_type, uint32_t busyloop_timeout,
Error **errp)
@@ -1693,10 +1715,7 @@ int vhost_dev_init(struct vhost_dev *hdev, void *opaque,
hdev->vdev = NULL;
hdev->migration_blocker = NULL;
- r = vhost_set_backend_type(hdev, backend_type);
- assert(r >= 0);
-
- r = hdev->vhost_ops->vhost_init(hdev, opaque, errp);
+ r = vhost_dev_init_backend(hdev, opaque, backend_type, errp);
if (r < 0) {
goto fail;
}
@@ -1707,12 +1726,6 @@ int vhost_dev_init(struct vhost_dev *hdev, void *opaque,
goto fail;
}
- r = vhost_dev_init_features(hdev);
- if (r < 0) {
- error_setg_errno(errp, -r, "vhost_init_features failed");
- goto fail;
- }
-
limit = hdev->vhost_ops->vhost_memslots_limit(hdev);
if (limit < MEMORY_DEVICES_SAFE_MAX_MEMSLOTS &&
memory_devices_memslot_auto_decision_active()) {
--
MST
^ permalink raw reply related [flat|nested] 84+ messages in thread
* [PULL 21/78] vhost: make vhost_dev_cleanup() safe on a partially initialized device
2026-10-28 17:13 [PULL 00/78] pci, vhost, virtio, iommu, acpi: features, fixes Michael S. Tsirkin
` (19 preceding siblings ...)
2026-10-28 17:14 ` [PULL 20/78] vhost: factor out vhost_dev_init_backend() Michael S. Tsirkin
@ 2026-10-28 17:14 ` Michael S. Tsirkin
2026-10-28 17:14 ` [PULL 22/78] vhost: add vhost_dev_set_owner() / vhost_dev_reset_owner() helpers Michael S. Tsirkin
` (57 subsequent siblings)
78 siblings, 0 replies; 84+ messages in thread
From: Michael S. Tsirkin @ 2026-10-28 17:14 UTC (permalink / raw)
To: qemu-devel
Cc: Peter Maydell, Andrey Drobyshev, Stefano Garzarella,
Akihiko Odaki
From: Andrey Drobyshev <andrey.drobyshev@virtuozzo.com>
A device which only went through vhost_dev_init_backend() has its VQs
not yet initialized, so vq->dev is NULL and vhost_virtqueue_cleanup()
would crash dereferencing it. Check vq->dev before use, so that
vhost_dev_cleanup() can be called to release a device whose full
vhost_dev_init() never ran or failed along the way.
As of now, there's no way for the cleanup path to be reached with
vq->dev uninitialized. However after a subsequent patch adding CPR
support for vhost-vsock this'll become a possibility, so let's prevent
such a crash beforehand.
Reviewed-by: Stefano Garzarella <sgarzare@redhat.com>
Signed-off-by: Andrey Drobyshev <andrey.drobyshev@virtuozzo.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Reviewed-by: Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Message-ID: <20261012-up-vsock-vhost-v5-v16-12-36c38bd15ffc@virtuozzo.com>
---
hw/virtio/vhost.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/hw/virtio/vhost.c b/hw/virtio/vhost.c
index 99561ac586..dfdfd49da3 100644
--- a/hw/virtio/vhost.c
+++ b/hw/virtio/vhost.c
@@ -1661,7 +1661,7 @@ fail_call:
static void vhost_virtqueue_cleanup(struct vhost_virtqueue *vq)
{
event_notifier_cleanup(&vq->masked_notifier);
- if (vq->dev->vhost_ops->vhost_set_vring_err) {
+ if (vq->dev && vq->dev->vhost_ops->vhost_set_vring_err) {
event_notifier_set_handler(&vq->error_notifier, NULL);
event_notifier_cleanup(&vq->error_notifier);
}
--
MST
^ permalink raw reply related [flat|nested] 84+ messages in thread
* [PULL 22/78] vhost: add vhost_dev_set_owner() / vhost_dev_reset_owner() helpers
2026-10-28 17:13 [PULL 00/78] pci, vhost, virtio, iommu, acpi: features, fixes Michael S. Tsirkin
` (20 preceding siblings ...)
2026-10-28 17:14 ` [PULL 21/78] vhost: make vhost_dev_cleanup() safe on a partially initialized device Michael S. Tsirkin
@ 2026-10-28 17:14 ` Michael S. Tsirkin
2026-10-28 17:14 ` [PULL 23/78] vhost: add vhost_dev_is_initialized() helper Michael S. Tsirkin
` (56 subsequent siblings)
78 siblings, 0 replies; 84+ messages in thread
From: Michael S. Tsirkin @ 2026-10-28 17:14 UTC (permalink / raw)
To: qemu-devel
Cc: Peter Maydell, Andrey Drobyshev, Stefano Garzarella,
Akihiko Odaki
From: Andrey Drobyshev <andrey.drobyshev@virtuozzo.com>
Wrap the set_owner/reset_owner backend ops in dev-level helpers, matching
other vhost_dev_* wrappers, so device code can take or release ownership
without reaching into vhost_ops directly. vhost_dev_init() now uses
vhost_dev_set_owner(). Both return -ENOSYS if the backend has no such op.
No functional change. These are used by the following vhost-vsock patch to
hand a device between owners during CPR.
Reviewed-by: Stefano Garzarella <sgarzare@redhat.com>
Signed-off-by: Andrey Drobyshev <andrey.drobyshev@virtuozzo.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Reviewed-by: Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Message-ID: <20261012-up-vsock-vhost-v5-v16-13-36c38bd15ffc@virtuozzo.com>
---
include/hw/virtio/vhost.h | 25 +++++++++++++++++++++++++
hw/virtio/vhost.c | 20 +++++++++++++++++++-
2 files changed, 44 insertions(+), 1 deletion(-)
diff --git a/include/hw/virtio/vhost.h b/include/hw/virtio/vhost.h
index b2b644c0d1..8127a9a5a2 100644
--- a/include/hw/virtio/vhost.h
+++ b/include/hw/virtio/vhost.h
@@ -178,6 +178,31 @@ int vhost_dev_init(struct vhost_dev *hdev, void *opaque,
int vhost_dev_init_backend(struct vhost_dev *hdev, void *opaque,
VhostBackendType backend_type, Error **errp);
+/**
+ * vhost_dev_set_owner() - take ownership of a vhost device
+ * @hdev: the common vhost_dev structure
+ *
+ * Issue VHOST_SET_OWNER on a device that has already been set up, e.g. to
+ * complete a CPR handoff on the destination, or to take the device back on
+ * the source after a failed CPR.
+ *
+ * Return: 0 on success, -ENOSYS if the backend has no such op, negative
+ * errno on failure.
+ */
+int vhost_dev_set_owner(struct vhost_dev *hdev);
+
+/**
+ * vhost_dev_reset_owner() - release ownership of a vhost device
+ * @hdev: the common vhost_dev structure
+ *
+ * Issue VHOST_RESET_OWNER so that another process can take the device over
+ * with vhost_dev_set_owner(), e.g. on the CPR source once the VM is stopped.
+ *
+ * Return: 0 on success, -ENOSYS if the backend has no such op, negative
+ * errno on failure.
+ */
+int vhost_dev_reset_owner(struct vhost_dev *hdev);
+
/**
* vhost_dev_cleanup() - tear down and cleanup vhost interface
* @hdev: the common vhost_dev structure
diff --git a/hw/virtio/vhost.c b/hw/virtio/vhost.c
index dfdfd49da3..56fa434588 100644
--- a/hw/virtio/vhost.c
+++ b/hw/virtio/vhost.c
@@ -1703,6 +1703,24 @@ int vhost_dev_init_backend(struct vhost_dev *hdev, void *opaque,
return 0;
}
+int vhost_dev_set_owner(struct vhost_dev *hdev)
+{
+ assert(hdev->vhost_ops);
+ if (!hdev->vhost_ops->vhost_set_owner) {
+ return -ENOSYS;
+ }
+ return hdev->vhost_ops->vhost_set_owner(hdev);
+}
+
+int vhost_dev_reset_owner(struct vhost_dev *hdev)
+{
+ assert(hdev->vhost_ops);
+ if (!hdev->vhost_ops->vhost_reset_owner) {
+ return -ENOSYS;
+ }
+ return hdev->vhost_ops->vhost_reset_owner(hdev);
+}
+
int vhost_dev_init(struct vhost_dev *hdev, void *opaque,
VhostBackendType backend_type, uint32_t busyloop_timeout,
Error **errp)
@@ -1720,7 +1738,7 @@ int vhost_dev_init(struct vhost_dev *hdev, void *opaque,
goto fail;
}
- r = hdev->vhost_ops->vhost_set_owner(hdev);
+ r = vhost_dev_set_owner(hdev);
if (r < 0) {
error_setg_errno(errp, -r, "vhost_set_owner failed");
goto fail;
--
MST
^ permalink raw reply related [flat|nested] 84+ messages in thread
* [PULL 23/78] vhost: add vhost_dev_is_initialized() helper
2026-10-28 17:13 [PULL 00/78] pci, vhost, virtio, iommu, acpi: features, fixes Michael S. Tsirkin
` (21 preceding siblings ...)
2026-10-28 17:14 ` [PULL 22/78] vhost: add vhost_dev_set_owner() / vhost_dev_reset_owner() helpers Michael S. Tsirkin
@ 2026-10-28 17:14 ` Michael S. Tsirkin
2026-10-28 17:15 ` [PULL 24/78] vhost: release ownership in vhost_dev_cleanup() Michael S. Tsirkin
` (55 subsequent siblings)
78 siblings, 0 replies; 84+ messages in thread
From: Michael S. Tsirkin @ 2026-10-28 17:14 UTC (permalink / raw)
To: qemu-devel
Cc: Peter Maydell, Andrey Drobyshev, Stefano Garzarella,
Akihiko Odaki
From: Andrey Drobyshev <andrey.drobyshev@virtuozzo.com>
The upcoming CPR support for vhost-vsock defers the full vhost_dev_init()
to .post_load(). On an incoming CPR .realize() only sets up the backend,
so we need a way to tell a fully initialized vhost device from a partially
initialized one.
Add an explicit 'bool initialized' field, set once vhost_dev_init()
succeeds, and cleared only in vhost_dev_cleanup() (by memset(0)). Also add
a public vhost_dev_is_initialized() helper.
Suggested-by: Stefano Garzarella <sgarzare@redhat.com>
Reviewed-by: Stefano Garzarella <sgarzare@redhat.com>
Signed-off-by: Andrey Drobyshev <andrey.drobyshev@virtuozzo.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Reviewed-by: Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Message-ID: <20261012-up-vsock-vhost-v5-v16-14-36c38bd15ffc@virtuozzo.com>
---
include/hw/virtio/vhost.h | 14 ++++++++++++++
hw/virtio/vhost.c | 2 ++
2 files changed, 16 insertions(+)
diff --git a/include/hw/virtio/vhost.h b/include/hw/virtio/vhost.h
index 8127a9a5a2..73ea130d7e 100644
--- a/include/hw/virtio/vhost.h
+++ b/include/hw/virtio/vhost.h
@@ -109,6 +109,8 @@ struct vhost_dev {
uint64_t backend_cap;
/* @started: is the vhost device started? */
bool started;
+ /* @initialized: has the full vhost_dev_init() completed? */
+ bool initialized;
bool log_enabled;
uint64_t log_size;
Error *migration_blocker;
@@ -248,6 +250,18 @@ static inline bool vhost_dev_is_started(struct vhost_dev *hdev)
return hdev->started;
}
+/**
+ * vhost_dev_is_initialized() - report init status of vhost device
+ * @hdev: common vhost_dev structure
+ *
+ * Return true if the full vhost_dev_init() has completed successfully
+ * (and vhost_dev_cleanup() has not run since).
+ */
+static inline bool vhost_dev_is_initialized(struct vhost_dev *hdev)
+{
+ return hdev->initialized;
+}
+
static inline int vhost_dev_set_vring_enable(struct vhost_dev *hdev, int enable)
{
if (!hdev->vhost_ops->vhost_set_vring_enable) {
diff --git a/hw/virtio/vhost.c b/hw/virtio/vhost.c
index 56fa434588..c01ccc800c 100644
--- a/hw/virtio/vhost.c
+++ b/hw/virtio/vhost.c
@@ -1834,6 +1834,8 @@ int vhost_dev_init(struct vhost_dev *hdev, void *opaque,
goto fail;
}
+ hdev->initialized = true;
+
trace_vhost_dev_init_out(hdev);
return 0;
--
MST
^ permalink raw reply related [flat|nested] 84+ messages in thread
* [PULL 24/78] vhost: release ownership in vhost_dev_cleanup()
2026-10-28 17:13 [PULL 00/78] pci, vhost, virtio, iommu, acpi: features, fixes Michael S. Tsirkin
` (22 preceding siblings ...)
2026-10-28 17:14 ` [PULL 23/78] vhost: add vhost_dev_is_initialized() helper Michael S. Tsirkin
@ 2026-10-28 17:15 ` Michael S. Tsirkin
2026-10-28 17:15 ` [PULL 25/78] migration: add MIG_EVENT_INCOMING_FAILED Michael S. Tsirkin
` (54 subsequent siblings)
78 siblings, 0 replies; 84+ messages in thread
From: Michael S. Tsirkin @ 2026-10-28 17:15 UTC (permalink / raw)
To: qemu-devel
Cc: Peter Maydell, Andrey Drobyshev, Akihiko Odaki,
Stefano Garzarella
From: Andrey Drobyshev <andrey.drobyshev@virtuozzo.com>
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() as the common teardown for all
those paths. Only do it if the ownership was handed over to us by
another process, which is marked by the device with a new bool flag
'owner_handed_over'. Upon ownership release failure, we have nothing to
unwind at this point (CPR target teardown), so we just report the failure.
Reported-by: Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp>
Suggested-by: Stefano Garzarella <sgarzare@redhat.com>
Signed-off-by: Andrey Drobyshev <andrey.drobyshev@virtuozzo.com>
Reviewed-by: Stefano Garzarella <sgarzare@redhat.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Reviewed-by: Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Message-ID: <20261012-up-vsock-vhost-v5-v16-15-36c38bd15ffc@virtuozzo.com>
---
include/hw/virtio/vhost.h | 11 +++++++++++
hw/virtio/vhost.c | 40 +++++++++++++++++++++++++++++++++++++--
2 files changed, 49 insertions(+), 2 deletions(-)
diff --git a/include/hw/virtio/vhost.h b/include/hw/virtio/vhost.h
index 73ea130d7e..efe1eed260 100644
--- a/include/hw/virtio/vhost.h
+++ b/include/hw/virtio/vhost.h
@@ -111,6 +111,17 @@ 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;
+ /*
+ * @owner_handed_over: CPR target side. The ownership was handed over
+ * to us by another process (the CPR source) and must be given back
+ * when this vhost_dev goes away. Set by the device.
+ */
+ bool owner_handed_over;
bool log_enabled;
uint64_t log_size;
Error *migration_blocker;
diff --git a/hw/virtio/vhost.c b/hw/virtio/vhost.c
index c01ccc800c..085e2ef6f1 100644
--- a/hw/virtio/vhost.c
+++ b/hw/virtio/vhost.c
@@ -53,6 +53,25 @@ static QLIST_HEAD(, vhost_dev) vhost_log_devs[VHOST_BACKEND_TYPE_MAX];
static QLIST_HEAD(, vhost_dev) vhost_devices =
QLIST_HEAD_INITIALIZER(vhost_devices);
+/*
+ * Release the device ownership if it was handed over to us by another
+ * process, e.g. the CPR source.
+ */
+static void vhost_dev_try_release_owner(struct vhost_dev *hdev,
+ const char *when)
+{
+ int r;
+
+ if (!hdev->owner || !hdev->owner_handed_over) {
+ return;
+ }
+ r = vhost_dev_reset_owner(hdev);
+ if (r < 0) {
+ error_report("vhost: cannot release device ownership on %s: %s",
+ when, strerror(-r));
+ }
+}
+
unsigned int vhost_get_max_memslots(void)
{
unsigned int max = UINT_MAX;
@@ -1705,20 +1724,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 +1895,11 @@ 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.
+ */
+ vhost_dev_try_release_owner(hdev, "cleanup");
hdev->vhost_ops->vhost_cleanup(hdev);
}
assert(!hdev->log);
--
MST
^ permalink raw reply related [flat|nested] 84+ messages in thread
* [PULL 25/78] migration: add MIG_EVENT_INCOMING_FAILED
2026-10-28 17:13 [PULL 00/78] pci, vhost, virtio, iommu, acpi: features, fixes Michael S. Tsirkin
` (23 preceding siblings ...)
2026-10-28 17:15 ` [PULL 24/78] vhost: release ownership in vhost_dev_cleanup() Michael S. Tsirkin
@ 2026-10-28 17:15 ` Michael S. Tsirkin
2026-10-28 17:15 ` [PULL 26/78] vhost: release ownership when the incoming migration fails Michael S. Tsirkin
` (53 subsequent siblings)
78 siblings, 0 replies; 84+ messages in thread
From: Michael S. Tsirkin @ 2026-10-28 17:15 UTC (permalink / raw)
To: qemu-devel
Cc: Peter Maydell, Andrey Drobyshev, Peter Xu, Akihiko Odaki,
Fabiano Rosas
From: Andrey Drobyshev <andrey.drobyshev@virtuozzo.com>
A device may take over a resource from the source in post_load(), e.g.
vhost-vsock acquires the device ownership on a CPR target. If the
incoming migration fails afterwards, there's currently no way for the
device to give that resource back before the failure is published, i.e.
before the source is told about it over the return path and resumes, or
the management sees the MIGRATION event on the target and resumes the
source, or the target exits.
Add a migration event for that, sent to the migration notifiers from
the failure path of process_incoming_migration_co() before the failure
is published.
Reviewed-by: Peter Xu <peterx@redhat.com>
Signed-off-by: Andrey Drobyshev <andrey.drobyshev@virtuozzo.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Reviewed-by: Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Message-ID: <20261012-up-vsock-vhost-v5-v16-16-36c38bd15ffc@virtuozzo.com>
---
include/migration/misc.h | 5 +++++
migration/migration.c | 10 ++++++++++
2 files changed, 15 insertions(+)
diff --git a/include/migration/misc.h b/include/migration/misc.h
index 019717f4cd..86cc7eefd6 100644
--- a/include/migration/misc.h
+++ b/include/migration/misc.h
@@ -65,12 +65,17 @@ bool migration_thread_is_self(void);
* - MIG_EVENT_SETUP [-> MIG_EVENT_POSTCOPY_START] -> MIG_EVENT_DONE
* - MIG_EVENT_SETUP [-> MIG_EVENT_POSTCOPY_START] -> MIG_EVENT_FAILED
* - MIG_EVENT_FAILED
+ *
+ * MIG_EVENT_INCOMING_FAILED is currently the only event sent on the
+ * incoming side, when the incoming migration fails and before the failure
+ * is published.
*/
typedef enum MigrationEventType {
MIG_EVENT_SETUP,
MIG_EVENT_POSTCOPY_START,
MIG_EVENT_DONE,
MIG_EVENT_FAILED,
+ MIG_EVENT_INCOMING_FAILED,
MIG_EVENT_MAX
} MigrationEventType;
diff --git a/migration/migration.c b/migration/migration.c
index fab7949fe1..82a8a25228 100644
--- a/migration/migration.c
+++ b/migration/migration.c
@@ -815,6 +815,16 @@ process_incoming_migration_co(void *opaque)
goto out;
fail:
+ /*
+ * Allow the devices which have registered their callbacks for
+ * migration target failure to undo the changes they may have
+ * done in post_load().
+ *
+ * Do that before the failure is published in any way: the MIGRATION
+ * event emitted by migrate_set_state() below, the return path (if
+ * present) in migration_incoming_state_destroy(), or the exit().
+ */
+ migration_call_notifiers(MIG_EVENT_INCOMING_FAILED, NULL);
migrate_set_state(&mis->state, MIGRATION_STATUS_ACTIVE,
MIGRATION_STATUS_FAILED);
migrate_error_propagate(s, local_err);
--
MST
^ permalink raw reply related [flat|nested] 84+ messages in thread
* [PULL 26/78] vhost: release ownership when the incoming migration fails
2026-10-28 17:13 [PULL 00/78] pci, vhost, virtio, iommu, acpi: features, fixes Michael S. Tsirkin
` (24 preceding siblings ...)
2026-10-28 17:15 ` [PULL 25/78] migration: add MIG_EVENT_INCOMING_FAILED Michael S. Tsirkin
@ 2026-10-28 17:15 ` Michael S. Tsirkin
2026-10-28 17:15 ` [PULL 27/78] vhost: release ownership on exit Michael S. Tsirkin
` (52 subsequent siblings)
78 siblings, 0 replies; 84+ messages in thread
From: Michael S. Tsirkin @ 2026-10-28 17:15 UTC (permalink / raw)
To: qemu-devel
Cc: Peter Maydell, Andrey Drobyshev, Akihiko Odaki,
Stefano Garzarella
From: Andrey Drobyshev <andrey.drobyshev@virtuozzo.com>
A CPR target that has taken the device ownership in post_load() doesn't
always reach vhost_dev_cleanup(): a subsequent load error, e.g. a
truncated migration stream footer, makes QEMU exit() right away, and the
ownership stays with the dead process. The source still holds the FD,
so the kernel never releases the device, and the source can't take it
back after being resumed -> SET_OWNER fails with EBUSY.
Releasing the ownership at exit isn't enough either: with the
return-path capability the target reports the failure to the source
before exiting, and the source resumes right away, racing with the
target's exit for the ownership. And with exit-on-error=false the
target doesn't exit at all.
Add a migration notifier to each vhost_dev, registered for its lifetime
for the CPR modes, that releases the ownership if still held on
MIG_EVENT_INCOMING_FAILED. It runs before the failure is published in
any way.
Reported-by: Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp>
Signed-off-by: Andrey Drobyshev <andrey.drobyshev@virtuozzo.com>
Acked-by: Stefano Garzarella <sgarzare@redhat.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Reviewed-by: Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Message-ID: <20261012-up-vsock-vhost-v5-v16-17-36c38bd15ffc@virtuozzo.com>
---
include/hw/virtio/vhost.h | 6 ++++++
hw/virtio/vhost.c | 23 +++++++++++++++++++++++
2 files changed, 29 insertions(+)
diff --git a/include/hw/virtio/vhost.h b/include/hw/virtio/vhost.h
index efe1eed260..3b9ef86bac 100644
--- a/include/hw/virtio/vhost.h
+++ b/include/hw/virtio/vhost.h
@@ -3,6 +3,7 @@
#include "net/vhost_net.h"
#include "hw/virtio/vhost-backend.h"
+#include "qemu/notify.h"
#include "hw/virtio/virtio.h"
#include "system/memory.h"
@@ -122,6 +123,11 @@ struct vhost_dev {
* when this vhost_dev goes away. Set by the device.
*/
bool owner_handed_over;
+ /*
+ * @migration_notifier: releases the ownership when an incoming
+ * migration fails, vhost_migration_notify()
+ */
+ NotifierWithReturn migration_notifier;
bool log_enabled;
uint64_t log_size;
Error *migration_blocker;
diff --git a/hw/virtio/vhost.c b/hw/virtio/vhost.c
index 085e2ef6f1..7fd2883504 100644
--- a/hw/virtio/vhost.c
+++ b/hw/virtio/vhost.c
@@ -26,6 +26,7 @@
#include "hw/virtio/vhost-user.h"
#include "hw/mem/memory-device.h"
#include "migration/blocker.h"
+#include "migration/misc.h"
#include "migration/qemu-file-types.h"
#include "system/dma.h"
#include "system/memory.h"
@@ -72,6 +73,23 @@ static void vhost_dev_try_release_owner(struct vhost_dev *hdev,
}
}
+/*
+ * A CPR target takes the device ownership in post_load(). If the load
+ * fails afterwards, release it before the source learns about the failure
+ * and resumes, so that the source can take the device back.
+ */
+static int vhost_migration_notify(NotifierWithReturn *notifier,
+ MigrationEvent *e, Error **errp)
+{
+ struct vhost_dev *hdev = container_of(notifier, struct vhost_dev,
+ migration_notifier);
+
+ if (e->type == MIG_EVENT_INCOMING_FAILED) {
+ vhost_dev_try_release_owner(hdev, "incoming migration failure");
+ }
+ return 0;
+}
+
unsigned int vhost_get_max_memslots(void)
{
unsigned int max = UINT_MAX;
@@ -1842,6 +1860,10 @@ int vhost_dev_init(struct vhost_dev *hdev, void *opaque,
hdev->log_enabled = false;
hdev->started = false;
memory_listener_register(&hdev->memory_listener, &address_space_memory);
+ migration_add_notifier_modes(&hdev->migration_notifier,
+ vhost_migration_notify,
+ BIT(MIG_MODE_CPR_TRANSFER) |
+ BIT(MIG_MODE_CPR_EXEC));
QLIST_INSERT_HEAD(&vhost_devices, hdev, entry);
/*
@@ -1891,6 +1913,7 @@ void vhost_dev_cleanup(struct vhost_dev *hdev)
memory_listener_unregister(&hdev->memory_listener);
QLIST_REMOVE(hdev, entry);
}
+ migration_remove_notifier(&hdev->migration_notifier);
migrate_del_blocker(&hdev->migration_blocker);
g_free(hdev->mem);
g_free(hdev->mem_sections);
--
MST
^ permalink raw reply related [flat|nested] 84+ messages in thread
* [PULL 27/78] vhost: release ownership on exit
2026-10-28 17:13 [PULL 00/78] pci, vhost, virtio, iommu, acpi: features, fixes Michael S. Tsirkin
` (25 preceding siblings ...)
2026-10-28 17:15 ` [PULL 26/78] vhost: release ownership when the incoming migration fails Michael S. Tsirkin
@ 2026-10-28 17:15 ` Michael S. Tsirkin
2026-10-28 17:15 ` [PULL 28/78] vhost-vsock: hand off device ownership across CPR Michael S. Tsirkin
` (51 subsequent siblings)
78 siblings, 0 replies; 84+ messages in thread
From: Michael S. Tsirkin @ 2026-10-28 17:15 UTC (permalink / raw)
To: qemu-devel
Cc: Peter Maydell, Andrey Drobyshev, Akihiko Odaki,
Stefano Garzarella
From: Andrey Drobyshev <andrey.drobyshev@virtuozzo.com>
A CPR target that has taken the device ownership in post_load() doesn't
always reach vhost_dev_cleanup(): a target which has loaded the state
but hasn't been started yet, e.g. one started with -S, may be quit
normally, with the source being resumed instead. Devices aren't
unrealized on quit, so the ownership stays with the dead process. The
source still holds the FD, so the kernel never releases the device, and
the source can't take it back after being resumed -> SET_OWNER fails
with EBUSY.
Add an exit notifier to each vhost_dev, registered for its lifetime,
that releases the ownership if still held. This covers every orderly
exit. A target killed with SIGKILL or crashing still leaves the device
unrecoverable.
Reported-by: Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp>
Signed-off-by: Andrey Drobyshev <andrey.drobyshev@virtuozzo.com>
Acked-by: Stefano Garzarella <sgarzare@redhat.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Reviewed-by: Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Message-ID: <20261012-up-vsock-vhost-v5-v16-18-36c38bd15ffc@virtuozzo.com>
---
include/hw/virtio/vhost.h | 2 ++
hw/virtio/vhost.c | 20 ++++++++++++++++++++
2 files changed, 22 insertions(+)
diff --git a/include/hw/virtio/vhost.h b/include/hw/virtio/vhost.h
index 3b9ef86bac..eb693e3289 100644
--- a/include/hw/virtio/vhost.h
+++ b/include/hw/virtio/vhost.h
@@ -123,6 +123,8 @@ struct vhost_dev {
* when this vhost_dev goes away. Set by the device.
*/
bool owner_handed_over;
+ /* @exit_notifier: releases the ownership at exit, vhost_exit_notify() */
+ Notifier exit_notifier;
/*
* @migration_notifier: releases the ownership when an incoming
* migration fails, vhost_migration_notify()
diff --git a/hw/virtio/vhost.c b/hw/virtio/vhost.c
index 7fd2883504..cbf6e82afb 100644
--- a/hw/virtio/vhost.c
+++ b/hw/virtio/vhost.c
@@ -31,6 +31,7 @@
#include "system/dma.h"
#include "system/memory.h"
#include "system/ramblock.h"
+#include "system/system.h"
#include "trace.h"
/* enabled until disconnected backend stabilizes */
@@ -90,6 +91,19 @@ static int vhost_migration_notify(NotifierWithReturn *notifier,
return 0;
}
+/*
+ * Release the ownership of the vhost devices still held at exit(), so that
+ * another process sharing the backend FDs (e.g. the CPR source) can take
+ * them over.
+ */
+static void vhost_exit_notify(Notifier *notifier, void *data)
+{
+ struct vhost_dev *hdev = container_of(notifier, struct vhost_dev,
+ exit_notifier);
+
+ vhost_dev_try_release_owner(hdev, "exit");
+}
+
unsigned int vhost_get_max_memslots(void)
{
unsigned int max = UINT_MAX;
@@ -1860,6 +1874,8 @@ int vhost_dev_init(struct vhost_dev *hdev, void *opaque,
hdev->log_enabled = false;
hdev->started = false;
memory_listener_register(&hdev->memory_listener, &address_space_memory);
+ hdev->exit_notifier.notify = vhost_exit_notify;
+ qemu_add_exit_notifier(&hdev->exit_notifier);
migration_add_notifier_modes(&hdev->migration_notifier,
vhost_migration_notify,
BIT(MIG_MODE_CPR_TRANSFER) |
@@ -1913,6 +1929,10 @@ void vhost_dev_cleanup(struct vhost_dev *hdev)
memory_listener_unregister(&hdev->memory_listener);
QLIST_REMOVE(hdev, entry);
}
+ if (hdev->exit_notifier.notify) {
+ qemu_remove_exit_notifier(&hdev->exit_notifier);
+ hdev->exit_notifier.notify = NULL;
+ }
migration_remove_notifier(&hdev->migration_notifier);
migrate_del_blocker(&hdev->migration_blocker);
g_free(hdev->mem);
--
MST
^ permalink raw reply related [flat|nested] 84+ messages in thread
* [PULL 28/78] vhost-vsock: hand off device ownership across CPR
2026-10-28 17:13 [PULL 00/78] pci, vhost, virtio, iommu, acpi: features, fixes Michael S. Tsirkin
` (26 preceding siblings ...)
2026-10-28 17:15 ` [PULL 27/78] vhost: release ownership on exit Michael S. Tsirkin
@ 2026-10-28 17:15 ` Michael S. Tsirkin
2026-10-28 17:15 ` [PULL 29/78] tests/qtest/migration: add vhost-vsock tests for cpr-transfer and cpr-exec Michael S. Tsirkin
` (50 subsequent siblings)
78 siblings, 0 replies; 84+ messages in thread
From: Michael S. Tsirkin @ 2026-10-28 17:15 UTC (permalink / raw)
To: qemu-devel
Cc: Peter Maydell, Andrey Drobyshev, Dongli Zhang, Stefano Garzarella,
Akihiko Odaki
From: Andrey Drobyshev <andrey.drobyshev@virtuozzo.com>
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 restart:
- .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. It also marks the ownership as
handed over ('owner_handed_over'), so that it's released if this
target goes away.
- .set_status() refuses to start a device whose init hasn't completed
yet (incoming CPR before post_load).
- .set_status() re-acquires ownership before starting the device if
pre_save released it and this QEMU is about to run the guest again:
the migration failed, or it completed and the management resumed the
source (e.g. cpr-transfer target died).
With the handoff in place, lift the CPR blocker.
Note: this requires kernel side support of VHOST_RESET_OWNER operation
for vhost-vsock. Otherwise CPR attempt fails in .pre_save().
Suggested-by: Dongli Zhang <dongli.zhang@oracle.com>
Signed-off-by: Andrey Drobyshev <andrey.drobyshev@virtuozzo.com>
Acked-by: Stefano Garzarella <sgarzare@redhat.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Reviewed-by: Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Message-ID: <20261012-up-vsock-vhost-v5-v16-19-36c38bd15ffc@virtuozzo.com>
---
include/hw/virtio/vhost-vsock.h | 1 -
hw/virtio/vhost-vsock.c | 157 ++++++++++++++++++++++++++------
2 files changed, 127 insertions(+), 31 deletions(-)
diff --git a/include/hw/virtio/vhost-vsock.h b/include/hw/virtio/vhost-vsock.h
index a964d57e1b..84f4e727c7 100644
--- a/include/hw/virtio/vhost-vsock.h
+++ b/include/hw/virtio/vhost-vsock.h
@@ -29,7 +29,6 @@ struct VHostVSock {
/*< private >*/
VHostVSockCommon parent;
VHostVSockConf conf;
- Error *migration_blocker; /* CPR migration is not supported */
/*< public >*/
};
diff --git a/hw/virtio/vhost-vsock.c b/hw/virtio/vhost-vsock.c
index 6e89007b2c..4ff58a9801 100644
--- a/hw/virtio/vhost-vsock.c
+++ b/hw/virtio/vhost-vsock.c
@@ -21,7 +21,6 @@
#include "hw/virtio/vhost-vsock.h"
#include "monitor/monitor.h"
#include "migration/cpr.h"
-#include "migration/blocker.h"
#include "migration/misc.h"
static void vhost_vsock_get_config(VirtIODevice *vdev, uint8_t *config)
@@ -88,11 +87,40 @@ static int vhost_vsock_set_status(VirtIODevice *vdev, uint8_t status)
bool should_start = virtio_device_should_start(vdev, status);
int ret;
+ /*
+ * During CPR, on target the full vhost_dev_init() is deferred to
+ * post_load. Refuse to start a half-initialised device rather than
+ * issue vhost ioctls on it.
+ */
+ if (should_start && !vhost_dev_is_initialized(&vvc->vhost_dev)) {
+ error_report("vhost-vsock: refusing to start, device init incomplete");
+ return 0;
+ }
+
if (vhost_dev_is_started(&vvc->vhost_dev) == should_start) {
return 0;
}
if (should_start) {
+ /*
+ * The ownership is released in .pre_save() on CPR migration
+ * source. If we end up here without it - that means that either
+ * CPR migration failed, or we got 'cont' cmd from the management
+ * (e.g. cpr-transfer target died). In either case, we need to
+ * re-acquire device ownership.
+ *
+ * On failure leave the device stopped. The next start attempt
+ * (guest driver reset, VM stop/cont) retries.
+ */
+ if (!vvc->vhost_dev.owner) {
+ ret = vhost_dev_set_owner(&vvc->vhost_dev);
+ if (ret < 0) {
+ error_report("vhost-vsock: cannot re-acquire device "
+ "ownership: %s", strerror(-ret));
+ return 0;
+ }
+ }
+
ret = vhost_vsock_common_start(vdev);
if (ret < 0) {
return 0;
@@ -123,8 +151,42 @@ static uint64_t vhost_vsock_get_features(VirtIODevice *vdev,
return vhost_vsock_common_get_features(vdev, requested_features, errp);
}
+static int vhost_vsock_pre_save(void *opaque)
+{
+ VHostVSockCommon *vvc = VHOST_VSOCK_COMMON(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) && migration_is_running() &&
+ vvc->vhost_dev.owner) {
+ ret = vhost_dev_reset_owner(&vvc->vhost_dev);
+ if (ret < 0) {
+ error_report("vhost-vsock: vhost_reset_owner failed: %s",
+ strerror(-ret));
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
static int vhost_vsock_post_load(void *opaque, int version_id)
{
+ VHostVSockCommon *vvc = VHOST_VSOCK_COMMON(opaque);
+ VirtIODevice *vdev = VIRTIO_DEVICE(opaque);
+ g_autofree char *cpr_name = vhost_vsock_cpr_name(DEVICE(opaque));
+ 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
@@ -134,6 +196,40 @@ 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(cpr_name, 0);
+ if (vhostfd < 0) {
+ error_report("vhost-vsock: could not find restored vhost FD");
+ return -1;
+ }
+
+ /*
+ * The source has released the ownership on this FD for us, so give it
+ * back if this target goes away.
+ */
+ vvc->vhost_dev.owner_handed_over = true;
+ 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);
+ cpr_delete_fd(cpr_name, 0);
+ return ret;
+ }
+
+ ret = vhost_vsock_set_guest_cid(vdev);
+ if (ret < 0) {
+ error_report("vhost-vsock: unable to set guest cid: %s",
+ strerror(-ret));
+ vhost_dev_cleanup(&vvc->vhost_dev);
+ cpr_delete_fd(cpr_name, 0);
+ return ret;
+ }
+
return 0;
}
@@ -145,7 +241,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,
};
@@ -171,37 +267,24 @@ static void vhost_vsock_device_realize(DeviceState *dev, Error **errp)
return;
}
- /*
- * 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.
- */
- 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 (cpr_incoming) {
/* Reuse the fd handed over from the source QEMU */
vhostfd = cpr_find_fd(cpr_name, 0);
if (vhostfd < 0) {
error_setg(errp, "vhost-vsock: could not find restored vhost FD");
- goto err_blocker;
+ return;
}
} else if (vsock->conf.vhostfd) {
vhostfd = monitor_fd_param(monitor_cur(), vsock->conf.vhostfd, errp);
if (vhostfd == -1) {
error_prepend(errp, "vhost-vsock: unable to parse vhostfd: ");
- goto err_blocker;
+ return;
}
} else {
vhostfd = open("/dev/vhost-vsock", O_RDWR);
if (vhostfd < 0) {
error_setg_file_open(errp, errno, "/dev/vhost-vsock");
- goto err_blocker;
+ return;
}
}
@@ -210,19 +293,37 @@ static void vhost_vsock_device_realize(DeviceState *dev, Error **errp)
if (cpr_incoming) {
cpr_delete_fd(cpr_name, 0);
}
- goto err_blocker;
+ return;
}
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 */
+ goto err_vhost_dev;
+ }
+
+ return;
}
ret = vhost_vsock_set_guest_cid(vdev);
@@ -232,7 +333,7 @@ static void vhost_vsock_device_realize(DeviceState *dev, Error **errp)
}
/* Register the fd for a future CPR after a fully successful realize */
- if (!cpr_incoming && !cpr_save_fd(cpr_name, 0, vhostfd, errp)) {
+ if (!cpr_save_fd(cpr_name, 0, vhostfd, errp)) {
goto err_vhost_dev;
}
@@ -247,22 +348,18 @@ err_vhost_dev:
}
err_virtio:
vhost_vsock_common_unrealize(vdev);
-err_blocker:
- migrate_del_blocker(&vsock->migration_blocker);
}
static void vhost_vsock_device_unrealize(DeviceState *dev)
{
VHostVSockCommon *vvc = VHOST_VSOCK_COMMON(dev);
VirtIODevice *vdev = VIRTIO_DEVICE(dev);
- VHostVSock *vsock = VHOST_VSOCK(dev);
g_autofree char *cpr_name = vhost_vsock_cpr_name(dev);
/* This will stop vhost backend if appropriate. */
vhost_vsock_set_status(vdev, 0);
cpr_delete_fd(cpr_name, 0);
- migrate_del_blocker(&vsock->migration_blocker);
vhost_dev_cleanup(&vvc->vhost_dev);
vhost_vsock_common_unrealize(vdev);
}
--
MST
^ permalink raw reply related [flat|nested] 84+ messages in thread
* [PULL 29/78] tests/qtest/migration: add vhost-vsock tests for cpr-transfer and cpr-exec
2026-10-28 17:13 [PULL 00/78] pci, vhost, virtio, iommu, acpi: features, fixes Michael S. Tsirkin
` (27 preceding siblings ...)
2026-10-28 17:15 ` [PULL 28/78] vhost-vsock: hand off device ownership across CPR Michael S. Tsirkin
@ 2026-10-28 17:15 ` Michael S. Tsirkin
2026-10-28 17:15 ` [PULL 30/78] tests/functional: add vhost-vsock CPR test with a live guest Michael S. Tsirkin
` (49 subsequent siblings)
78 siblings, 0 replies; 84+ messages in thread
From: Michael S. Tsirkin @ 2026-10-28 17:15 UTC (permalink / raw)
To: qemu-devel
Cc: Peter Maydell, Andrey Drobyshev, Peter Xu, Akihiko Odaki,
Maciej S. Szmigiero, Fabiano Rosas, Mark Kanda, Ben Chaney,
Laurent Vivier, Paolo Bonzini
From: Andrey Drobyshev <andrey.drobyshev@virtuozzo.com>
Run the existing cpr-transfer and cpr-exec tests with a vhost-vsock-pci
device attached. There's no guest involved which is driving the device,
so here we only test the QEMU side of the handoff, i.e. the vhost FD is
preserved, the device ownership is released on the source and re-acquired
on the target.
The tests need /dev/vhost-vsock on the host and a kernel supporting
VHOST_RESET_OWNER for vhost-vsock, they are skipped otherwise.
Reviewed-by: Peter Xu <peterx@redhat.com>
Signed-off-by: Andrey Drobyshev <andrey.drobyshev@virtuozzo.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Reviewed-by: Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Message-ID: <20261012-up-vsock-vhost-v5-v16-20-36c38bd15ffc@virtuozzo.com>
---
tests/qtest/migration/cpr-tests.c | 93 +++++++++++++++++++++++++++++--
1 file changed, 87 insertions(+), 6 deletions(-)
diff --git a/tests/qtest/migration/cpr-tests.c b/tests/qtest/migration/cpr-tests.c
index 05c2ac1336..3c21b79001 100644
--- a/tests/qtest/migration/cpr-tests.c
+++ b/tests/qtest/migration/cpr-tests.c
@@ -19,6 +19,13 @@
#include "qobject/qjson.h"
#include "qobject/qlist.h"
+#ifdef CONFIG_LINUX
+#include <sys/ioctl.h>
+#include <linux/vhost.h>
+#endif
+
+/* We expect this CID to be unassigned on the host */
+#define VHOST_VSOCK_TEST_DEV "vhost-vsock-pci,guest-cid=4000000000"
static char *tmpfs;
@@ -101,14 +108,17 @@ static int test_transfer(MigrateCommon *args, const char *cpr_channel,
* migration, and cannot connect synchronously to the monitor, so defer
* the target connection.
*/
-static void test_mode_transfer_common(MigrateCommon *args, bool incoming_defer)
+static void test_mode_transfer_common(MigrateCommon *args, bool incoming_defer,
+ const char *dev_opts)
{
g_autofree char *cpr_path = g_strdup_printf("%s/cpr.sock", tmpfs);
g_autofree char *mig_path = g_strdup_printf("%s/migsocket", tmpfs);
g_autofree char *uri = g_strdup_printf("unix:%s", mig_path);
g_autofree char *opts_target = NULL;
- const char *opts = "-machine aux-ram-share=on -nodefaults";
+ g_autofree char *opts = g_strdup_printf("-machine aux-ram-share=on "
+ "-nodefaults %s",
+ dev_opts ? dev_opts : "");
g_autofree const char *cpr_channel = g_strdup_printf(
"cpr,addr.transport=socket,addr.type=unix,addr.path=%s",
cpr_path);
@@ -154,12 +164,12 @@ static void test_mode_transfer_common(MigrateCommon *args, bool incoming_defer)
static void test_mode_transfer(char *name, MigrateCommon *args)
{
- test_mode_transfer_common(args, false);
+ test_mode_transfer_common(args, false, NULL);
}
static void test_mode_transfer_defer(char *name, MigrateCommon *args)
{
- test_mode_transfer_common(args, true);
+ test_mode_transfer_common(args, true, NULL);
}
static void set_cpr_exec_args(QTestState *who, MigrateCommon *args)
@@ -276,20 +286,85 @@ static void *test_mode_exec_start(QTestState *from, QTestState *to)
return NULL;
}
-static void test_mode_exec(char *name, MigrateCommon *args)
+static void test_mode_exec_common(MigrateCommon *args, const char *dev_opts)
{
g_autofree char *uri = g_strdup_printf("file:%s/%s", tmpfs,
FILE_TEST_FILENAME);
+ g_autofree char *opts = g_strdup_printf("-machine aux-ram-share=on "
+ "-nodefaults %s",
+ dev_opts ? dev_opts : "");
args->uri = uri;
args->start_hook = test_mode_exec_start;
args->start.only_source = true;
- args->start.opts_source = "-machine aux-ram-share=on -nodefaults";
+ args->start.opts_source = opts;
args->start.mem_type = MEM_TYPE_MEMFD;
test_cpr_exec(args);
}
+static void test_mode_exec(char *name, MigrateCommon *args)
+{
+ test_mode_exec_common(args, NULL);
+}
+
+/*
+ * A vhost-vsock device can be handed over across CPR only if the host
+ * exposes /dev/vhost-vsock and its kernel supports VHOST_RESET_OWNER
+ * for it. Skip the test otherwise.
+ */
+static bool vhost_vsock_cpr_check(void)
+{
+#ifdef CONFIG_LINUX
+ bool set_owner, reset_owner;
+ int fd;
+
+ if (!qtest_has_device("vhost-vsock-pci")) {
+ g_test_skip("vhost-vsock-pci device is not available");
+ return false;
+ }
+
+ fd = open("/dev/vhost-vsock", O_RDWR);
+ if (fd < 0) {
+ g_test_skip("/dev/vhost-vsock is not available");
+ return false;
+ }
+
+ set_owner = ioctl(fd, VHOST_SET_OWNER) == 0;
+ reset_owner = ioctl(fd, VHOST_RESET_OWNER) == 0;
+ close(fd);
+
+ if (!set_owner) {
+ g_test_skip("VHOST_SET_OWNER is not supported for vhost-vsock");
+ return false;
+ }
+
+ if (!reset_owner) {
+ g_test_skip("VHOST_RESET_OWNER is not supported for vhost-vsock");
+ return false;
+ }
+
+ return true;
+#else
+ g_test_skip("vhost-vsock is Linux only");
+ return false;
+#endif
+}
+
+static void test_mode_transfer_vhost_vsock(char *name, MigrateCommon *args)
+{
+ if (vhost_vsock_cpr_check()) {
+ test_mode_transfer_common(args, false, "-device " VHOST_VSOCK_TEST_DEV);
+ }
+}
+
+static void test_mode_exec_vhost_vsock(char *name, MigrateCommon *args)
+{
+ if (vhost_vsock_cpr_check()) {
+ test_mode_exec_common(args, "-device " VHOST_VSOCK_TEST_DEV);
+ }
+}
+
/* Snapshots must be refused while a CPR migration mode is set */
static void test_mode_no_snapshot(char *name, MigrateCommon *args)
{
@@ -354,4 +429,10 @@ void migration_test_add_cpr(MigrationTestEnv *env)
test_mode_transfer_defer);
migration_test_add("/migration/mode/exec", test_mode_exec);
}
+
+ /* The vhost-vsock handoff doesn't need KVM */
+ migration_test_add("/migration/mode/transfer/vhost-vsock",
+ test_mode_transfer_vhost_vsock);
+ migration_test_add("/migration/mode/exec/vhost-vsock",
+ test_mode_exec_vhost_vsock);
}
--
MST
^ permalink raw reply related [flat|nested] 84+ messages in thread
* [PULL 30/78] tests/functional: add vhost-vsock CPR test with a live guest
2026-10-28 17:13 [PULL 00/78] pci, vhost, virtio, iommu, acpi: features, fixes Michael S. Tsirkin
` (28 preceding siblings ...)
2026-10-28 17:15 ` [PULL 29/78] tests/qtest/migration: add vhost-vsock tests for cpr-transfer and cpr-exec Michael S. Tsirkin
@ 2026-10-28 17:15 ` Michael S. Tsirkin
2026-10-28 17:15 ` [PULL 31/78] tests/functional: cover a cpr-transfer target going away with vhost-vsock Michael S. Tsirkin
` (48 subsequent siblings)
78 siblings, 0 replies; 84+ messages in thread
From: Michael S. Tsirkin @ 2026-10-28 17:15 UTC (permalink / raw)
To: qemu-devel
Cc: Peter Maydell, Andrey Drobyshev, Peter Xu, Akihiko Odaki,
Paolo Bonzini, Zhao Liu
From: Andrey Drobyshev <andrey.drobyshev@virtuozzo.com>
The qtests only cover the QEMU side of the vhost-vsock handoff. Add a
functional test which runs an actual host<->guest vsock transfer across
a series of CPR migrations, for both cpr-transfer and cpr-exec:
- the guest is booted on top of a qcow2 overlay shared by all the QEMU
instances it goes through;
- the host sends a 512M file of random data to the guest with socat,
where it's stored on disk, then gets it back the same way; a CPR
migration is performed every 0.2s while a transfer is in progress;
- with cpr-transfer each migration starts a fresh target QEMU and
retires the source; with cpr-exec the same process is re-exec'ed
over and over;
- the file received back must be identical to the one sent, and both
socat instances in the guest must exit cleanly.
The test needs KVM, socat and /dev/vhost-vsock on the host, and a kernel
supporting VHOST_RESET_OWNER for vhost-vsock. If any of those
requirements are missing the test is skipped.
Acked-by: Peter Xu <peterx@redhat.com>
Signed-off-by: Andrey Drobyshev <andrey.drobyshev@virtuozzo.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Reviewed-by: Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Message-ID: <20261012-up-vsock-vhost-v5-v16-21-36c38bd15ffc@virtuozzo.com>
---
tests/functional/x86_64/meson.build | 2 +
.../functional/x86_64/test_vhost_vsock_cpr.py | 245 ++++++++++++++++++
2 files changed, 247 insertions(+)
create mode 100755 tests/functional/x86_64/test_vhost_vsock_cpr.py
diff --git a/tests/functional/x86_64/meson.build b/tests/functional/x86_64/meson.build
index 6254149239..bcdb1ee558 100644
--- a/tests/functional/x86_64/meson.build
+++ b/tests/functional/x86_64/meson.build
@@ -7,6 +7,7 @@ test_x86_64_timeouts = {
'netdev_ethtool' : 180,
'replay' : 480,
'virtio_balloon': 120,
+ 'vhost_vsock_cpr': 300,
}
tests_x86_64_system_quick = [
@@ -52,4 +53,5 @@ tests_x86_64_system_thorough = [
'virtio_gpu',
'rebuild_vmfd',
'tap_migration',
+ 'vhost_vsock_cpr',
]
diff --git a/tests/functional/x86_64/test_vhost_vsock_cpr.py b/tests/functional/x86_64/test_vhost_vsock_cpr.py
new file mode 100755
index 0000000000..035307fc82
--- /dev/null
+++ b/tests/functional/x86_64/test_vhost_vsock_cpr.py
@@ -0,0 +1,245 @@
+#!/usr/bin/env python3
+#
+# Functional test for vhost-vsock over CPR (cpr-transfer and cpr-exec):
+# a host<->guest vsock transfer must survive repeated CPR migrations, in
+# either direction.
+#
+# Copyright (c) 2026 Virtuozzo International GmbH.
+#
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+import fcntl
+import filecmp
+import os
+import subprocess
+import time
+
+from qemu.qmp import ConnectError, ExecInterruptedError
+from qemu.qmp.legacy import QEMUMonitorProtocol
+from qemu_test import (Asset, LinuxKernelTest, exec_command,
+ exec_command_and_wait_for_pattern, get_qemu_img,
+ skipIfMissingCommands)
+
+GUEST_CID = 4000000010 # we expect this CID to be free on the host
+VSOCK_PORT = 5000
+
+TRANSFER_SIZE = 512 * 1024 * 1024 # file size for host<->guest transfer
+CPR_INTERVAL = 0.2 # interval between CPR ops
+
+VHOST_SET_OWNER = 0xAF01 # _IO(VHOST_VIRTIO, 0x01)
+VHOST_RESET_OWNER = 0xAF02 # _IO(VHOST_VIRTIO, 0x02)
+
+# What a QMP connection raises when its QEMU goes away underneath it
+QMP_GONE = (OSError, EOFError, ConnectError, ExecInterruptedError)
+
+
+@skipIfMissingCommands('socat')
+class VhostVsockCPR(LinuxKernelTest):
+ # This should have passwordless root login, vsock support + socat
+ ASSET_DISKIMAGE = Asset(
+ ('https://cloud.debian.org/images/cloud/bookworm/20231210-1590/'
+ 'debian-12-nocloud-amd64-20231210-1590.qcow2'),
+ 'b94e3f34db59988a815b33dffc24f5a58502540efece2cc1ed15e0ac8749bb2e')
+
+ def require_vhost_vsock_cpr(self):
+ try:
+ fd = os.open('/dev/vhost-vsock', os.O_RDWR)
+ except OSError:
+ self.skipTest('/dev/vhost-vsock is not available')
+ try:
+ for name, request in (('VHOST_SET_OWNER', VHOST_SET_OWNER),
+ ('VHOST_RESET_OWNER', VHOST_RESET_OWNER)):
+ try:
+ fcntl.ioctl(fd, request)
+ except OSError:
+ self.skipTest(f'{name} is not supported for vhost-vsock')
+ finally:
+ os.close(fd)
+
+ def setUp(self):
+ super().setUp()
+ self.require_accelerator('kvm')
+ self.require_device('vhost-vsock-pci')
+ self.require_vhost_vsock_cpr()
+ self.set_machine('q35')
+
+ # Use Debian cloud image as a backing. During host<->guest
+ # transfer the guest will write data to its disk, so we start
+ # it with an overlay
+ self.disk = self.scratch_file('disk.qcow2')
+ subprocess.check_call([get_qemu_img(self), 'create', '-q', '-f',
+ 'qcow2', '-b', self.ASSET_DISKIMAGE.fetch(),
+ '-F', 'qcow2', self.disk])
+
+ self.payload = self.scratch_file('payload')
+ with open(self.payload, 'wb') as f:
+ for _ in range(TRANSFER_SIZE >> 20):
+ f.write(os.urandom(1 << 20))
+
+ self.sock_dir = self.socket_dir().name
+ self.cpr_count = 0
+
+ self.guest = None
+ self.qmp_path = None
+
+ def prepare_vm(self, vm, name, *extra_args):
+ # We don't want QEMUMachine.launch() set up QMP for us. In this
+ # case launch() waits for greeting - which won't be sent cause
+ # CPR target is reading CPR migration channel before QMP
+ qmp_path = os.path.join(self.sock_dir, f'{name}-qmp.sock')
+ vm.set_qmp_monitor(False)
+ vm.set_console()
+ vm.add_args('-accel', 'kvm', '-m', '1G',
+ '-object', 'memory-backend-memfd,id=ram,share=on,size=1G',
+ '-machine', 'memory-backend=ram,aux-ram-share=on',
+ '-drive', f'file={self.disk},if=none,id=disk,format=qcow2',
+ '-device', 'virtio-blk-pci,drive=disk',
+ '-device', f'vhost-vsock-pci,guest-cid={GUEST_CID}',
+ '-qmp', f'unix:{qmp_path},server=on,wait=off',
+ *extra_args)
+ return qmp_path
+
+ def wait_for(self, what, cond, timeout=60):
+ """Poll cond() until it returns something true, and return that"""
+ deadline = time.monotonic() + timeout
+ while True:
+ value = cond()
+ if value:
+ return value
+ self.assertLess(time.monotonic(), deadline,
+ f'timed out waiting for {what}')
+ time.sleep(0.2)
+
+ def qmp_connect(self, qmp_path):
+ """Connect to a QEMU which may still be starting up"""
+ def attempt():
+ try:
+ qmp = QEMUMonitorProtocol(qmp_path)
+ qmp.connect()
+ return qmp
+ except (OSError, ConnectError):
+ return None
+ return self.wait_for('QMP to come up', attempt)
+
+ def wait_migration(self, qmp):
+ def completed():
+ status = qmp.cmd('query-migrate').get('status')
+ self.assertNotEqual(status, 'failed', 'migration failed')
+ return status == 'completed'
+ self.wait_for('migration completion', completed)
+
+ def wait_running(self, qmp_path):
+ # Make sure we're dealing with the new QEMU after CPR
+ while True:
+ try:
+ with self.qmp_connect(qmp_path) as qmp:
+ self.wait_for('the VM to resume', lambda: qmp.cmd(
+ 'query-status')['status'] == 'running')
+ return
+ except QMP_GONE:
+ pass
+
+ def boot(self):
+ self.guest = self.vm
+ self.qmp_path = self.prepare_vm(self.guest, 'source')
+ self.guest.launch()
+ self.wait_for_console_pattern('login:')
+ exec_command_and_wait_for_pattern(self, 'root', 'root@localhost:~#')
+
+ def transfer(self, guest_cmd, host_cmd, cpr):
+ """
+ Run a socat transfer between the guest and the host, and keep
+ performing CPR migrations as long as it's in progress
+ """
+ exec_command(self, f'{guest_cmd} &', vm=self.guest)
+ with subprocess.Popen(host_cmd) as socat:
+ while socat.poll() is None:
+ time.sleep(CPR_INTERVAL)
+ cpr()
+ self.assertEqual(socat.returncode, 0, 'host socat failed')
+ exec_command_and_wait_for_pattern(self, 'wait %1; echo RC_$?',
+ 'RC_0', vm=self.guest)
+
+ def transfer_both_ways(self, cpr):
+ """
+ Send the payload to a file on the guest's disk, get it back into
+ another file on the host, then compare.
+ """
+ echo = self.scratch_file('echo')
+ connect = f'VSOCK-CONNECT:{GUEST_CID}:{VSOCK_PORT},retry=10'
+
+ self.transfer(f'socat -u VSOCK-LISTEN:{VSOCK_PORT} CREATE:/root/data',
+ ['socat', '-u', f'FILE:{self.payload}', connect], cpr)
+ self.transfer(f'socat -u FILE:/root/data VSOCK-LISTEN:{VSOCK_PORT}',
+ ['socat', '-u', connect, f'CREATE:{echo}'], cpr)
+
+ self.log.info('%d CPR migrations performed', self.cpr_count)
+ self.assertTrue(filecmp.cmp(self.payload, echo, shallow=False),
+ 'the data came back different')
+
+ def test_cpr_transfer(self):
+ """
+ Perform consecutive cpr-transfer migrations during vsock file
+ transfer, starting a new QEMU instance on each one, migrating into
+ it and shutting down the source
+ """
+ def cpr():
+ n = self.cpr_count
+ mig_sock = os.path.join(self.sock_dir, f'mig{n}.sock')
+ cpr_sock = os.path.join(self.sock_dir, f'cpr{n}.sock')
+ target = self.get_vm(name=f'target{n}')
+ target_qmp_path = self.prepare_vm(
+ target, f'target{n}',
+ '-incoming', f'unix:{mig_sock}',
+ '-incoming', 'cpr,addr.transport=socket,addr.type=unix,'
+ f'addr.path={cpr_sock}')
+
+ # The target listens on the CPR channel: wait for it to be there
+ target.launch()
+ self.wait_for('the CPR socket', lambda: os.path.exists(cpr_sock))
+
+ with self.qmp_connect(self.qmp_path) as qmp:
+ qmp.cmd('migrate-set-parameters', mode='cpr-transfer')
+ qmp.cmd('migrate', channels=[
+ {'channel-type': 'main',
+ 'addr': {'transport': 'socket', 'type': 'unix',
+ 'path': mig_sock}},
+ {'channel-type': 'cpr',
+ 'addr': {'transport': 'socket', 'type': 'unix',
+ 'path': cpr_sock}}])
+ self.wait_migration(qmp)
+ self.wait_running(target_qmp_path)
+
+ self.guest.shutdown()
+ self.guest, self.qmp_path = target, target_qmp_path
+ self.cpr_count += 1
+
+ self.boot()
+ self.transfer_both_ways(cpr)
+
+ def test_cpr_exec(self):
+ """
+ Perform consecutive cpr-exec migration during vsock file transfer,
+ saving devices' state to a file
+ """
+ state = self.scratch_file('cpr.state')
+
+ def cpr():
+ try:
+ with self.qmp_connect(self.qmp_path) as qmp:
+ qmp.cmd('migrate-set-parameters', **{
+ 'mode': 'cpr-exec', 'cpr-exec-command': exec_cmd})
+ qmp.cmd('migrate', uri=f'file:{state}')
+ self.wait_migration(qmp)
+ except QMP_GONE:
+ pass
+ self.wait_running(self.qmp_path)
+ self.cpr_count += 1
+
+ self.boot()
+ exec_cmd = [*self.guest._qemu_full_args, '-incoming', f'file:{state}']
+ self.transfer_both_ways(cpr)
+
+
+if __name__ == '__main__':
+ LinuxKernelTest.main()
--
MST
^ permalink raw reply related [flat|nested] 84+ messages in thread
* [PULL 31/78] tests/functional: cover a cpr-transfer target going away with vhost-vsock
2026-10-28 17:13 [PULL 00/78] pci, vhost, virtio, iommu, acpi: features, fixes Michael S. Tsirkin
` (29 preceding siblings ...)
2026-10-28 17:15 ` [PULL 30/78] tests/functional: add vhost-vsock CPR test with a live guest Michael S. Tsirkin
@ 2026-10-28 17:15 ` Michael S. Tsirkin
2026-10-28 17:15 ` [PULL 32/78] qapi/virtio: Restore backend features in x-query-virtio-status Michael S. Tsirkin
` (47 subsequent siblings)
78 siblings, 0 replies; 84+ messages in thread
From: Michael S. Tsirkin @ 2026-10-28 17:15 UTC (permalink / raw)
To: qemu-devel
Cc: Peter Maydell, Andrey Drobyshev, Akihiko Odaki, Paolo Bonzini,
Zhao Liu
From: Andrey Drobyshev <andrey.drobyshev@virtuozzo.com>
A cpr-transfer target takes the vsock device over in post_load(). Make
sure the source gets it back when the target goes away afterwards.
Three cases, in the first two the target is made to fail the load by
lacking a device the source has:
- the target exits on the failure (default exit-on-error); the source
is stopped beforehand, so it stays paused until we 'cont' it once
the target is gone;
- the target stays alive (exit-on-error=false) and the source resumes
on its own (return-path capability);
- the target loads the state while the source is stopped, stays
paused and is quit normally; the source is resumed instead.
In all cases a host<->guest transfer must then work on the source.
The transfer helpers get a payload size parameter, since the plain
checks don't need the 512M of the stress cases, and the target start
code is moved into a helper shared with the existing cpr-transfer test.
Signed-off-by: Andrey Drobyshev <andrey.drobyshev@virtuozzo.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Reviewed-by: Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Message-ID: <20261012-up-vsock-vhost-v5-v16-22-36c38bd15ffc@virtuozzo.com>
---
.../functional/x86_64/test_vhost_vsock_cpr.py | 178 ++++++++++++++----
1 file changed, 140 insertions(+), 38 deletions(-)
diff --git a/tests/functional/x86_64/test_vhost_vsock_cpr.py b/tests/functional/x86_64/test_vhost_vsock_cpr.py
index 035307fc82..3ac0a80c5e 100755
--- a/tests/functional/x86_64/test_vhost_vsock_cpr.py
+++ b/tests/functional/x86_64/test_vhost_vsock_cpr.py
@@ -24,6 +24,7 @@
VSOCK_PORT = 5000
TRANSFER_SIZE = 512 * 1024 * 1024 # file size for host<->guest transfer
+CHECK_SIZE = 16 * 1024 * 1024 # enough to just check that vsock works
CPR_INTERVAL = 0.2 # interval between CPR ops
VHOST_SET_OWNER = 0xAF01 # _IO(VHOST_VIRTIO, 0x01)
@@ -71,11 +72,6 @@ def setUp(self):
'qcow2', '-b', self.ASSET_DISKIMAGE.fetch(),
'-F', 'qcow2', self.disk])
- self.payload = self.scratch_file('payload')
- with open(self.payload, 'wb') as f:
- for _ in range(TRANSFER_SIZE >> 20):
- f.write(os.urandom(1 << 20))
-
self.sock_dir = self.socket_dir().name
self.cpr_count = 0
@@ -121,12 +117,16 @@ def attempt():
return None
return self.wait_for('QMP to come up', attempt)
- def wait_migration(self, qmp):
- def completed():
+ def wait_migration(self, qmp, expect='completed'):
+ def finished():
status = qmp.cmd('query-migrate').get('status')
- self.assertNotEqual(status, 'failed', 'migration failed')
- return status == 'completed'
- self.wait_for('migration completion', completed)
+ if status in ('completed', 'failed'):
+ if expect:
+ self.assertEqual(status, expect,
+ 'unexpected migration result')
+ return True
+ return False
+ self.wait_for('migration completion', finished)
def wait_running(self, qmp_path):
# Make sure we're dealing with the new QEMU after CPR
@@ -139,13 +139,37 @@ def wait_running(self, qmp_path):
except QMP_GONE:
pass
- def boot(self):
+ def boot(self, *extra_args):
self.guest = self.vm
- self.qmp_path = self.prepare_vm(self.guest, 'source')
+ self.qmp_path = self.prepare_vm(self.guest, 'source', *extra_args)
self.guest.launch()
self.wait_for_console_pattern('login:')
exec_command_and_wait_for_pattern(self, 'root', 'root@localhost:~#')
+ def start_target(self, name, main_incoming=None):
+ """
+ Start a cpr-transfer target listening on the CPR channel. Return
+ the VM, its QMP socket path and the channels for 'migrate'.
+ """
+ mig_sock = os.path.join(self.sock_dir, f'{name}-mig.sock')
+ cpr_sock = os.path.join(self.sock_dir, f'{name}-cpr.sock')
+ channels = [
+ {'channel-type': 'main',
+ 'addr': {'transport': 'socket', 'type': 'unix',
+ 'path': mig_sock}},
+ {'channel-type': 'cpr',
+ 'addr': {'transport': 'socket', 'type': 'unix',
+ 'path': cpr_sock}}]
+
+ target = self.get_vm(name=name)
+ qmp_path = self.prepare_vm(
+ target, name, '-incoming', main_incoming or f'unix:{mig_sock}',
+ '-incoming', 'cpr,addr.transport=socket,addr.type=unix,'
+ f'addr.path={cpr_sock}')
+ target.launch()
+ self.wait_for('the CPR socket', lambda: os.path.exists(cpr_sock))
+ return target, qmp_path, channels
+
def transfer(self, guest_cmd, host_cmd, cpr):
"""
Run a socat transfer between the guest and the host, and keep
@@ -160,21 +184,26 @@ def transfer(self, guest_cmd, host_cmd, cpr):
exec_command_and_wait_for_pattern(self, 'wait %1; echo RC_$?',
'RC_0', vm=self.guest)
- def transfer_both_ways(self, cpr):
+ def transfer_both_ways(self, cpr, size=TRANSFER_SIZE):
"""
- Send the payload to a file on the guest's disk, get it back into
- another file on the host, then compare.
+ Send a random payload to a file on the guest's disk, get it back
+ into another file on the host, then compare.
"""
+ payload = self.scratch_file('payload')
+ with open(payload, 'wb') as f:
+ for _ in range(size >> 20):
+ f.write(os.urandom(1 << 20))
+
echo = self.scratch_file('echo')
connect = f'VSOCK-CONNECT:{GUEST_CID}:{VSOCK_PORT},retry=10'
self.transfer(f'socat -u VSOCK-LISTEN:{VSOCK_PORT} CREATE:/root/data',
- ['socat', '-u', f'FILE:{self.payload}', connect], cpr)
+ ['socat', '-u', f'FILE:{payload}', connect], cpr)
self.transfer(f'socat -u FILE:/root/data VSOCK-LISTEN:{VSOCK_PORT}',
['socat', '-u', connect, f'CREATE:{echo}'], cpr)
self.log.info('%d CPR migrations performed', self.cpr_count)
- self.assertTrue(filecmp.cmp(self.payload, echo, shallow=False),
+ self.assertTrue(filecmp.cmp(payload, echo, shallow=False),
'the data came back different')
def test_cpr_transfer(self):
@@ -184,29 +213,11 @@ def test_cpr_transfer(self):
it and shutting down the source
"""
def cpr():
- n = self.cpr_count
- mig_sock = os.path.join(self.sock_dir, f'mig{n}.sock')
- cpr_sock = os.path.join(self.sock_dir, f'cpr{n}.sock')
- target = self.get_vm(name=f'target{n}')
- target_qmp_path = self.prepare_vm(
- target, f'target{n}',
- '-incoming', f'unix:{mig_sock}',
- '-incoming', 'cpr,addr.transport=socket,addr.type=unix,'
- f'addr.path={cpr_sock}')
-
- # The target listens on the CPR channel: wait for it to be there
- target.launch()
- self.wait_for('the CPR socket', lambda: os.path.exists(cpr_sock))
-
+ target, target_qmp_path, channels = self.start_target(
+ f'target{self.cpr_count}')
with self.qmp_connect(self.qmp_path) as qmp:
qmp.cmd('migrate-set-parameters', mode='cpr-transfer')
- qmp.cmd('migrate', channels=[
- {'channel-type': 'main',
- 'addr': {'transport': 'socket', 'type': 'unix',
- 'path': mig_sock}},
- {'channel-type': 'cpr',
- 'addr': {'transport': 'socket', 'type': 'unix',
- 'path': cpr_sock}}])
+ qmp.cmd('migrate', channels=channels)
self.wait_migration(qmp)
self.wait_running(target_qmp_path)
@@ -217,6 +228,97 @@ def cpr():
self.boot()
self.transfer_both_ways(cpr)
+ def test_cpr_transfer_dead_target(self):
+ """
+ A cpr-transfer target which exits on a load error after having
+ taken the vsock device over must release it on exit, so that the
+ source can be resumed afterwards. The target lacks a device the
+ source has. The source is stopped beforehand, so it stays paused
+ on failure until we 'cont' it.
+ """
+ self.boot('-device', 'virtio-rng-pci')
+ target, _, channels = self.start_target('target')
+
+ with self.qmp_connect(self.qmp_path) as qmp:
+ qmp.cmd('stop')
+ qmp.cmd('migrate-set-parameters', mode='cpr-transfer')
+ qmp.cmd('migrate', channels=channels)
+ self.wait_migration(qmp, expect=None)
+ self.wait_for('the target to exit',
+ lambda: not target.is_running())
+ self.assertEqual(target.exitcode(), 1,
+ 'the target did not exit on the load error')
+ qmp.cmd('cont')
+
+ self.wait_running(self.qmp_path)
+ self.transfer_both_ways(lambda: None, size=CHECK_SIZE)
+
+ def test_cpr_transfer_target_quit(self):
+ """
+ A cpr-transfer target which has loaded the state but hasn't been
+ started yet may quit normally, and the source resume instead.
+ The target doesn't unrealize its devices on quit, so it must
+ release the vsock device on exit.
+ """
+ self.boot()
+ target, target_qmp_path, channels = self.start_target(
+ 'target', main_incoming='defer')
+
+ with self.qmp_connect(self.qmp_path) as qmp:
+ qmp.cmd('stop')
+ qmp.cmd('migrate-set-parameters', mode='cpr-transfer')
+ qmp.cmd('migrate', channels=channels)
+
+ target_qmp = self.qmp_connect(target_qmp_path)
+ target_qmp.cmd('migrate-incoming', channels=channels[:1])
+ self.wait_migration(qmp)
+ self.wait_migration(target_qmp)
+ self.assertEqual(target_qmp.cmd('query-status')['status'],
+ 'paused')
+ try:
+ target_qmp.cmd('quit')
+ target_qmp.close()
+ except QMP_GONE:
+ pass
+
+ self.wait_for('the target to quit',
+ lambda: not target.is_running())
+ self.assertEqual(target.exitcode(), 0, 'the target did not quit')
+ qmp.cmd('cont')
+
+ self.wait_running(self.qmp_path)
+ self.transfer_both_ways(lambda: None, size=CHECK_SIZE)
+
+ def test_cpr_transfer_failed_target(self):
+ """
+ A cpr-transfer target which fails to load the state after having
+ taken the vsock device over must give it back to the source.
+ The target lacks a device the source has, stays alive on failure
+ (exit-on-error=false), and the source resumes on its own
+ (return-path). Then vsock must still work on the source.
+ """
+ self.boot('-device', 'virtio-rng-pci')
+ target, target_qmp_path, channels = self.start_target(
+ 'target', main_incoming='defer')
+
+ with self.qmp_connect(self.qmp_path) as qmp:
+ qmp.cmd('migrate-set-parameters', mode='cpr-transfer')
+ qmp.cmd('migrate-set-capabilities', capabilities=[
+ {'capability': 'return-path', 'state': True}])
+ qmp.cmd('migrate', channels=channels)
+
+ # The target opens its monitor once it has read the CPR state
+ with self.qmp_connect(target_qmp_path) as target_qmp:
+ target_qmp.cmd('migrate-incoming', channels=channels[:1],
+ **{'exit-on-error': False})
+ self.wait_migration(qmp, expect='failed')
+ self.wait_migration(target_qmp, expect='failed')
+
+ # The source resumes by itself, and vsock must work on it
+ self.wait_running(self.qmp_path)
+ self.transfer_both_ways(lambda: None, size=CHECK_SIZE)
+ target.shutdown()
+
def test_cpr_exec(self):
"""
Perform consecutive cpr-exec migration during vsock file transfer,
--
MST
^ permalink raw reply related [flat|nested] 84+ messages in thread
* [PULL 32/78] qapi/virtio: Restore backend features in x-query-virtio-status
2026-10-28 17:13 [PULL 00/78] pci, vhost, virtio, iommu, acpi: features, fixes Michael S. Tsirkin
` (30 preceding siblings ...)
2026-10-28 17:15 ` [PULL 31/78] tests/functional: cover a cpr-transfer target going away with vhost-vsock Michael S. Tsirkin
@ 2026-10-28 17:15 ` Michael S. Tsirkin
2026-10-28 17:15 ` [PULL 33/78] qapi/monitor: Handle missing vhost-user protocol features Michael S. Tsirkin
` (46 subsequent siblings)
78 siblings, 0 replies; 84+ messages in thread
From: Michael S. Tsirkin @ 2026-10-28 17:15 UTC (permalink / raw)
To: qemu-devel; +Cc: Peter Maydell, Vladimir Sementsov-Ogievskiy
From: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
Commit 1ee0bc0c9bf0 removed vhost_dev.backend_features and its
representation in VhostStatus. It also removed the assignment to
VirtioStatus.backend_features, although both that field and its source,
VirtIODevice.backend_features_ex, still exist.
The resulting NULL pointer causes HMP info virtio-status and QMP
x-query-virtio-status to crash. This affects virtio devices even without
an active vhost backend.
Restore the assignment from VirtIODevice.backend_features_ex.
Fixes: 1ee0bc0c9bf0 ("vhost: drop backend_features field")
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Message-ID: <20260922213210.1429435-3-vsementsov@yandex-team.ru>
---
hw/virtio/virtio-qmp.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/hw/virtio/virtio-qmp.c b/hw/virtio/virtio-qmp.c
index 5aaeebdd96..8b7cd7a310 100644
--- a/hw/virtio/virtio-qmp.c
+++ b/hw/virtio/virtio-qmp.c
@@ -713,6 +713,8 @@ VirtioStatus *qmp_x_query_virtio_status(const char *path, Error **errp)
vdev->guest_features_ex);
status->host_features = qmp_decode_features(vdev->device_id,
vdev->host_features_ex);
+ status->backend_features = qmp_decode_features(vdev->device_id,
+ vdev->backend_features_ex);
switch (vdev->device_endian) {
case VIRTIO_DEVICE_ENDIAN_LITTLE:
--
MST
^ permalink raw reply related [flat|nested] 84+ messages in thread
* [PULL 33/78] qapi/monitor: Handle missing vhost-user protocol features
2026-10-28 17:13 [PULL 00/78] pci, vhost, virtio, iommu, acpi: features, fixes Michael S. Tsirkin
` (31 preceding siblings ...)
2026-10-28 17:15 ` [PULL 32/78] qapi/virtio: Restore backend features in x-query-virtio-status Michael S. Tsirkin
@ 2026-10-28 17:15 ` Michael S. Tsirkin
2026-10-28 17:15 ` [PULL 34/78] hw/cxl: destroy primary CCI before re-initialization on reset Michael S. Tsirkin
` (45 subsequent siblings)
78 siblings, 0 replies; 84+ messages in thread
From: Michael S. Tsirkin @ 2026-10-28 17:15 UTC (permalink / raw)
To: qemu-devel
Cc: Peter Maydell, Vladimir Sementsov-Ogievskiy, Nikolay Kuratov,
Markus Armbruster, Eric Blake
From: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
Commit f9a8f5812607 moved protocol_features to the vhost-user backend.
As a result, VhostStatus.protocol_features is only populated for
vhost-user devices.
For an active kernel vhost device, HMP info virtio-status and QMP
x-query-virtio-status crashes now because of unset protocol-features.
Make protocol-features optional in VhostStatus and skip dumping a
missing object in HMP.
This is a compatibility break. Okay, since VhostStatus is only used
by x-query-virtio-status, which is unstable.
Fixes: f9a8f5812607 ("vhost: move protocol_features to vhost_user")
Co-authored-by: Nikolay Kuratov <kniv@yandex-team.ru>
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
Reviewed-by: Markus Armbruster <armbru@redhat.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Message-ID: <20260922213210.1429435-4-vsementsov@yandex-team.ru>
---
qapi/virtio.json | 5 +++--
hw/virtio/virtio-hmp-cmds.c | 6 ++++--
2 files changed, 7 insertions(+), 4 deletions(-)
diff --git a/qapi/virtio.json b/qapi/virtio.json
index 0a4753cc34..f6876a5de1 100644
--- a/qapi/virtio.json
+++ b/qapi/virtio.json
@@ -82,7 +82,8 @@
#
# @acked-features: vhost_dev acked_features
#
-# @protocol-features: vhost_dev protocol_features
+# @protocol-features: vhost-user protocol features. Present only for
+# vhost-user devices.
#
# @max-queues: vhost_dev max_queues
#
@@ -101,7 +102,7 @@
'vq-index': 'int',
'features': 'VirtioDeviceFeatures',
'acked-features': 'VirtioDeviceFeatures',
- 'protocol-features': 'VhostDeviceProtocols',
+ '*protocol-features': 'VhostDeviceProtocols',
'max-queues': 'uint64',
'backend-cap': 'uint64',
'log-enabled': 'bool',
diff --git a/hw/virtio/virtio-hmp-cmds.c b/hw/virtio/virtio-hmp-cmds.c
index e5da6f0069..5b08a06ec4 100644
--- a/hw/virtio/virtio-hmp-cmds.c
+++ b/hw/virtio/virtio-hmp-cmds.c
@@ -176,8 +176,10 @@ void hmp_virtio_status(MonitorHMP *hmp, const QDict *qdict)
hmp_virtio_dump_features(hmp, s->vhost_dev->features);
monitor_hmp_printf(hmp, " Acked features:\n");
hmp_virtio_dump_features(hmp, s->vhost_dev->acked_features);
- monitor_hmp_printf(hmp, " Protocol features:\n");
- hmp_virtio_dump_protocols(hmp, s->vhost_dev->protocol_features);
+ if (s->vhost_dev->protocol_features) {
+ monitor_hmp_printf(hmp, " Protocol features:\n");
+ hmp_virtio_dump_protocols(hmp, s->vhost_dev->protocol_features);
+ }
}
qapi_free_VirtioStatus(s);
--
MST
^ permalink raw reply related [flat|nested] 84+ messages in thread
* [PULL 34/78] hw/cxl: destroy primary CCI before re-initialization on reset
2026-10-28 17:13 [PULL 00/78] pci, vhost, virtio, iommu, acpi: features, fixes Michael S. Tsirkin
` (32 preceding siblings ...)
2026-10-28 17:15 ` [PULL 33/78] qapi/monitor: Handle missing vhost-user protocol features Michael S. Tsirkin
@ 2026-10-28 17:15 ` Michael S. Tsirkin
2026-10-28 17:15 ` [PULL 35/78] hw/cxl: convert cxl-type3 to three-phase reset Michael S. Tsirkin
` (44 subsequent siblings)
78 siblings, 0 replies; 84+ messages in thread
From: Michael S. Tsirkin @ 2026-10-28 17:15 UTC (permalink / raw)
To: qemu-devel
Cc: Peter Maydell, Junjie Cao, qemu-stable, Jonathan Cameron,
Junjie Cao, Anisa Su, linux-cxl
From: Junjie Cao <junjie.cao@intel.com>
ct3d_reset() re-initializes the primary CCI through the call chain
cxl_device_register_init_t3() -> cxl_initialize_mailbox_t3() ->
cxl_init_cci(), but never calls cxl_destroy_cci() first. Each reset
cycle therefore leaks the old timer and leaves the old mutex
undestroyed while silently overwriting the CCI state.
Per CXL r4.0, the "Mailbox Interfaces Ready" bit in the Memory Device
Status register (Table 8-212) is set after a Conventional Reset or CXL
Reset once the device has re-initialized its mailbox interfaces. The
CCI is the software abstraction of these interfaces and must be properly
torn down before re-initialization.
The secondary CCIs (vdm_fm_owned_ld_mctp_cci, ld0_cci) already follow
the correct destroy-before-reinit pattern in the same function; apply
the same discipline to the primary CCI.
Also destroy the secondary CCIs in ct3_exit() where they were
previously leaked at device removal time. Guard the primary CCI
teardown there with the same .initialized check used for the secondary
CCIs: the primary CCI is only brought up from the reset path
(cxl_device_register_init_t3()), so a device that is unrealized before
its first reset would otherwise tear down a never-initialized CCI.
Fixes: cac36a8faffc ("hw/cxl/mbox: Pull the CCI definition out of the CXLDeviceState")
Cc: qemu-stable@nongnu.org
Signed-off-by: Junjie Cao <junjie.cao@intel.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Message-ID: <20260729144645.1552511-3-junjie.cao@intel.com>
---
hw/mem/cxl_type3.c | 13 ++++++++++++-
1 file changed, 12 insertions(+), 1 deletion(-)
diff --git a/hw/mem/cxl_type3.c b/hw/mem/cxl_type3.c
index a04dc74ecc..363ac632f9 100644
--- a/hw/mem/cxl_type3.c
+++ b/hw/mem/cxl_type3.c
@@ -1076,7 +1076,15 @@ static void ct3_exit(PCIDevice *pci_dev)
cxl_doe_cdat_release(cxl_cstate);
msix_uninit_exclusive_bar(pci_dev);
g_free(regs->special_ops);
- cxl_destroy_cci(&ct3d->cci);
+ if (ct3d->cci.initialized) {
+ cxl_destroy_cci(&ct3d->cci);
+ }
+ if (ct3d->vdm_fm_owned_ld_mctp_cci.initialized) {
+ cxl_destroy_cci(&ct3d->vdm_fm_owned_ld_mctp_cci);
+ }
+ if (ct3d->ld0_cci.initialized) {
+ cxl_destroy_cci(&ct3d->ld0_cci);
+ }
if (ct3d->dc.host_dc) {
cxl_destroy_dc_regions(ct3d);
address_space_destroy(&ct3d->dc.host_dc_as);
@@ -1331,6 +1339,9 @@ static void ct3d_reset(DeviceState *dev)
ct3d->flitmode);
cxl_component_register_init_common(reg_state, write_msk,
CXL2_TYPE3_DEVICE, ct3d->hdmdb);
+ if (ct3d->cci.initialized) {
+ cxl_destroy_cci(&ct3d->cci);
+ }
cxl_device_register_init_t3(ct3d, CXL_T3_MSIX_MBOX);
/*
--
MST
^ permalink raw reply related [flat|nested] 84+ messages in thread
* [PULL 35/78] hw/cxl: convert cxl-type3 to three-phase reset
2026-10-28 17:13 [PULL 00/78] pci, vhost, virtio, iommu, acpi: features, fixes Michael S. Tsirkin
` (33 preceding siblings ...)
2026-10-28 17:15 ` [PULL 34/78] hw/cxl: destroy primary CCI before re-initialization on reset Michael S. Tsirkin
@ 2026-10-28 17:15 ` Michael S. Tsirkin
2026-10-28 17:15 ` [PULL 36/78] hw/cxl: add cxl_destroy_mailbox_t3() as the mailbox teardown counterpart Michael S. Tsirkin
` (43 subsequent siblings)
78 siblings, 0 replies; 84+ messages in thread
From: Michael S. Tsirkin @ 2026-10-28 17:15 UTC (permalink / raw)
To: qemu-devel
Cc: Peter Maydell, Junjie Cao, Philippe Mathieu-Daudé,
Jonathan Cameron, Junjie Cao, Anisa Su, linux-cxl
From: Junjie Cao <junjie.cao@intel.com>
Replace the deprecated device_class_set_legacy_reset() registration
with the three-phase resettable interface, following the pattern
already established by the CXL root port (cxl_rp_reset_hold).
Only the hold phase is needed; enter and exit are left NULL. The
parent hold phase is chained for correctness: TYPE_PCI_DEVICE installs
no hold phase today, so parent_phases.hold is currently NULL and the
chained call is a no-op, but capturing and invoking it is the correct
forward-compatible pattern and mirrors cxl_rp_reset_hold().
No functional change — the reset body is identical; only the
registration path and function signature change.
Signed-off-by: Junjie Cao <junjie.cao@intel.com>
Reviewed-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Message-ID: <20260729144645.1552511-4-junjie.cao@intel.com>
---
include/hw/cxl/cxl_device.h | 2 ++
hw/mem/cxl_type3.c | 16 ++++++++++++----
2 files changed, 14 insertions(+), 4 deletions(-)
diff --git a/include/hw/cxl/cxl_device.h b/include/hw/cxl/cxl_device.h
index ba551fa5f9..b7e20e3fe4 100644
--- a/include/hw/cxl/cxl_device.h
+++ b/include/hw/cxl/cxl_device.h
@@ -805,6 +805,8 @@ struct CXLType3Class {
/* Private */
PCIDeviceClass parent_class;
+ ResettablePhases parent_phases;
+
/* public */
uint64_t (*get_lsa_size)(CXLType3Dev *ct3d);
diff --git a/hw/mem/cxl_type3.c b/hw/mem/cxl_type3.c
index 363ac632f9..734397da62 100644
--- a/hw/mem/cxl_type3.c
+++ b/hw/mem/cxl_type3.c
@@ -1329,13 +1329,18 @@ MemTxResult cxl_type3_write(PCIDevice *d, hwaddr host_addr, uint64_t data,
return address_space_write(as, dpa_offset, attrs, &data, size);
}
-static void ct3d_reset(DeviceState *dev)
+static void ct3d_reset_hold(Object *obj, ResetType type)
{
- CXLType3Dev *ct3d = CXL_TYPE3(dev);
+ CXLType3Dev *ct3d = CXL_TYPE3(obj);
+ CXLType3Class *cvc = CXL_TYPE3_GET_CLASS(obj);
uint32_t *reg_state = ct3d->cxl_cstate.crb.cache_mem_registers;
uint32_t *write_msk = ct3d->cxl_cstate.crb.cache_mem_regs_write_mask;
- pcie_cap_fill_link_ep_usp(PCI_DEVICE(dev), ct3d->width, ct3d->speed,
+ if (cvc->parent_phases.hold) {
+ cvc->parent_phases.hold(obj, type);
+ }
+
+ pcie_cap_fill_link_ep_usp(PCI_DEVICE(obj), ct3d->width, ct3d->speed,
ct3d->flitmode);
cxl_component_register_init_common(reg_state, write_msk,
CXL2_TYPE3_DEVICE, ct3d->hdmdb);
@@ -2467,6 +2472,7 @@ static void ct3_class_init(ObjectClass *oc, const void *data)
DeviceClass *dc = DEVICE_CLASS(oc);
PCIDeviceClass *pc = PCI_DEVICE_CLASS(oc);
CXLType3Class *cvc = CXL_TYPE3_CLASS(oc);
+ ResettableClass *rc = RESETTABLE_CLASS(oc);
pc->realize = ct3_realize;
pc->exit = ct3_exit;
@@ -2480,9 +2486,11 @@ static void ct3_class_init(ObjectClass *oc, const void *data)
set_bit(DEVICE_CATEGORY_STORAGE, dc->categories);
dc->desc = "CXL Memory Device (Type 3)";
- device_class_set_legacy_reset(dc, ct3d_reset);
device_class_set_props(dc, ct3_props);
+ resettable_class_set_parent_phases(rc, NULL, ct3d_reset_hold, NULL,
+ &cvc->parent_phases);
+
cvc->get_lsa_size = get_lsa_size;
cvc->get_lsa = get_lsa;
cvc->set_lsa = set_lsa;
--
MST
^ permalink raw reply related [flat|nested] 84+ messages in thread
* [PULL 36/78] hw/cxl: add cxl_destroy_mailbox_t3() as the mailbox teardown counterpart
2026-10-28 17:13 [PULL 00/78] pci, vhost, virtio, iommu, acpi: features, fixes Michael S. Tsirkin
` (34 preceding siblings ...)
2026-10-28 17:15 ` [PULL 35/78] hw/cxl: convert cxl-type3 to three-phase reset Michael S. Tsirkin
@ 2026-10-28 17:15 ` Michael S. Tsirkin
2026-10-28 17:15 ` [PULL 37/78] hw/cxl: discard in-flight sanitize state on mailbox teardown Michael S. Tsirkin
` (42 subsequent siblings)
78 siblings, 0 replies; 84+ messages in thread
From: Michael S. Tsirkin @ 2026-10-28 17:15 UTC (permalink / raw)
To: qemu-devel
Cc: Peter Maydell, Junjie Cao, Philippe Mathieu-Daudé,
Jonathan Cameron, Junjie Cao, Anisa Su, linux-cxl
From: Junjie Cao <junjie.cao@intel.com>
The primary CCI teardown is open-coded, guarded by .initialized, at
the two places that need it: the reset path (before
cxl_device_register_init_t3() brings the mailbox back up) and
ct3_exit().
Factor it into cxl_destroy_mailbox_t3(), the teardown counterpart of
cxl_initialize_mailbox_t3(), keeping the .initialized guard inside the
helper: the primary CCI is only brought up from the reset path, so the
first reset after realize and an unrealize before any reset must both
skip the teardown.
No functional change.
Suggested-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com>
Signed-off-by: Junjie Cao <junjie.cao@intel.com>
Reviewed-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Message-ID: <20260729144645.1552511-5-junjie.cao@intel.com>
---
include/hw/cxl/cxl_device.h | 1 +
hw/cxl/cxl-mailbox-utils.c | 7 +++++++
hw/mem/cxl_type3.c | 8 ++------
3 files changed, 10 insertions(+), 6 deletions(-)
diff --git a/include/hw/cxl/cxl_device.h b/include/hw/cxl/cxl_device.h
index b7e20e3fe4..608d05165a 100644
--- a/include/hw/cxl/cxl_device.h
+++ b/include/hw/cxl/cxl_device.h
@@ -325,6 +325,7 @@ CXL_DEVICE_CAPABILITY_HEADER_REGISTER(MEMORY_DEVICE,
CXL_DEVICE_CAP_REG_SIZE * 2)
void cxl_initialize_mailbox_t3(CXLCCI *cci, DeviceState *d, size_t payload_max);
+void cxl_destroy_mailbox_t3(CXLType3Dev *ct3d);
void cxl_initialize_mailbox_swcci(CXLCCI *cci, DeviceState *intf,
DeviceState *d, size_t payload_max);
void cxl_init_cci(CXLCCI *cci, size_t payload_max);
diff --git a/hw/cxl/cxl-mailbox-utils.c b/hw/cxl/cxl-mailbox-utils.c
index 603677a97b..bc00fec9f8 100644
--- a/hw/cxl/cxl-mailbox-utils.c
+++ b/hw/cxl/cxl-mailbox-utils.c
@@ -4843,6 +4843,13 @@ void cxl_initialize_mailbox_t3(CXLCCI *cci, DeviceState *d, size_t payload_max)
cxl_init_cci(cci, payload_max);
}
+void cxl_destroy_mailbox_t3(CXLType3Dev *ct3d)
+{
+ if (ct3d->cci.initialized) {
+ cxl_destroy_cci(&ct3d->cci);
+ }
+}
+
static const struct cxl_cmd cxl_cmd_set_t3_ld[256][256] = {
[INFOSTAT][IS_IDENTIFY] = { "IDENTIFY", cmd_infostat_identify, 0, 0 },
[LOGS][GET_SUPPORTED] = { "LOGS_GET_SUPPORTED", cmd_logs_get_supported, 0,
diff --git a/hw/mem/cxl_type3.c b/hw/mem/cxl_type3.c
index 734397da62..04558a114f 100644
--- a/hw/mem/cxl_type3.c
+++ b/hw/mem/cxl_type3.c
@@ -1076,9 +1076,7 @@ static void ct3_exit(PCIDevice *pci_dev)
cxl_doe_cdat_release(cxl_cstate);
msix_uninit_exclusive_bar(pci_dev);
g_free(regs->special_ops);
- if (ct3d->cci.initialized) {
- cxl_destroy_cci(&ct3d->cci);
- }
+ cxl_destroy_mailbox_t3(ct3d);
if (ct3d->vdm_fm_owned_ld_mctp_cci.initialized) {
cxl_destroy_cci(&ct3d->vdm_fm_owned_ld_mctp_cci);
}
@@ -1344,9 +1342,7 @@ static void ct3d_reset_hold(Object *obj, ResetType type)
ct3d->flitmode);
cxl_component_register_init_common(reg_state, write_msk,
CXL2_TYPE3_DEVICE, ct3d->hdmdb);
- if (ct3d->cci.initialized) {
- cxl_destroy_cci(&ct3d->cci);
- }
+ cxl_destroy_mailbox_t3(ct3d);
cxl_device_register_init_t3(ct3d, CXL_T3_MSIX_MBOX);
/*
--
MST
^ permalink raw reply related [flat|nested] 84+ messages in thread
* [PULL 37/78] hw/cxl: discard in-flight sanitize state on mailbox teardown
2026-10-28 17:13 [PULL 00/78] pci, vhost, virtio, iommu, acpi: features, fixes Michael S. Tsirkin
` (35 preceding siblings ...)
2026-10-28 17:15 ` [PULL 36/78] hw/cxl: add cxl_destroy_mailbox_t3() as the mailbox teardown counterpart Michael S. Tsirkin
@ 2026-10-28 17:15 ` Michael S. Tsirkin
2026-10-28 17:15 ` [PULL 38/78] hw/cxl: clear event logs, scan media and interrupt policy on reset Michael S. Tsirkin
` (41 subsequent siblings)
78 siblings, 0 replies; 84+ messages in thread
From: Michael S. Tsirkin @ 2026-10-28 17:15 UTC (permalink / raw)
To: qemu-devel
Cc: Peter Maydell, Junjie Cao, Philippe Mathieu-Daudé,
Jonathan Cameron, Junjie Cao, Anisa Su, linux-cxl
From: Junjie Cao <junjie.cao@intel.com>
Per CXL r4.0 Section 8.2.9.4, "Background commands do not continue to
execute across Conventional Resets." If a Media Operations sanitize is
in progress when the device is reset, the background timer is cancelled
and freed via cxl_destroy_cci(), but the per-operation state
(media_op_sanitize) is heap-allocated separately and would be leaked.
Free it from cxl_destroy_mailbox_t3(): the timer that advances the
operation lives in the CCI torn down there, so the operation can never
complete once the mailbox is gone; preserving the heap state has no
benefit and would leak it the next time media_op_sanitize is assigned.
Routing the discard through the mailbox teardown also covers device
unrealize, where ct3_exit() previously leaked an in-flight sanitize.
The completion path in __do_sanitize() frees the same state; factor the
free into a cxl_discard_media_op_sanitize() helper used by both places,
so the whole media_op_sanitize lifecycle now stays within
cxl-mailbox-utils.c.
Note that Section 8.2.10.9.5.1 additionally requires a device whose
Sanitize was interrupted by reset to remain in the Media Disabled state
until a successful Sanitize completes. That latch is not modelled here
(reset re-enables media via memdev_reg_init_common()) and is left for
future work; this patch only addresses the resource leak.
Suggested-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com>
Signed-off-by: Junjie Cao <junjie.cao@intel.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Message-ID: <20260729144645.1552511-6-junjie.cao@intel.com>
---
hw/cxl/cxl-mailbox-utils.c | 15 +++++++++++++--
1 file changed, 13 insertions(+), 2 deletions(-)
diff --git a/hw/cxl/cxl-mailbox-utils.c b/hw/cxl/cxl-mailbox-utils.c
index bc00fec9f8..cbd6ebf9fe 100644
--- a/hw/cxl/cxl-mailbox-utils.c
+++ b/hw/cxl/cxl-mailbox-utils.c
@@ -2636,6 +2636,12 @@ static int sanitize_range(CXLType3Dev *ct3d, uint64_t dpa_addr, size_t length,
return address_space_set(as, dpa_addr, fill_value, length, mem_attrs);
}
+static void cxl_discard_media_op_sanitize(CXLType3Dev *ct3d)
+{
+ g_free(ct3d->media_op_sanitize);
+ ct3d->media_op_sanitize = NULL;
+}
+
/* Perform the actual device zeroing */
static void __do_sanitize(CXLType3Dev *ct3d)
{
@@ -2653,8 +2659,7 @@ static void __do_sanitize(CXLType3Dev *ct3d)
}
}
exit:
- g_free(ct3d->media_op_sanitize);
- ct3d->media_op_sanitize = NULL;
+ cxl_discard_media_op_sanitize(ct3d);
return;
}
@@ -4848,6 +4853,12 @@ void cxl_destroy_mailbox_t3(CXLType3Dev *ct3d)
if (ct3d->cci.initialized) {
cxl_destroy_cci(&ct3d->cci);
}
+ /*
+ * An in-flight Media Operations sanitize is only advanced by this CCI's
+ * background timer; with the CCI gone the operation can never complete,
+ * so its state would otherwise be leaked (CXL r4.0 Section 8.2.9.4).
+ */
+ cxl_discard_media_op_sanitize(ct3d);
}
static const struct cxl_cmd cxl_cmd_set_t3_ld[256][256] = {
--
MST
^ permalink raw reply related [flat|nested] 84+ messages in thread
* [PULL 38/78] hw/cxl: clear event logs, scan media and interrupt policy on reset
2026-10-28 17:13 [PULL 00/78] pci, vhost, virtio, iommu, acpi: features, fixes Michael S. Tsirkin
` (36 preceding siblings ...)
2026-10-28 17:15 ` [PULL 37/78] hw/cxl: discard in-flight sanitize state on mailbox teardown Michael S. Tsirkin
@ 2026-10-28 17:15 ` Michael S. Tsirkin
2026-10-28 17:15 ` [PULL 39/78] hw/cxl: clear poison lists and feature transfer state " Michael S. Tsirkin
` (40 subsequent siblings)
78 siblings, 0 replies; 84+ messages in thread
From: Michael S. Tsirkin @ 2026-10-28 17:15 UTC (permalink / raw)
To: qemu-devel
Cc: Peter Maydell, Junjie Cao, Jonathan Cameron, Junjie Cao, Anisa Su,
linux-cxl
From: Junjie Cao <junjie.cao@intel.com>
Event records, scan media results and event interrupt settings are
device-internal dynamic state that should not survive a device reset.
Per CXL r4.0 Section 8.2.10.9.4.6 (Get Scan Media Results), "If the
Scan Media command has not been called since the last Conventional
Reset, the device shall return the Unsupported return code." This
explicitly invalidates scan media results across reset, so clear the
scan_media_hasrun flag.
Per CXL r4.0 Section 8.2.10.2.5 (Set Event Interrupt Policy), "All
event log interrupt settings shall be reset to 00b (No Interrupts) by
the device on Conventional Reset." Clear irq_enabled for every event
log accordingly.
For the event records there is no direct spec mandate to drop the
stored records on reset; the Event Status register (Table 8-203) is
non-sticky and resets to zero per Section 9.7, and no reset flavor
requires the already-reported records to persist. Draining the queues
is therefore a reasonable modelling choice that keeps the records
consistent with the freshly-reset status register, rather than a spec
requirement. Call the existing cxl_discard_all_event_records() helper
to drain all event queues. The event log infrastructure itself
(mutexes, IRQ vectors) remains intact as it is initialized once during
device realize.
This is also where reset-type gating begins: a wakeup from suspend-to-RAM
(RESET_TYPE_WAKEUP) is not a Conventional or CXL Reset and must retain
device-internal state, so the hold phase returns early for that type
before discarding any records. This mirrors the RESET_TYPE_WAKEUP
shortcut in virtio-mem and virtio-balloon. Only the unconditional
mailbox interface re-initialization and the in-flight sanitize free
(whose backing timer was already destroyed) run on a wakeup.
Signed-off-by: Junjie Cao <junjie.cao@intel.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Message-ID: <20260729144645.1552511-7-junjie.cao@intel.com>
---
hw/mem/cxl_type3.c | 17 +++++++++++++++++
1 file changed, 17 insertions(+)
diff --git a/hw/mem/cxl_type3.c b/hw/mem/cxl_type3.c
index 04558a114f..26c3773dc0 100644
--- a/hw/mem/cxl_type3.c
+++ b/hw/mem/cxl_type3.c
@@ -1360,6 +1360,23 @@ static void ct3d_reset_hold(Object *obj, ResetType type)
}
cxl_initialize_t3_ld_cci(&ct3d->ld0_cci, DEVICE(ct3d), DEVICE(ct3d),
512); /* Max payload made up */
+
+ /*
+ * A wakeup from suspend-to-RAM is not a Conventional or CXL Reset. The
+ * device-internal dynamic state cleared below (event logs, scan media
+ * results, and the poison/feature-transfer state cleared in subsequent
+ * patches) must be preserved across resume, so stop here for a wakeup.
+ * virtio-mem and virtio-balloon take the same RESET_TYPE_WAKEUP shortcut.
+ */
+ if (type == RESET_TYPE_WAKEUP) {
+ return;
+ }
+
+ cxl_discard_all_event_records(&ct3d->cxl_dstate);
+ for (int i = 0; i < CXL_EVENT_TYPE_MAX; i++) {
+ ct3d->cxl_dstate.event_logs[i].irq_enabled = false;
+ }
+ ct3d->scan_media_hasrun = false;
}
static const Property ct3_props[] = {
--
MST
^ permalink raw reply related [flat|nested] 84+ messages in thread
* [PULL 39/78] hw/cxl: clear poison lists and feature transfer state on reset
2026-10-28 17:13 [PULL 00/78] pci, vhost, virtio, iommu, acpi: features, fixes Michael S. Tsirkin
` (37 preceding siblings ...)
2026-10-28 17:15 ` [PULL 38/78] hw/cxl: clear event logs, scan media and interrupt policy on reset Michael S. Tsirkin
@ 2026-10-28 17:15 ` Michael S. Tsirkin
2026-10-28 17:15 ` [PULL 40/78] pcie_sriov: register user created virtual function before realizing it Michael S. Tsirkin
` (39 subsequent siblings)
78 siblings, 0 replies; 84+ messages in thread
From: Michael S. Tsirkin @ 2026-10-28 17:15 UTC (permalink / raw)
To: qemu-devel
Cc: Peter Maydell, Junjie Cao, Jonathan Cameron, Junjie Cao, Anisa Su,
linux-cxl
From: Junjie Cao <junjie.cao@intel.com>
The poison lists (active, backup, and scan-media results), their
associated overflow tracking, and the Set Feature partial-transfer
state are all device-internal dynamic state that accumulates during
the device's lifetime.
Per CXL r4.0 Table 8-309 (Identify Memory Device), the "Injects
Persistent Poison" capability bit (offset 41h, Bit[0]) controls poison
retention across reset. When cleared — the QEMU default — "a
Conventional Reset or CXL Reset shall automatically clear the injected
poison." Clear all poison lists accordingly, reusing the existing
cxl_clear_poison_list_overflowed() helper for the overflow tracking.
For the Set Feature transfer state, CXL r4.0 Section 8.2.10.6.3 (Set
Feature) requires: "If the Feature data transfer is interrupted by a
Conventional Reset or a CXL Reset, the Feature data transfer shall be
aborted by the device [...] the device shall require the Feature data
transfer to be started from the beginning." Zero set_feat_info on
reset so any partially transferred Set Feature is abandoned and must
restart from the beginning.
Both clears run after the RESET_TYPE_WAKEUP early-return added in the
previous patch, so this state is preserved across a suspend-to-RAM
wakeup.
Signed-off-by: Junjie Cao <junjie.cao@intel.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Message-ID: <20260729144645.1552511-8-junjie.cao@intel.com>
---
hw/mem/cxl_type3.c | 18 ++++++++++++++++++
1 file changed, 18 insertions(+)
diff --git a/hw/mem/cxl_type3.c b/hw/mem/cxl_type3.c
index 26c3773dc0..1ff01121f8 100644
--- a/hw/mem/cxl_type3.c
+++ b/hw/mem/cxl_type3.c
@@ -1327,6 +1327,16 @@ MemTxResult cxl_type3_write(PCIDevice *d, hwaddr host_addr, uint64_t data,
return address_space_write(as, dpa_offset, attrs, &data, size);
}
+static void ct3d_clear_poison_list(CXLPoisonList *list)
+{
+ CXLPoison *ent, *next;
+
+ QLIST_FOREACH_SAFE(ent, list, node, next) {
+ QLIST_REMOVE(ent, node);
+ g_free(ent);
+ }
+}
+
static void ct3d_reset_hold(Object *obj, ResetType type)
{
CXLType3Dev *ct3d = CXL_TYPE3(obj);
@@ -1377,6 +1387,14 @@ static void ct3d_reset_hold(Object *obj, ResetType type)
ct3d->cxl_dstate.event_logs[i].irq_enabled = false;
}
ct3d->scan_media_hasrun = false;
+
+ ct3d_clear_poison_list(&ct3d->poison_list);
+ ct3d_clear_poison_list(&ct3d->poison_list_bkp);
+ ct3d_clear_poison_list(&ct3d->scan_media_results);
+ ct3d->poison_list_cnt = 0;
+ cxl_clear_poison_list_overflowed(ct3d);
+
+ memset(&ct3d->set_feat_info, 0, sizeof(ct3d->set_feat_info));
}
static const Property ct3_props[] = {
--
MST
^ permalink raw reply related [flat|nested] 84+ messages in thread
* [PULL 40/78] pcie_sriov: register user created virtual function before realizing it
2026-10-28 17:13 [PULL 00/78] pci, vhost, virtio, iommu, acpi: features, fixes Michael S. Tsirkin
` (38 preceding siblings ...)
2026-10-28 17:15 ` [PULL 39/78] hw/cxl: clear poison lists and feature transfer state " Michael S. Tsirkin
@ 2026-10-28 17:15 ` Michael S. Tsirkin
2026-10-28 17:34 ` Daniel Paziyski
2026-10-28 17:15 ` [PULL 41/78] hw/pci-bridge: Add generic PCIe switch upstream and downstream ports Michael S. Tsirkin
` (38 subsequent siblings)
78 siblings, 1 reply; 84+ messages in thread
From: Michael S. Tsirkin @ 2026-10-28 17:15 UTC (permalink / raw)
To: qemu-devel; +Cc: Peter Maydell, Daniel Paziyski, qemu-stable
From: Daniel Paziyski <danielpaziyski@gmail.com>
There are two ways of creating virtual functions: by using the sriov-pf device
parameter (this way, creating a user created VF and binding it to a PF), or
by using a device specific parameter, where the device manually creates a given
amount of VFs.
When a PCI device is realized, the device specific realize function is called
first, and then the pcie_sriov_register_device function is called, which checks
whether the device that has been created is a user created VF, and if it is the
case and the device allows this kind of VFs, it inserts it into a hashmap, with
the key being the ID of the PF, and the items being arrays of VFs.
User created VFs are instantiated independently, and later, when the PF calls
pcie_sriov_pf_init_from_user_created_vfs, it discovers its VFs from the hashmap
mentioned previously, and sets up in their PCIDevice.ex.sriov_pf structure
the pointer to the PF. This means that, during realization, VFs have no access
to the PF.
Device created VFs are instantiated during or after PF realization using the
pcie_sriov_pf_init function, which sets up for the VFs the pointer to the PF
before their realization, so when they're realized, they can access the PF
with no issues.
The problem here is that pcie_sriov_register_device is called after the
realization, and not before. This means that when a user created VF is created
for a device which does not support user created VFs, but supports device
created VFs, the realize function will notice that a VF is being created, and
may try to access the PF, causing a null pointer dereference fault.
Fix this by placing the user created VF check and registering before the
realization. This way, incorrectly created user created VFs will be noticed,
and device creation will be aborted.
Additionally, remove from the pcie_sriov_register_device function the top
check. It seems that this check is done to error out if the PF failed for some
reason to initialize its list of user created VFs. However, in such situations,
the pcie_sriov_pf_init_from_user_created_vfs function will error during
realization, and it will be caught before the check is done at all. Moreover,
since now pcie_sriov_register_device is called before realization, the check
will always fail for PFs with user created VFs, because the list will be
populated during realization.
The rest of the function though, correctly errors out if a user created VF
is created for an unsupported device type, if a VF is created for a non-PCIe
device, or if the PF is already instantiated, with now the advantage being
that the check is done before the VF instantiation.
Moreover, move the unregistering of a device's acpi-index and the removal
of a VF from its PF list from pci_qdev_unrealize to do_pci_unregister_device.
We do the former so that the acpi-index is unclaimed on user created VF
creation error, and the latter to not have extra code on the device specific
realization error path.
When instantiating a user created VF for a NVME controller:
Command line:
qemu-system-x86_64 -device nvme-subsys,id=subsys0 \
-device nvme,id=vctrl0,sriov-pf=ctrl0,subsys=subsys0 \
-device nvme,id=ctrl0,subsys=subsys0,serial=s
ASAN splat:
../hw/nvme/ctrl.c:9613:28: runtime error: member access within null pointer of type 'struct NvmeCtrl'
AddressSanitizer:DEADLYSIGNAL
=================================================================
==91050==ERROR: AddressSanitizer: SEGV on unknown address 0x000000001cf0 (pc 0x7f79a8573dcd bp 0x7ffd622c1bc0 sp 0x7ffd622c1b68 T0)
==91050==The signal is caused by a READ memory access.
#0 0x7f79a8573dcd (/usr/lib/libc.so.6+0x173dcd) (BuildId: 1fa174a830cef40a5b2388add4318ee2795f573e)
#1 0x5644c6c0266e in nvme_realize ../hw/nvme/ctrl.c:9613
#2 0x5644c6c67213 in pci_qdev_realize ../hw/pci/pci.c:2316
#3 0x5644c7a2cfc1 in device_set_realized ../hw/core/qdev.c:514
#4 0x5644c7a4f767 in property_set_bool ../qom/object.c:2484
#5 0x5644c7a48d0b in object_property_set ../qom/object.c:1548
#6 0x5644c7a568a5 in object_property_set_qobject ../qom/qom-qobject.c:28
#7 0x5644c7a49385 in object_property_set_bool ../qom/object.c:1618
#8 0x5644c7a2aeb0 in qdev_realize ../hw/core/qdev.c:277
#9 0x5644c735f29f in qdev_device_add_from_qdict ../system/qdev-monitor.c:740
#10 0x5644c735f3ab in qdev_device_add ../system/qdev-monitor.c:758
#11 0x5644c72b1901 in device_init_func ../system/vl.c:1217
#12 0x5644c829d4a3 in qemu_opts_foreach ../util/qemu-option.c:1148
#13 0x5644c72bc33e in qemu_create_cli_devices ../system/vl.c:2762
#14 0x5644c72bcaa1 in qmp_x_exit_preconfig ../system/vl.c:2822
#15 0x5644c72c3241 in qemu_init ../system/vl.c:3862
#16 0x5644c801abf8 in main ../system/main.c:71
#17 0x7f79a8427780 (/usr/lib/libc.so.6+0x27780) (BuildId: 1fa174a830cef40a5b2388add4318ee2795f573e)
#18 0x7f79a84278b8 in __libc_start_main (/usr/lib/libc.so.6+0x278b8) (BuildId: 1fa174a830cef40a5b2388add4318ee2795f573e)
#19 0x5644c5f0a1f4 in _start (BuildId: 8483f952216d9e345c3744300d0aefa38feb50d7)
==91050==Register values:
rax = 0x00007e49988640f0 rbx = 0x00007e49988640f0 rcx = 0x00000fc9b3104828 rdx = 0x0000000000000058
rdi = 0x00007e49988640f0 rsi = 0x0000000000001cf0 rbp = 0x00007ffd622c1bc0 rsp = 0x00007ffd622c1b68
r8 = 0x00000fc9b3104829 r9 = 0x00000fc9b3104828 r10 = 0x00000fc9b310481e r11 = 0x00000fc9b310481e
r12 = 0x0000000000001cf0 r13 = 0x00000f6f32e78578 r14 = 0x00000000ffffffff r15 = 0x00007ffd622c1c10
AddressSanitizer can not provide additional info.
SUMMARY: AddressSanitizer: SEGV (/usr/lib/libc.so.6+0x173dcd) (BuildId: 1fa174a830cef40a5b2388add4318ee2795f573e)
==91050==ABORTING
Cc: qemu-stable@nongnu.org
Fixes: 19e55471d4e8 ("pcie_sriov: Allow user to create SR-IOV device")
Signed-off-by: Daniel Paziyski <danielpaziyski@gmail.com>
Message-ID: <20260905122924.53813-1-danielpaziyski@gmail.com>
---
hw/pci/pci.c | 42 ++++++++++++++++++++++++------------------
hw/pci/pcie_sriov.c | 6 ------
2 files changed, 24 insertions(+), 24 deletions(-)
diff --git a/hw/pci/pci.c b/hw/pci/pci.c
index 0efb4eb4bb..2d2fe02c68 100644
--- a/hw/pci/pci.c
+++ b/hw/pci/pci.c
@@ -1227,6 +1227,19 @@ static void pci_config_free(PCIDevice *pci_dev)
g_free(pci_dev->used);
}
+static void pci_unregister_acpi_index(PCIDevice *pci_dev)
+{
+ if (pci_dev->acpi_index) {
+ GSequence *used_indexes = pci_acpi_index_list();
+
+ g_sequence_remove(g_sequence_lookup(used_indexes,
+ GINT_TO_POINTER(pci_dev->acpi_index),
+ g_cmp_uint32, NULL));
+
+ pci_dev->acpi_index = 0;
+ }
+}
+
static void do_pci_unregister_device(PCIDevice *pci_dev)
{
pci_get_bus(pci_dev)->devices[pci_dev->devfn] = NULL;
@@ -1240,6 +1253,9 @@ static void do_pci_unregister_device(PCIDevice *pci_dev)
&pci_dev->bus_master_enable_region);
}
address_space_destroy(&pci_dev->bus_master_as);
+
+ pcie_sriov_unregister_device(pci_dev);
+ pci_unregister_acpi_index(pci_dev);
}
/* Extract PCIReqIDCache into BDF format */
@@ -1491,7 +1507,6 @@ static void pci_qdev_unrealize(DeviceState *dev)
pci_unregister_io_regions(pci_dev);
pci_del_option_rom(pci_dev);
- pcie_sriov_unregister_device(pci_dev);
if (pc->exit) {
pc->exit(pci_dev);
@@ -1501,17 +1516,6 @@ static void pci_qdev_unrealize(DeviceState *dev)
do_pci_unregister_device(pci_dev);
pci_dev->msi_trigger = NULL;
-
- /*
- * clean up acpi-index so it could reused by another device
- */
- if (pci_dev->acpi_index) {
- GSequence *used_indexes = pci_acpi_index_list();
-
- g_sequence_remove(g_sequence_lookup(used_indexes,
- GINT_TO_POINTER(pci_dev->acpi_index),
- g_cmp_uint32, NULL));
- }
}
void pci_register_bar(PCIDevice *pci_dev, int region_num,
@@ -2326,8 +2330,15 @@ static void pci_qdev_realize(DeviceState *qdev, Error **errp)
pci_dev = do_pci_register_device(pci_dev,
object_get_typename(OBJECT(qdev)),
pci_dev->devfn, errp);
- if (pci_dev == NULL)
+ if (pci_dev == NULL) {
+ pci_unregister_acpi_index(pci_dev);
return;
+ }
+
+ if (!pcie_sriov_register_device(pci_dev, errp)) {
+ do_pci_unregister_device(pci_dev);
+ return;
+ }
if (pc->realize) {
pc->realize(pci_dev, &local_err);
@@ -2338,11 +2349,6 @@ static void pci_qdev_realize(DeviceState *qdev, Error **errp)
}
}
- if (!pcie_sriov_register_device(pci_dev, errp)) {
- pci_qdev_unrealize(DEVICE(pci_dev));
- return;
- }
-
/*
* A PCIe Downstream Port that do not have ARI Forwarding enabled must
* associate only Device 0 with the device attached to the bus
diff --git a/hw/pci/pcie_sriov.c b/hw/pci/pcie_sriov.c
index c41ac95bee..69930c7b8c 100644
--- a/hw/pci/pcie_sriov.c
+++ b/hw/pci/pcie_sriov.c
@@ -357,12 +357,6 @@ int16_t pcie_sriov_pf_init_from_user_created_vfs(PCIDevice *dev,
bool pcie_sriov_register_device(PCIDevice *dev, Error **errp)
{
- if (!dev->exp.sriov_pf.vf && dev->qdev.id &&
- pfs && g_hash_table_contains(pfs, dev->qdev.id)) {
- error_setg(errp, "attaching user-created SR-IOV VF unsupported");
- return false;
- }
-
if (dev->sriov_pf) {
PCIDevice *pci_pf;
GPtrArray *pf;
--
MST
^ permalink raw reply related [flat|nested] 84+ messages in thread
* [PULL 41/78] hw/pci-bridge: Add generic PCIe switch upstream and downstream ports
2026-10-28 17:13 [PULL 00/78] pci, vhost, virtio, iommu, acpi: features, fixes Michael S. Tsirkin
` (39 preceding siblings ...)
2026-10-28 17:15 ` [PULL 40/78] pcie_sriov: register user created virtual function before realizing it Michael S. Tsirkin
@ 2026-10-28 17:15 ` Michael S. Tsirkin
2026-10-28 17:15 ` [PULL 42/78] hw/pci-bridge: Make speed and width for PCIe ports non-experimental Michael S. Tsirkin
` (37 subsequent siblings)
78 siblings, 0 replies; 84+ messages in thread
From: Michael S. Tsirkin @ 2026-10-28 17:15 UTC (permalink / raw)
To: qemu-devel
Cc: Peter Maydell, David Vrabel, Pierrick Bouvier, Paolo Bonzini,
Zhao Liu
From: David Vrabel <david.vrabel@nutanix.com>
The TI XIO3130 PCIe switch is a vintage Gen1 device, so we add generic
PCIe upstream and downstream port devices that are Gen6.
In guests with pairs of passthrough PCIe devices (e.g, a GPU+NIC pair)
often need to place the devices behind a common (emulated) PCIe switch
so in-guest software (such as NCCL) can correctly determine that P2P
transactions between the devices is possible.
NCCL also needs to be able to determine the available PCIe
bandwidth. If such a guest sees the TI XIO3130 PCIe switch, then it
incorrectly calculates only 2.5 GT/s and x1 is available, but the real
hardware might be 32 GT/s and x16.
Instead of enhancing the existing switch and claiming it is more
capable than the real hardware we add additional generic PCIe upstream
and downstream port devices, which matches the existing generic PCIe
root port device.
The speed and width of the ports are configurable.
Signed-off-by: David Vrabel <david.vrabel@nutanix.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Message-ID: <20260928152431.310363-2-david.vrabel@nutanix.com>
---
docs/pcie.txt | 6 +-
docs/specs/pci-ids.rst | 4 +
include/hw/pci/pci.h | 2 +
hw/pci-bridge/gen_pcie_downstream_port.c | 202 ++++++++++++++++++++
hw/pci-bridge/gen_pcie_upstream_port.c | 177 +++++++++++++++++
hw/pci-bridge/meson.build | 3 +-
tests/functional/x86_64/meson.build | 1 +
tests/functional/x86_64/test_pcie_switch.py | 98 ++++++++++
8 files changed, 489 insertions(+), 4 deletions(-)
create mode 100644 hw/pci-bridge/gen_pcie_downstream_port.c
create mode 100644 hw/pci-bridge/gen_pcie_upstream_port.c
create mode 100755 tests/functional/x86_64/test_pcie_switch.py
diff --git a/docs/pcie.txt b/docs/pcie.txt
index df49178311..f12a9daaad 100644
--- a/docs/pcie.txt
+++ b/docs/pcie.txt
@@ -85,7 +85,7 @@ number of PCI Express Root Ports per PCI Express Root Bus is 256.
Prefer grouping PCI Express Root Ports into multi-function devices
to keep a simple flat hierarchy that is enough for most scenarios.
-Only use PCI Express Switches (x3130-upstream, xio3130-downstream)
+Only use PCI Express Switches (pcie-upstream-port, pcie-downstream-port)
if there is no more room for PCI Express Root Ports.
Please see section 4. for further justifications.
@@ -120,8 +120,8 @@ Plug only PCI Express devices into PCI Express Ports.
-device pcie-root-port,id=root_port3,chassis=x2,addr=z.2[,slot=y2][,bus=pcie.0] \
2.2.3 Plugging a PCI Express device into a Switch:
-device pcie-root-port,id=root_port1,chassis=x,slot=y[,bus=pcie.0][,addr=z] \
- -device x3130-upstream,id=upstream_port1,bus=root_port1[,addr=x] \
- -device xio3130-downstream,id=downstream_port1,bus=upstream_port1,chassis=x1,slot=y1[,addr=z1]] \
+ -device pcie-upstream-port,id=upstream_port1,bus=root_port1[,addr=x] \
+ -device pcie-downstream-port,id=downstream_port1,bus=upstream_port1,chassis=x1,slot=y1[,addr=z1]] \
-device <dev>,bus=downstream_port1
Notes:
diff --git a/docs/specs/pci-ids.rst b/docs/specs/pci-ids.rst
index 261b0f359f..60cb0f7db6 100644
--- a/docs/specs/pci-ids.rst
+++ b/docs/specs/pci-ids.rst
@@ -100,6 +100,10 @@ PCI devices (other than virtio):
PCI UFS device (``-device ufs``)
1b36:0014
PCI RISC-V IOMMU device
+1b36:0015
+ PCIe Upstream Switch Port (``-device pcie-upstream-port``)
+1b36:0016
+ PCIe Downstream Switch Port (``-device pcie-downstream-port``)
All these devices are documented in :doc:`index`.
diff --git a/include/hw/pci/pci.h b/include/hw/pci/pci.h
index f2448e941a..6520886855 100644
--- a/include/hw/pci/pci.h
+++ b/include/hw/pci/pci.h
@@ -121,6 +121,8 @@ extern bool pci_available;
#define PCI_DEVICE_ID_REDHAT_ACPI_ERST 0x0012
#define PCI_DEVICE_ID_REDHAT_UFS 0x0013
#define PCI_DEVICE_ID_REDHAT_RISCV_IOMMU 0x0014
+#define PCI_DEVICE_ID_REDHAT_PCIE_USP 0x0015
+#define PCI_DEVICE_ID_REDHAT_PCIE_DSP 0x0016
#define PCI_DEVICE_ID_REDHAT_QXL 0x0100
#define FMT_PCIBUS PRIx64
diff --git a/hw/pci-bridge/gen_pcie_downstream_port.c b/hw/pci-bridge/gen_pcie_downstream_port.c
new file mode 100644
index 0000000000..d9c182e881
--- /dev/null
+++ b/hw/pci-bridge/gen_pcie_downstream_port.c
@@ -0,0 +1,202 @@
+/*
+ * Generic PCI Express Downstream Port emulation
+ *
+ * Copyright (C) 2026 Nutanix, Inc.
+ *
+ * Derived from gen_pcie_root_port.c:
+ * Copyright (C) 2017 Red Hat Inc
+ *
+ * Derived from xio3130_downstream.c:
+ * Copyright (c) 2010 Isaku Yamahata <yamahata at valinux co jp>
+ * VA Linux Systems Japan K.K.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "qemu/module.h"
+#include "hw/pci/msix.h"
+#include "hw/pci/pcie.h"
+#include "hw/pci/pcie_port.h"
+#include "hw/core/qdev-properties.h"
+#include "hw/core/qdev-properties-system.h"
+#include "migration/vmstate.h"
+#include "qom/object.h"
+
+#define TYPE_GEN_PCIE_DOWNSTREAM_PORT "pcie-downstream-port"
+OBJECT_DECLARE_SIMPLE_TYPE(GenPCIEDownstreamPort, GEN_PCIE_DOWNSTREAM_PORT)
+
+struct GenPCIEDownstreamPort {
+ PCIESlot parent_obj;
+};
+
+#define GEN_PCIE_DOWNSTREAM_PORT_AER_OFFSET 0x100
+#define GEN_PCIE_DOWNSTREAM_PORT_ACS_OFFSET \
+ (GEN_PCIE_DOWNSTREAM_PORT_AER_OFFSET + PCI_ERR_SIZEOF)
+#define GEN_PCIE_DOWNSTREAM_PORT_MSIX_NR_VECTOR 1
+
+
+static int gen_downstream_interrupts_init(PCIDevice *d, Error **errp)
+{
+ int rc;
+
+ rc = msix_init_exclusive_bar(d, GEN_PCIE_DOWNSTREAM_PORT_MSIX_NR_VECTOR,
+ 0, errp);
+ if (rc < 0) {
+ assert(rc == -ENOTSUP);
+ } else {
+ msix_vector_use(d, 0);
+ }
+
+ return rc;
+}
+
+static void gen_downstream_interrupts_uninit(PCIDevice *d)
+{
+ msix_uninit_exclusive_bar(d);
+}
+
+static void gen_downstream_write_config(PCIDevice *d, uint32_t address,
+ uint32_t val, int len)
+{
+ uint16_t slt_ctl, slt_sta;
+
+ pcie_cap_slot_get(d, &slt_ctl, &slt_sta);
+ pci_bridge_write_config(d, address, val, len);
+ pcie_cap_flr_write_config(d, address, val, len);
+ pcie_cap_slot_write_config(d, slt_ctl, slt_sta, address, val, len);
+ pcie_aer_write_config(d, address, val, len);
+}
+
+static void gen_downstream_reset(DeviceState *qdev)
+{
+ PCIDevice *d = PCI_DEVICE(qdev);
+
+ pcie_cap_deverr_reset(d);
+ pcie_cap_slot_reset(d);
+ pcie_cap_arifwd_reset(d);
+ pci_bridge_reset(qdev);
+}
+
+static void gen_downstream_realize(PCIDevice *d, Error **errp)
+{
+ PCIEPort *p = PCIE_PORT(d);
+ PCIESlot *s = PCIE_SLOT(d);
+ int rc;
+
+ pci_bridge_initfn(d, TYPE_PCIE_BUS);
+ pcie_port_init_reg(d);
+
+ rc = gen_downstream_interrupts_init(d, errp);
+ if (rc < 0) {
+ goto err_bridge;
+ }
+
+ rc = pcie_cap_init(d, 0x90, PCI_EXP_TYPE_DOWNSTREAM, p->port, errp);
+ if (rc < 0) {
+ goto err_int;
+ }
+ pcie_cap_flr_init(d);
+ pcie_cap_deverr_init(d);
+ pcie_cap_slot_init(d, s);
+ pcie_cap_arifwd_init(d);
+
+ pcie_chassis_create(s->chassis);
+ rc = pcie_chassis_add_slot(s);
+ if (rc < 0) {
+ error_setg(errp, "Can't add chassis slot, error %d", rc);
+ goto err_cap;
+ }
+
+ rc = pcie_aer_init(d, PCI_ERR_VER, GEN_PCIE_DOWNSTREAM_PORT_AER_OFFSET,
+ PCI_ERR_SIZEOF, errp);
+ if (rc < 0) {
+ goto err_chassis;
+ }
+
+ pcie_acs_init(d, GEN_PCIE_DOWNSTREAM_PORT_ACS_OFFSET);
+
+ return;
+
+err_chassis:
+ pcie_chassis_del_slot(s);
+err_cap:
+ pcie_cap_exit(d);
+err_int:
+ gen_downstream_interrupts_uninit(d);
+err_bridge:
+ pci_bridge_exitfn(d);
+}
+
+static void gen_downstream_exitfn(PCIDevice *d)
+{
+ PCIESlot *s = PCIE_SLOT(d);
+
+ pcie_aer_exit(d);
+ pcie_chassis_del_slot(s);
+ pcie_cap_exit(d);
+ gen_downstream_interrupts_uninit(d);
+ pci_bridge_exitfn(d);
+}
+
+static const VMStateDescription vmstate_gen_pcie_downstream_port = {
+ .name = "pcie-downstream-port",
+ .priority = MIG_PRI_PCI_BUS,
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .post_load = pcie_cap_slot_post_load,
+ .fields = (const VMStateField[]) {
+ VMSTATE_PCI_DEVICE(parent_obj.parent_obj.parent_obj, PCIESlot),
+ VMSTATE_STRUCT(parent_obj.parent_obj.parent_obj.exp.aer_log,
+ PCIESlot, 0, vmstate_pcie_aer_log, PCIEAERLog),
+ VMSTATE_MSIX(parent_obj.parent_obj.parent_obj.parent_obj,
+ GenPCIEDownstreamPort),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static const Property gen_pcie_downstream_port_props[] = {
+ DEFINE_PROP_BIT(COMPAT_PROP_PCP, PCIDevice, cap_present,
+ QEMU_PCIE_SLTCAP_PCP_BITNR, true),
+ DEFINE_PROP_PCIE_LINK_SPEED("x-speed", PCIESlot,
+ speed, PCIE_LINK_SPEED_64),
+ DEFINE_PROP_PCIE_LINK_WIDTH("x-width", PCIESlot,
+ width, PCIE_LINK_WIDTH_32),
+};
+
+static void gen_pcie_downstream_port_class_init(ObjectClass *klass,
+ const void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
+
+ k->config_write = gen_downstream_write_config;
+ k->realize = gen_downstream_realize;
+ k->exit = gen_downstream_exitfn;
+ k->vendor_id = PCI_VENDOR_ID_REDHAT;
+ k->device_id = PCI_DEVICE_ID_REDHAT_PCIE_DSP;
+ set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories);
+ dc->desc = "PCI Express Downstream Port";
+ device_class_set_legacy_reset(dc, gen_downstream_reset);
+ dc->vmsd = &vmstate_gen_pcie_downstream_port;
+ device_class_set_props(dc, gen_pcie_downstream_port_props);
+}
+
+static const TypeInfo gen_pcie_downstream_port_info = {
+ .name = TYPE_GEN_PCIE_DOWNSTREAM_PORT,
+ .parent = TYPE_PCIE_SLOT,
+ .instance_size = sizeof(GenPCIEDownstreamPort),
+ .class_init = gen_pcie_downstream_port_class_init,
+ .interfaces = (const InterfaceInfo[]) {
+ { INTERFACE_PCIE_DEVICE },
+ { }
+ },
+};
+
+static void gen_pcie_downstream_port_register_types(void)
+{
+ type_register_static(&gen_pcie_downstream_port_info);
+}
+
+type_init(gen_pcie_downstream_port_register_types)
diff --git a/hw/pci-bridge/gen_pcie_upstream_port.c b/hw/pci-bridge/gen_pcie_upstream_port.c
new file mode 100644
index 0000000000..e86eff099f
--- /dev/null
+++ b/hw/pci-bridge/gen_pcie_upstream_port.c
@@ -0,0 +1,177 @@
+/*
+ * Generic PCI Express Upstream Port emulation
+ *
+ * Copyright (C) 2026 Nutanix, Inc.
+ *
+ * Derived from gen_pcie_root_port.c:
+ * Copyright (C) 2017 Red Hat Inc
+ *
+ * Derived from xio3130_upstream.c:
+ * Copyright (c) 2010 Isaku Yamahata <yamahata at valinux co jp>
+ * VA Linux Systems Japan K.K.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "qemu/module.h"
+#include "hw/pci/msix.h"
+#include "hw/pci/pcie.h"
+#include "hw/pci/pcie_port.h"
+#include "hw/core/qdev-properties.h"
+#include "hw/core/qdev-properties-system.h"
+#include "migration/vmstate.h"
+#include "qom/object.h"
+
+#define TYPE_GEN_PCIE_UPSTREAM_PORT "pcie-upstream-port"
+OBJECT_DECLARE_SIMPLE_TYPE(GenPCIEUpstreamPort, GEN_PCIE_UPSTREAM_PORT)
+
+struct GenPCIEUpstreamPort {
+ PCIEPort parent_obj;
+
+ PCIExpLinkSpeed speed;
+ PCIExpLinkWidth width;
+};
+
+#define GEN_PCIE_UPSTREAM_PORT_AER_OFFSET 0x100
+#define GEN_PCIE_UPSTREAM_PORT_MSIX_NR_VECTOR 1
+
+static int gen_upstream_interrupts_init(PCIDevice *d, Error **errp)
+{
+ int rc;
+
+ rc = msix_init_exclusive_bar(d, GEN_PCIE_UPSTREAM_PORT_MSIX_NR_VECTOR,
+ 0, errp);
+ if (rc < 0) {
+ assert(rc == -ENOTSUP);
+ } else {
+ msix_vector_use(d, 0);
+ }
+
+ return rc;
+}
+
+static void gen_upstream_interrupts_uninit(PCIDevice *d)
+{
+ msix_uninit_exclusive_bar(d);
+}
+
+static void gen_upstream_write_config(PCIDevice *d, uint32_t address,
+ uint32_t val, int len)
+{
+ pci_bridge_write_config(d, address, val, len);
+ pcie_cap_flr_write_config(d, address, val, len);
+ pcie_aer_write_config(d, address, val, len);
+}
+
+static void gen_upstream_reset(DeviceState *qdev)
+{
+ PCIDevice *d = PCI_DEVICE(qdev);
+ GenPCIEUpstreamPort *usp = GEN_PCIE_UPSTREAM_PORT(d);
+
+ pci_bridge_reset(qdev);
+ pcie_cap_deverr_reset(d);
+ pcie_cap_fill_link_ep_usp(d, usp->width, usp->speed, false);
+}
+
+static void gen_upstream_realize(PCIDevice *d, Error **errp)
+{
+ PCIEPort *p = PCIE_PORT(d);
+ int rc;
+
+ pci_bridge_initfn(d, TYPE_PCIE_BUS);
+ pcie_port_init_reg(d);
+
+ rc = gen_upstream_interrupts_init(d, errp);
+ if (rc < 0) {
+ goto err_bridge;
+ }
+
+ rc = pcie_cap_init(d, 0x90, PCI_EXP_TYPE_UPSTREAM, p->port, errp);
+ if (rc < 0) {
+ goto err_int;
+ }
+ pcie_cap_flr_init(d);
+ pcie_cap_deverr_init(d);
+
+ rc = pcie_aer_init(d, PCI_ERR_VER, GEN_PCIE_UPSTREAM_PORT_AER_OFFSET,
+ PCI_ERR_SIZEOF, errp);
+ if (rc < 0) {
+ goto err_cap;
+ }
+
+ return;
+
+err_cap:
+ pcie_cap_exit(d);
+err_int:
+ gen_upstream_interrupts_uninit(d);
+err_bridge:
+ pci_bridge_exitfn(d);
+}
+
+static void gen_upstream_exitfn(PCIDevice *d)
+{
+ pcie_aer_exit(d);
+ pcie_cap_exit(d);
+ gen_upstream_interrupts_uninit(d);
+ pci_bridge_exitfn(d);
+}
+
+static const VMStateDescription vmstate_gen_pcie_upstream_port = {
+ .name = "pcie-upstream-port",
+ .priority = MIG_PRI_PCI_BUS,
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (const VMStateField[]) {
+ VMSTATE_PCI_DEVICE(parent_obj.parent_obj, PCIEPort),
+ VMSTATE_STRUCT(parent_obj.parent_obj.exp.aer_log, PCIEPort, 0,
+ vmstate_pcie_aer_log, PCIEAERLog),
+ VMSTATE_MSIX(parent_obj.parent_obj.parent_obj, GenPCIEUpstreamPort),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static const Property gen_pcie_upstream_port_props[] = {
+ DEFINE_PROP_PCIE_LINK_SPEED("x-speed", GenPCIEUpstreamPort,
+ speed, PCIE_LINK_SPEED_64),
+ DEFINE_PROP_PCIE_LINK_WIDTH("x-width", GenPCIEUpstreamPort,
+ width, PCIE_LINK_WIDTH_32),
+};
+
+static void gen_pcie_upstream_port_class_init(ObjectClass *klass,
+ const void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
+
+ k->config_write = gen_upstream_write_config;
+ k->realize = gen_upstream_realize;
+ k->exit = gen_upstream_exitfn;
+ k->vendor_id = PCI_VENDOR_ID_REDHAT;
+ k->device_id = PCI_DEVICE_ID_REDHAT_PCIE_USP;
+ set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories);
+ dc->desc = "PCI Express Upstream Port";
+ device_class_set_legacy_reset(dc, gen_upstream_reset);
+ dc->vmsd = &vmstate_gen_pcie_upstream_port;
+ device_class_set_props(dc, gen_pcie_upstream_port_props);
+}
+
+static const TypeInfo gen_pcie_upstream_port_info = {
+ .name = TYPE_GEN_PCIE_UPSTREAM_PORT,
+ .parent = TYPE_PCIE_PORT,
+ .instance_size = sizeof(GenPCIEUpstreamPort),
+ .class_init = gen_pcie_upstream_port_class_init,
+ .interfaces = (const InterfaceInfo[]) {
+ { INTERFACE_PCIE_DEVICE },
+ { }
+ },
+};
+
+static void gen_pcie_upstream_port_register_types(void)
+{
+ type_register_static(&gen_pcie_upstream_port_info);
+}
+
+type_init(gen_pcie_upstream_port_register_types)
diff --git a/hw/pci-bridge/meson.build b/hw/pci-bridge/meson.build
index 073aeacf7f..aa9959df04 100644
--- a/hw/pci-bridge/meson.build
+++ b/hw/pci-bridge/meson.build
@@ -2,7 +2,8 @@ pci_ss = ss.source_set()
pci_ss.add(when: 'CONFIG_PCI_BRIDGE', if_true: files('pci_bridge_dev.c'))
pci_ss.add(when: 'CONFIG_I82801B11', if_true: files('i82801b11.c'))
pci_ss.add(when: 'CONFIG_IOH3420', if_true: files('ioh3420.c'))
-pci_ss.add(when: 'CONFIG_PCIE_PORT', if_true: files('pcie_root_port.c', 'gen_pcie_root_port.c'))
+pci_ss.add(when: 'CONFIG_PCIE_PORT', if_true: files('pcie_root_port.c', 'gen_pcie_root_port.c',
+ 'gen_pcie_upstream_port.c', 'gen_pcie_downstream_port.c'))
pci_ss.add(when: 'CONFIG_PCIE_PCI_BRIDGE', if_true: files('pcie_pci_bridge.c'))
pci_ss.add(when: 'CONFIG_PXB', if_true: files('pci_expander_bridge.c'))
stub_ss.add(files('pci_expander_bridge_stubs.c'))
diff --git a/tests/functional/x86_64/meson.build b/tests/functional/x86_64/meson.build
index bcdb1ee558..893214e8bb 100644
--- a/tests/functional/x86_64/meson.build
+++ b/tests/functional/x86_64/meson.build
@@ -44,6 +44,7 @@ tests_x86_64_system_thorough = [
'multiprocess',
'netdev_ethtool',
'nvme_migration',
+ 'pcie_switch',
'replay',
'reverse_debug',
'tuxrun',
diff --git a/tests/functional/x86_64/test_pcie_switch.py b/tests/functional/x86_64/test_pcie_switch.py
new file mode 100755
index 0000000000..7088b85059
--- /dev/null
+++ b/tests/functional/x86_64/test_pcie_switch.py
@@ -0,0 +1,98 @@
+#!/usr/bin/env python3
+#
+# Functional test for generic PCIe switch upstream and downstream ports
+#
+# Copyright (C) 2026 Nutanix, Inc.
+#
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+from qemu_test import LinuxKernelTest, Asset, exec_command_and_wait_for_pattern
+
+
+class PCIeSwitchPort(LinuxKernelTest):
+
+ timeout = 120
+
+ ASSET_KERNEL = Asset(
+ ('https://archives.fedoraproject.org/pub/archive/fedora/linux/releases'
+ '/31/Server/x86_64/os/images/pxeboot/vmlinuz'),
+ 'd4738d03dbbe083ca610d0821d0a8f1488bebbdccef54ce33e3adb35fda00129')
+
+ ASSET_INITRD = Asset(
+ ('https://archives.fedoraproject.org/pub/archive/fedora/linux/releases'
+ '/31/Server/x86_64/os/images/pxeboot/initrd.img'),
+ '277cd6c7adf77c7e63d73bbb2cded8ef9e2d3a2f100000e92ff1f8396513cd8b')
+
+ USP_VENDOR_ID = '0x1b36'
+ USP_DEVICE_ID = '0x0015'
+ DSP_VENDOR_ID = '0x1b36'
+ DSP_DEVICE_ID = '0x0016'
+ CLASS_BRIDGE_PCI_PCI = '0x060400'
+
+ def command(self, command: str, expected_result: int = 0):
+ command = f'echo __begin ; {command} ; D="done" ; echo "$?/__$D"'
+ output = exec_command_and_wait_for_pattern(self, command, '__done')
+ lines = output.decode().splitlines()
+ for s, line in enumerate(lines):
+ if line == '__begin':
+ break
+ result = int(lines[-1].split("/")[0])
+ assert result == expected_result
+ return "\n".join(lines[s+1:-1])
+
+ def test_pcie_switch_ports(self):
+ """Boot a q35 VM with a PCIe switch (USP + DSP) and verify topology."""
+ self.require_accelerator('kvm')
+ self.set_machine('q35')
+
+ self.vm.add_args('-accel', 'kvm')
+ self.vm.add_args('-m', '1G')
+ self.vm.add_args('-device',
+ 'pcie-root-port,id=rp0,slot=0,chassis=0,bus=pcie.0')
+ self.vm.add_args('-device',
+ 'pcie-upstream-port,id=sw-usp,bus=rp0')
+ self.vm.add_args('-device',
+ 'pcie-downstream-port,id=sw-dsp,bus=sw-usp,'
+ 'chassis=1')
+ self.vm.add_args('-append', 'console=ttyS0 rd.rescue')
+
+ self.launch_kernel(self.ASSET_KERNEL.fetch(),
+ self.ASSET_INITRD.fetch(),
+ wait_for='Entering emergency mode.')
+ self.wait_for_console_pattern('# ')
+
+ # Capture lspci output, to diagnose failures.
+ #
+ # [0000:00]-+-00.0 8086:29c0
+ # +-01.0 8086:10d3
+ # +-02.0-[01-03]----00.0-[02-03]----00.0-[03]--
+ # +-1f.0 8086:2918
+ # +-1f.2 8086:2922
+ # \-1f.3 8086:2930
+ self.command("lspci -vtn")
+ self.command("lspci -vvv")
+ self.command("ls -R /sys/devices/")
+
+ usp_path = "/sys/devices/pci0000:00/0000:00:02.0/0000:01:00.0"
+ dsp_path = usp_path + "/0000:02:00.0"
+
+ # Upstream port is expected model.
+ vendor_id = self.command(f'cat {usp_path}/vendor')
+ assert vendor_id == self.USP_VENDOR_ID
+
+ device_id = self.command(f'cat {usp_path}/device')
+ assert device_id == self.USP_DEVICE_ID
+
+ class_code = self.command(f'cat {usp_path}/class')
+ assert class_code == self.CLASS_BRIDGE_PCI_PCI
+
+ # Downstream port is expected model.
+ vendor_id = self.command(f'cat {dsp_path}/vendor')
+ assert vendor_id == self.DSP_VENDOR_ID
+
+ device_id = self.command(f'cat {dsp_path}/device')
+ assert device_id == self.DSP_DEVICE_ID
+
+
+if __name__ == '__main__':
+ LinuxKernelTest.main()
--
MST
^ permalink raw reply related [flat|nested] 84+ messages in thread
* [PULL 42/78] hw/pci-bridge: Make speed and width for PCIe ports non-experimental
2026-10-28 17:13 [PULL 00/78] pci, vhost, virtio, iommu, acpi: features, fixes Michael S. Tsirkin
` (40 preceding siblings ...)
2026-10-28 17:15 ` [PULL 41/78] hw/pci-bridge: Add generic PCIe switch upstream and downstream ports Michael S. Tsirkin
@ 2026-10-28 17:15 ` Michael S. Tsirkin
2026-10-28 17:15 ` [PULL 43/78] hw/pci-bridge: Default the generic PCIe root port as Gen6 capable Michael S. Tsirkin
` (36 subsequent siblings)
78 siblings, 0 replies; 84+ messages in thread
From: Michael S. Tsirkin @ 2026-10-28 17:15 UTC (permalink / raw)
To: qemu-devel; +Cc: Peter Maydell, David Vrabel
From: David Vrabel <david.vrabel@nutanix.com>
For guests with passthrough PCIe devices, in order to make the guest
PCIe topology have the same apparent bandwidth as the underlying
hardware we need to be able to set the capabilities (speed and width)
of the virtual root, upstream, and downstream ports.
Promote the existing experimental `x-speed` and `x-width` options to
non-experimental `speed` and `width`.
Signed-off-by: David Vrabel <david.vrabel@nutanix.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Message-ID: <20260928152431.310363-3-david.vrabel@nutanix.com>
---
hw/pci-bridge/gen_pcie_downstream_port.c | 4 ++--
hw/pci-bridge/gen_pcie_root_port.c | 4 ++--
hw/pci-bridge/gen_pcie_upstream_port.c | 4 ++--
3 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/hw/pci-bridge/gen_pcie_downstream_port.c b/hw/pci-bridge/gen_pcie_downstream_port.c
index d9c182e881..d00bbd55f0 100644
--- a/hw/pci-bridge/gen_pcie_downstream_port.c
+++ b/hw/pci-bridge/gen_pcie_downstream_port.c
@@ -159,9 +159,9 @@ static const VMStateDescription vmstate_gen_pcie_downstream_port = {
static const Property gen_pcie_downstream_port_props[] = {
DEFINE_PROP_BIT(COMPAT_PROP_PCP, PCIDevice, cap_present,
QEMU_PCIE_SLTCAP_PCP_BITNR, true),
- DEFINE_PROP_PCIE_LINK_SPEED("x-speed", PCIESlot,
+ DEFINE_PROP_PCIE_LINK_SPEED("speed", PCIESlot,
speed, PCIE_LINK_SPEED_64),
- DEFINE_PROP_PCIE_LINK_WIDTH("x-width", PCIESlot,
+ DEFINE_PROP_PCIE_LINK_WIDTH("width", PCIESlot,
width, PCIE_LINK_WIDTH_32),
};
diff --git a/hw/pci-bridge/gen_pcie_root_port.c b/hw/pci-bridge/gen_pcie_root_port.c
index 5434d693d9..d3ace7f0db 100644
--- a/hw/pci-bridge/gen_pcie_root_port.c
+++ b/hw/pci-bridge/gen_pcie_root_port.c
@@ -129,9 +129,9 @@ static const Property gen_rp_props[] = {
res_reserve.mem_pref_32, -1),
DEFINE_PROP_SIZE("pref64-reserve", GenPCIERootPort,
res_reserve.mem_pref_64, -1),
- DEFINE_PROP_PCIE_LINK_SPEED("x-speed", PCIESlot,
+ DEFINE_PROP_PCIE_LINK_SPEED("speed", PCIESlot,
speed, PCIE_LINK_SPEED_16),
- DEFINE_PROP_PCIE_LINK_WIDTH("x-width", PCIESlot,
+ DEFINE_PROP_PCIE_LINK_WIDTH("width", PCIESlot,
width, PCIE_LINK_WIDTH_32),
};
diff --git a/hw/pci-bridge/gen_pcie_upstream_port.c b/hw/pci-bridge/gen_pcie_upstream_port.c
index e86eff099f..61ae3a294a 100644
--- a/hw/pci-bridge/gen_pcie_upstream_port.c
+++ b/hw/pci-bridge/gen_pcie_upstream_port.c
@@ -134,9 +134,9 @@ static const VMStateDescription vmstate_gen_pcie_upstream_port = {
};
static const Property gen_pcie_upstream_port_props[] = {
- DEFINE_PROP_PCIE_LINK_SPEED("x-speed", GenPCIEUpstreamPort,
+ DEFINE_PROP_PCIE_LINK_SPEED("speed", GenPCIEUpstreamPort,
speed, PCIE_LINK_SPEED_64),
- DEFINE_PROP_PCIE_LINK_WIDTH("x-width", GenPCIEUpstreamPort,
+ DEFINE_PROP_PCIE_LINK_WIDTH("width", GenPCIEUpstreamPort,
width, PCIE_LINK_WIDTH_32),
};
--
MST
^ permalink raw reply related [flat|nested] 84+ messages in thread
* [PULL 43/78] hw/pci-bridge: Default the generic PCIe root port as Gen6 capable
2026-10-28 17:13 [PULL 00/78] pci, vhost, virtio, iommu, acpi: features, fixes Michael S. Tsirkin
` (41 preceding siblings ...)
2026-10-28 17:15 ` [PULL 42/78] hw/pci-bridge: Make speed and width for PCIe ports non-experimental Michael S. Tsirkin
@ 2026-10-28 17:15 ` Michael S. Tsirkin
2026-10-28 17:15 ` [PULL 44/78] hw/pci: Ensure PCIe link speed/width is consistent between both ends Michael S. Tsirkin
` (35 subsequent siblings)
78 siblings, 0 replies; 84+ messages in thread
From: Michael S. Tsirkin @ 2026-10-28 17:15 UTC (permalink / raw)
To: qemu-devel
Cc: Peter Maydell, David Vrabel, Philippe Mathieu-Daudé,
Zhao Liu
From: David Vrabel <david.vrabel@nutanix.com>
As part of making virtual PCIe topology having the same apparent
bandwidth as a the underlying hardware, make the generic PCIe root
port support maximum speed so by default it doesn't appear to be a
bottleneck.
This also makes the three generic PCIe ports (root, upstream, and
downstream) have the same link speed/width capabilities.
Signed-off-by: David Vrabel <david.vrabel@nutanix.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Message-ID: <20260928152431.310363-4-david.vrabel@nutanix.com>
---
hw/core/machine.c | 1 +
hw/pci-bridge/gen_pcie_root_port.c | 2 +-
2 files changed, 2 insertions(+), 1 deletion(-)
diff --git a/hw/core/machine.c b/hw/core/machine.c
index d9ac4b419a..4da24f014a 100644
--- a/hw/core/machine.c
+++ b/hw/core/machine.c
@@ -43,6 +43,7 @@
GlobalProperty hw_compat_11_1[] = {
{ "sysbus-ehci-usb", "x-migrate-fetch-addr-64bit", "off" },
{ "pci-ehci-usb", "x-migrate-fetch-addr-64bit", "off" },
+ { "pcie-root-port", "speed", "16" },
};
const size_t hw_compat_11_1_len = G_N_ELEMENTS(hw_compat_11_1);
diff --git a/hw/pci-bridge/gen_pcie_root_port.c b/hw/pci-bridge/gen_pcie_root_port.c
index d3ace7f0db..8cabeadac8 100644
--- a/hw/pci-bridge/gen_pcie_root_port.c
+++ b/hw/pci-bridge/gen_pcie_root_port.c
@@ -130,7 +130,7 @@ static const Property gen_rp_props[] = {
DEFINE_PROP_SIZE("pref64-reserve", GenPCIERootPort,
res_reserve.mem_pref_64, -1),
DEFINE_PROP_PCIE_LINK_SPEED("speed", PCIESlot,
- speed, PCIE_LINK_SPEED_16),
+ speed, PCIE_LINK_SPEED_64),
DEFINE_PROP_PCIE_LINK_WIDTH("width", PCIESlot,
width, PCIE_LINK_WIDTH_32),
};
--
MST
^ permalink raw reply related [flat|nested] 84+ messages in thread
* [PULL 44/78] hw/pci: Ensure PCIe link speed/width is consistent between both ends
2026-10-28 17:13 [PULL 00/78] pci, vhost, virtio, iommu, acpi: features, fixes Michael S. Tsirkin
` (42 preceding siblings ...)
2026-10-28 17:15 ` [PULL 43/78] hw/pci-bridge: Default the generic PCIe root port as Gen6 capable Michael S. Tsirkin
@ 2026-10-28 17:15 ` Michael S. Tsirkin
2026-10-28 17:15 ` [PULL 45/78] vdpa-dev: implement guest_notifier_mask/pending Michael S. Tsirkin
` (34 subsequent siblings)
78 siblings, 0 replies; 84+ messages in thread
From: Michael S. Tsirkin @ 2026-10-28 17:15 UTC (permalink / raw)
To: qemu-devel; +Cc: Peter Maydell, David Vrabel
From: David Vrabel <david.vrabel@nutanix.com>
If an emulated PCIe endpoint has higher link speed or width
capabilities than the upstream port it is connected to, ensure that
the link status on both ends is consistent by using the link status
computed for the upstream port.
If the PCIe endpoint is a passthrough device, the link status
continues to match the real status and consistent values requires the
emulated upstream port to be at least as capable as the endpoint.
Signed-off-by: David Vrabel <david.vrabel@nutanix.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Message-ID: <20260928152431.310363-5-david.vrabel@nutanix.com>
---
hw/pci/pcie.c | 26 ++++++++++++++++++++++----
1 file changed, 22 insertions(+), 4 deletions(-)
diff --git a/hw/pci/pcie.c b/hw/pci/pcie.c
index 42bcb9206d..bd00305ecc 100644
--- a/hw/pci/pcie.c
+++ b/hw/pci/pcie.c
@@ -169,18 +169,36 @@ void pcie_cap_fill_link_ep_usp(PCIDevice *dev, PCIExpLinkWidth width,
PCIExpLinkSpeed speed, bool flitmode)
{
uint8_t *exp_cap = dev->config + dev->exp.exp_cap;
+ PCIDevice *upstream;
/*
- * For an end point or USP need to set the current status as well
- * as the capabilities.
+ * For an endpoint or USP, set Link Status as well as capabilities.
+ * Provisionally use this device's width/speed, then adopt the upstream
+ * port's values so both ends of an emulated link agree. The upstream
+ * LNKSTA is already clamped to the negotiated link capabilities via
+ * pcie_sync_bridge_lnk().
*/
- pci_long_test_and_clear_mask(exp_cap + PCI_EXP_LNKSTA,
+ pci_word_test_and_clear_mask(exp_cap + PCI_EXP_LNKSTA,
PCI_EXP_LNKSTA_CLS | PCI_EXP_LNKSTA_NLW);
- pci_long_test_and_set_mask(exp_cap + PCI_EXP_LNKSTA,
+ pci_word_test_and_set_mask(exp_cap + PCI_EXP_LNKSTA,
QEMU_PCI_EXP_LNKSTA_NLW(width) |
QEMU_PCI_EXP_LNKSTA_CLS(speed));
pcie_cap_fill_lnk(exp_cap, width, speed, flitmode);
+
+ upstream = pci_bridge_get_device(pci_get_bus(dev));
+ if (upstream && pci_is_express_downstream_port(upstream)) {
+ uint8_t *up_exp = upstream->config + upstream->exp.exp_cap;
+ uint16_t lnksta;
+
+ pcie_sync_bridge_lnk(upstream);
+ lnksta = pci_get_word(up_exp + PCI_EXP_LNKSTA);
+ pci_word_test_and_clear_mask(exp_cap + PCI_EXP_LNKSTA,
+ PCI_EXP_LNKSTA_CLS | PCI_EXP_LNKSTA_NLW);
+ pci_word_test_and_set_mask(exp_cap + PCI_EXP_LNKSTA,
+ lnksta & (PCI_EXP_LNKSTA_CLS |
+ PCI_EXP_LNKSTA_NLW));
+ }
}
static void pcie_cap_fill_slot_lnk(PCIDevice *dev)
--
MST
^ permalink raw reply related [flat|nested] 84+ messages in thread
* [PULL 45/78] vdpa-dev: implement guest_notifier_mask/pending
2026-10-28 17:13 [PULL 00/78] pci, vhost, virtio, iommu, acpi: features, fixes Michael S. Tsirkin
` (43 preceding siblings ...)
2026-10-28 17:15 ` [PULL 44/78] hw/pci: Ensure PCIe link speed/width is consistent between both ends Michael S. Tsirkin
@ 2026-10-28 17:15 ` Michael S. Tsirkin
2026-10-28 17:15 ` [PULL 46/78] vhost-user: remove shared object entries during cleanup Michael S. Tsirkin
` (33 subsequent siblings)
78 siblings, 0 replies; 84+ messages in thread
From: Michael S. Tsirkin @ 2026-10-28 17:15 UTC (permalink / raw)
To: qemu-devel
Cc: Peter Maydell, Li Zhaoxin, Paolo Bonzini, Daniil Tatianin,
Vladimir Sementsov-Ogievskiy
From: Li Zhaoxin <lizhaoxin04@baidu.com>
Forward guest notifier masking to the vhost layer via
VHOST_SET_VRING_CALL, so the KVM irqfd stays installed across
MSI-X mask/unmask cycles instead of being torn down and re-added
each time.
Signed-off-by: Li Zhaoxin <lizhaoxin04@baidu.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Message-ID: <20260909155042.34186-1-lizhaoxin04@baidu.com>
---
hw/virtio/vdpa-dev.c | 30 +++++++++++++++++++++++++-----
1 file changed, 25 insertions(+), 5 deletions(-)
diff --git a/hw/virtio/vdpa-dev.c b/hw/virtio/vdpa-dev.c
index 7e5cc040e9..cd7ce2e381 100644
--- a/hw/virtio/vdpa-dev.c
+++ b/hw/virtio/vdpa-dev.c
@@ -267,11 +267,7 @@ static int vhost_vdpa_device_start(VirtIODevice *vdev, Error **errp)
}
s->started = true;
- /*
- * guest_notifier_mask/pending not used yet, so just unmask
- * everything here. virtio-pci will do the right thing by
- * enabling/disabling irqfd.
- */
+ /* Point every vring call fd at its guest notifier. */
for (i = 0; i < s->dev.nvqs; i++) {
vhost_virtqueue_mask(&s->dev, vdev, i, false);
}
@@ -285,6 +281,28 @@ err_host_notifiers:
return ret;
}
+static void vhost_vdpa_device_guest_notifier_mask(VirtIODevice *vdev, int idx,
+ bool mask)
+{
+ VhostVdpaDevice *s = VHOST_VDPA_DEVICE(vdev);
+
+ if (idx == VIRTIO_CONFIG_IRQ_IDX) {
+ return;
+ }
+ vhost_virtqueue_mask(&s->dev, vdev, idx, mask);
+}
+
+static bool vhost_vdpa_device_guest_notifier_pending(VirtIODevice *vdev,
+ int idx)
+{
+ VhostVdpaDevice *s = VHOST_VDPA_DEVICE(vdev);
+
+ if (idx == VIRTIO_CONFIG_IRQ_IDX) {
+ return false;
+ }
+ return vhost_virtqueue_pending(&s->dev, idx);
+}
+
static void vhost_vdpa_device_stop(VirtIODevice *vdev)
{
VhostVdpaDevice *s = VHOST_VDPA_DEVICE(vdev);
@@ -376,6 +394,8 @@ static void vhost_vdpa_device_class_init(ObjectClass *klass, const void *data)
vdc->get_features = vhost_vdpa_device_get_features;
vdc->set_status = vhost_vdpa_device_set_status;
vdc->get_vhost = vhost_vdpa_device_get_vhost;
+ vdc->guest_notifier_mask = vhost_vdpa_device_guest_notifier_mask;
+ vdc->guest_notifier_pending = vhost_vdpa_device_guest_notifier_pending;
}
static void vhost_vdpa_device_instance_init(Object *obj)
--
MST
^ permalink raw reply related [flat|nested] 84+ messages in thread
* [PULL 46/78] vhost-user: remove shared object entries during cleanup
2026-10-28 17:13 [PULL 00/78] pci, vhost, virtio, iommu, acpi: features, fixes Michael S. Tsirkin
` (44 preceding siblings ...)
2026-10-28 17:15 ` [PULL 45/78] vdpa-dev: implement guest_notifier_mask/pending Michael S. Tsirkin
@ 2026-10-28 17:15 ` Michael S. Tsirkin
2026-10-28 17:16 ` [PULL 47/78] vhost: fix off-by-one in vhost_svq_next_desc with IN_ORDER Michael S. Tsirkin
` (32 subsequent siblings)
78 siblings, 0 replies; 84+ messages in thread
From: Michael S. Tsirkin @ 2026-10-28 17:15 UTC (permalink / raw)
To: qemu-devel
Cc: Peter Maydell, Yuho Choi, Yuho Choi, Albert Esteve,
Stefano Garzarella, vhost-user
From: Yuho Choi <dbgh9129@gmail.com>
SHARED_OBJECT_ADD publishes a borrowed vhost_dev pointer in the global
UUID table. If an exporter goes away without sending SHARED_OBJECT_REMOVE,
vhost_user_backend_cleanup() frees dev->opaque but leaves these entries
behind. A later lookup from another backend can dereference the NULL
opaque pointer, or access a freed vhost_dev if its containing allocation
has also been released.
Remove all entries exported by the device after closing its backend
request channel and before freeing its state. Match both the resource
type and owner pointer so other exporters and dma-buf entries are kept.
Add unit coverage for multiple UUIDs per owner, unrelated resources,
repeated cleanup, UUID reuse, and an uninitialized or empty table.
Fixes: 160947666276 ("vhost-user: add shared_object msg")
Signed-off-by: Yuho Choi <oss.patchbox@gmail.com>
Reviewed-by: Albert Esteve <aesteve@redhat.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Message-ID: <20260910053012.1395885-1-oss.patchbox@gmail.com>
---
include/hw/virtio/virtio-dmabuf.h | 8 ++++++
hw/display/virtio-dmabuf.c | 18 +++++++++++++
hw/virtio/vhost-user.c | 1 +
tests/unit/test-virtio-dmabuf.c | 43 +++++++++++++++++++++++++++++++
4 files changed, 70 insertions(+)
diff --git a/include/hw/virtio/virtio-dmabuf.h b/include/hw/virtio/virtio-dmabuf.h
index 627c3b6db7..a1ad362c37 100644
--- a/include/hw/virtio/virtio-dmabuf.h
+++ b/include/hw/virtio/virtio-dmabuf.h
@@ -65,6 +65,14 @@ bool virtio_add_vhost_device(QemuUUID *uuid, struct vhost_dev *dev);
*/
bool virtio_remove_resource(const QemuUUID *uuid);
+/**
+ * virtio_remove_vhost_device() - Remove a vhost device's exported resources
+ * @dev: the exporter whose entries are to be removed
+ *
+ * The caller must remove the entries before cleaning up the device.
+ */
+void virtio_remove_vhost_device(struct vhost_dev *dev);
+
/**
* virtio_lookup_dmabuf() - Looks for a dma-buf resource in the lookup table
* @uuid: resource's UUID
diff --git a/hw/display/virtio-dmabuf.c b/hw/display/virtio-dmabuf.c
index 5e0395be77..636372543a 100644
--- a/hw/display/virtio-dmabuf.c
+++ b/hw/display/virtio-dmabuf.c
@@ -96,6 +96,24 @@ bool virtio_remove_resource(const QemuUUID *uuid)
return result;
}
+static gboolean virtio_vhost_device_match(gpointer key, gpointer value,
+ gpointer dev)
+{
+ VirtioSharedObject *vso = value;
+
+ return vso->type == TYPE_VHOST_DEV && vso->value == dev;
+}
+
+void virtio_remove_vhost_device(struct vhost_dev *dev)
+{
+ g_mutex_lock(&lock);
+ if (resource_uuids != NULL) {
+ g_hash_table_foreach_remove(resource_uuids, virtio_vhost_device_match,
+ dev);
+ }
+ g_mutex_unlock(&lock);
+}
+
static VirtioSharedObject *get_shared_object(const QemuUUID *uuid)
{
gpointer lookup_res = NULL;
diff --git a/hw/virtio/vhost-user.c b/hw/virtio/vhost-user.c
index 65889b0776..3402accc6f 100644
--- a/hw/virtio/vhost-user.c
+++ b/hw/virtio/vhost-user.c
@@ -2711,6 +2711,7 @@ static int vhost_user_backend_cleanup(struct vhost_dev *dev)
if (u->backend_sioc) {
close_backend_channel(u);
}
+ virtio_remove_vhost_device(dev);
g_free(u->region_rb);
u->region_rb = NULL;
g_free(u->region_rb_offset);
diff --git a/tests/unit/test-virtio-dmabuf.c b/tests/unit/test-virtio-dmabuf.c
index a45ec52f42..395ea6f464 100644
--- a/tests/unit/test-virtio-dmabuf.c
+++ b/tests/unit/test-virtio-dmabuf.c
@@ -22,6 +22,47 @@
#include "hw/virtio/virtio-dmabuf.h"
+static void test_remove_vhost_device(void)
+{
+ struct vhost_dev dev = { 0 }, other = { 0 };
+ QemuUUID uuids[2], other_uuid, dmabuf_uuid;
+ int i;
+
+ /* Also allow cleanup before any resources have been registered. */
+ virtio_remove_vhost_device(&dev);
+
+ for (i = 0; i < ARRAY_SIZE(uuids); i++) {
+ qemu_uuid_generate(&uuids[i]);
+ g_assert_true(virtio_add_vhost_device(&uuids[i], &dev));
+ }
+ qemu_uuid_generate(&other_uuid);
+ g_assert_true(virtio_add_vhost_device(&other_uuid, &other));
+ qemu_uuid_generate(&dmabuf_uuid);
+ g_assert_true(virtio_add_dmabuf(&dmabuf_uuid, 3));
+
+ virtio_remove_vhost_device(&dev);
+ for (i = 0; i < ARRAY_SIZE(uuids); i++) {
+ g_assert_null(virtio_lookup_vhost_device(&uuids[i]));
+ g_assert_cmpint(virtio_object_type(&uuids[i]), ==, TYPE_INVALID);
+ }
+
+ /* Repeated cleanup must preserve unrelated resources. */
+ virtio_remove_vhost_device(&dev);
+ g_assert_true(virtio_lookup_vhost_device(&other_uuid) == &other);
+ g_assert_cmpint(virtio_lookup_dmabuf(&dmabuf_uuid), ==, 3);
+
+ /* Removed UUIDs can be exported by another device. */
+ g_assert_true(virtio_add_vhost_device(&uuids[0], &other));
+ virtio_remove_vhost_device(&other);
+ g_assert_null(virtio_lookup_vhost_device(&uuids[0]));
+ g_assert_null(virtio_lookup_vhost_device(&other_uuid));
+ g_assert_true(virtio_remove_resource(&dmabuf_uuid));
+
+ virtio_remove_vhost_device(&dev);
+ virtio_free_resources();
+ virtio_remove_vhost_device(&dev);
+}
+
static void test_add_remove_resources(void)
{
QemuUUID uuid;
@@ -125,6 +166,8 @@ static void test_free_resources(void)
int main(int argc, char **argv)
{
g_test_init(&argc, &argv, NULL);
+ g_test_add_func("/virtio-dmabuf/remove_vhost_device",
+ test_remove_vhost_device);
g_test_add_func("/virtio-dmabuf/add_rm_res", test_add_remove_resources);
g_test_add_func("/virtio-dmabuf/add_rm_dev", test_add_remove_dev);
g_test_add_func("/virtio-dmabuf/rm_invalid_res",
--
MST
^ permalink raw reply related [flat|nested] 84+ messages in thread
* [PULL 47/78] vhost: fix off-by-one in vhost_svq_next_desc with IN_ORDER
2026-10-28 17:13 [PULL 00/78] pci, vhost, virtio, iommu, acpi: features, fixes Michael S. Tsirkin
` (45 preceding siblings ...)
2026-10-28 17:15 ` [PULL 46/78] vhost-user: remove shared object entries during cleanup Michael S. Tsirkin
@ 2026-10-28 17:16 ` Michael S. Tsirkin
2026-10-28 17:16 ` [PULL 48/78] pcie_sriov: fix wrong write width and off-by-one in pf_reset Michael S. Tsirkin
` (31 subsequent siblings)
78 siblings, 0 replies; 84+ messages in thread
From: Michael S. Tsirkin @ 2026-10-28 17:16 UTC (permalink / raw)
To: qemu-devel
Cc: Peter Maydell, Li RongQing, Stefano Garzarella,
Eugenio Pérez
From: Li RongQing <lirongqing@baidu.com>
vhost_svq_next_desc checks id against vring.num before incrementing,
so when id == vring.num - 1 the function returns vring.num instead of
0. This causes vhost_svq_vring_write_descs to write descs[num-1].next
= num (an invalid descriptor index) and, on the next iteration, to
access descs[num] — which lies past the descriptor ring and into the
avail ring, corrupting it.
The bug triggers whenever an IN_ORDER descriptor chain wraps around the
end of the SVQ ring.
Fix by incrementing id first, then checking for wrap-around.
Fixes: 485c9e69ef6e ("vhost: add in_order feature to shadow virtqueue")
Signed-off-by: Li RongQing <lirongqing@baidu.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Message-ID: <20260911081635.1872-1-lirongqing@baidu.com>
---
hw/virtio/vhost-shadow-virtqueue.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/hw/virtio/vhost-shadow-virtqueue.c b/hw/virtio/vhost-shadow-virtqueue.c
index c8831d52be..0fae35ba1a 100644
--- a/hw/virtio/vhost-shadow-virtqueue.c
+++ b/hw/virtio/vhost-shadow-virtqueue.c
@@ -161,7 +161,7 @@ static uint16_t vhost_svq_next_desc(const VhostShadowVirtqueue *svq,
uint16_t id)
{
if (virtio_vdev_has_feature(svq->vdev, VIRTIO_F_IN_ORDER)) {
- return (id == svq->vring.num) ? 0 : ++id;
+ return (++id == svq->vring.num) ? 0 : id;
} else {
return svq->desc_state[id].next;
}
--
MST
^ permalink raw reply related [flat|nested] 84+ messages in thread
* [PULL 48/78] pcie_sriov: fix wrong write width and off-by-one in pf_reset
2026-10-28 17:13 [PULL 00/78] pci, vhost, virtio, iommu, acpi: features, fixes Michael S. Tsirkin
` (46 preceding siblings ...)
2026-10-28 17:16 ` [PULL 47/78] vhost: fix off-by-one in vhost_svq_next_desc with IN_ORDER Michael S. Tsirkin
@ 2026-10-28 17:16 ` Michael S. Tsirkin
2026-10-28 17:16 ` [PULL 49/78] util/event_notifier: more strict API Michael S. Tsirkin
` (30 subsequent siblings)
78 siblings, 0 replies; 84+ messages in thread
From: Michael S. Tsirkin @ 2026-10-28 17:16 UTC (permalink / raw)
To: qemu-devel; +Cc: Peter Maydell, Li RongQing
From: Li RongQing <lirongqing@baidu.com>
pcie_sriov_pf_reset() restores the VF BAR type registers using
pci_set_quad() with a 4-byte stride. Since each VF BAR register is
4 bytes wide, the 8-byte write also covers the following BAR
register. Although subsequent iterations overwrite most of the
unintended writes, the access is incorrect and the final iteration
can write beyond the VF BAR registers.
SR-IOV defines six VF BAR registers and does not provide a VF ROM BAR.
However, several SR-IOV code paths use PCI_NUM_REGIONS, which includes
the ROM slot and therefore has a value of seven.
In particular, the final iteration in pcie_sriov_pf_reset() currently
writes a quadword at the offset of the seventh region. This starts at
the byte immediately following the six VF BAR registers and can
overwrite subsequent fields in the SR-IOV capability.
Use pci_set_long() to match the 4-byte VF BAR register width, and use
PCI_SRIOV_NUM_BARS when iterating over SR-IOV VF BARs. This also makes
the BAR count consistent with the assertion in
pcie_sriov_pf_init_vf_bar().
Fixes: 19e55471d4e8 ("pcie_sriov: Allow user to create SR-IOV device")
Fixes: c8bc4db403e1 ("pcie_sriov: Reset SR-IOV extended capability")
Signed-off-by: Li RongQing <lirongqing@baidu.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Message-ID: <20260918063526.2191-1-lirongqing@baidu.com>
---
hw/pci/pcie_sriov.c | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/hw/pci/pcie_sriov.c b/hw/pci/pcie_sriov.c
index 69930c7b8c..6a748c3626 100644
--- a/hw/pci/pcie_sriov.c
+++ b/hw/pci/pcie_sriov.c
@@ -229,7 +229,7 @@ void pcie_sriov_pf_init_vf_bar(PCIDevice *dev, int region_num,
assert(sriov_cap > 0);
assert(region_num >= 0);
- assert(region_num < PCI_NUM_REGIONS);
+ assert(region_num < PCI_SRIOV_NUM_BARS);
assert(region_num != PCI_ROM_SLOT);
wmask = ~(size - 1);
@@ -308,7 +308,7 @@ int16_t pcie_sriov_pf_init_from_user_created_vfs(PCIDevice *dev,
return -1;
}
- for (size_t j = 0; j < PCI_NUM_REGIONS; j++) {
+ for (size_t j = 0; j < PCI_SRIOV_NUM_BARS; j++) {
if (vfs[i]->io_regions[j].size != vfs[0]->io_regions[j].size ||
vfs[i]->io_regions[j].type != vfs[0]->io_regions[j].type) {
error_setg(errp, "inconsistent SR-IOV BARs");
@@ -344,7 +344,7 @@ int16_t pcie_sriov_pf_init_from_user_created_vfs(PCIDevice *dev,
dev->exp.sriov_pf.vf = vfs;
dev->exp.sriov_pf.vf_user_created = true;
- for (i = 0; i < PCI_NUM_REGIONS; i++) {
+ for (i = 0; i < PCI_SRIOV_NUM_BARS; i++) {
PCIIORegion *region = &vfs[0]->io_regions[i];
if (region->size) {
@@ -457,8 +457,8 @@ void pcie_sriov_pf_reset(PCIDevice *dev)
*/
pci_set_word(dev->config + sriov_cap + PCI_SRIOV_SYS_PGSIZE, 0x1);
- for (uint16_t i = 0; i < PCI_NUM_REGIONS; i++) {
- pci_set_quad(dev->config + sriov_cap + PCI_SRIOV_BAR + i * 4,
+ for (uint16_t i = 0; i < PCI_SRIOV_NUM_BARS; i++) {
+ pci_set_long(dev->config + sriov_cap + PCI_SRIOV_BAR + i * 4,
dev->exp.sriov_pf.vf_bar_type[i]);
}
}
--
MST
^ permalink raw reply related [flat|nested] 84+ messages in thread
* [PULL 49/78] util/event_notifier: more strict API
2026-10-28 17:13 [PULL 00/78] pci, vhost, virtio, iommu, acpi: features, fixes Michael S. Tsirkin
` (47 preceding siblings ...)
2026-10-28 17:16 ` [PULL 48/78] pcie_sriov: fix wrong write width and off-by-one in pf_reset Michael S. Tsirkin
@ 2026-10-28 17:16 ` Michael S. Tsirkin
2026-10-28 17:16 ` [PULL 50/78] virtio-qmp: Fix out-of-bounds vqs index in vhost queue status Michael S. Tsirkin
` (29 subsequent siblings)
78 siblings, 0 replies; 84+ messages in thread
From: Michael S. Tsirkin @ 2026-10-28 17:16 UTC (permalink / raw)
To: qemu-devel
Cc: Peter Maydell, Vladimir Sementsov-Ogievskiy, Elena Ufimtseva,
Jagannathan Raman, Stefano Garzarella, Eugenio Pérez,
Paolo Bonzini
From: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
We have .initialized in EventNotifier to track was it actually
initialized, and we have a comment that @fd passed to
event_notifier_init_fd() must be an eventfd object.
Still, there are two specific users, which ignore these things,
and use -1 as specific value storing in notifier, to mark it
"uninitialized".
Let's make strict API:
- store only valid FDs in initialized notifier
- _set() asserts that passed fd is valid
- _get() asserts that notifier is initialized (actually, except
the two specific cases we rework, all other callers are not
prepared to notifier being uninitialized when _get() is called.
In this case _get() returns 0 (which is a valid fd), and caller
will mistakenly use it, which is worse than abort())
- for two specific cases, implement additional getter for .initialized
field itself
- zeroed notifier is uninitialized(.initialized is false)
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Message-ID: <20260911185800.134135-1-vsementsov@yandex-team.ru>
---
include/qemu/event_notifier.h | 1 +
hw/remote/iohub.c | 11 +++++-----
hw/virtio/vhost-shadow-virtqueue.c | 32 +++++++++++++++++-------------
util/event_notifier-posix.c | 10 ++++++++++
4 files changed, 35 insertions(+), 19 deletions(-)
diff --git a/include/qemu/event_notifier.h b/include/qemu/event_notifier.h
index 8a4ff308e1..820efe7722 100644
--- a/include/qemu/event_notifier.h
+++ b/include/qemu/event_notifier.h
@@ -39,6 +39,7 @@ int event_notifier_test_and_clear(EventNotifier *);
void event_notifier_init_fd(EventNotifier *, int fd);
int event_notifier_get_fd(const EventNotifier *);
int event_notifier_get_wfd(const EventNotifier *);
+bool event_notifier_initialized(const EventNotifier *e);
#else
HANDLE event_notifier_get_handle(EventNotifier *);
#endif
diff --git a/hw/remote/iohub.c b/hw/remote/iohub.c
index 988d3285cc..ab698d9f03 100644
--- a/hw/remote/iohub.c
+++ b/hw/remote/iohub.c
@@ -28,8 +28,6 @@ void remote_iohub_init(RemoteIOHubState *iohub)
for (pirq = 0; pirq < REMOTE_IOHUB_NB_PIRQS; pirq++) {
qemu_mutex_init(&iohub->irq_level_lock[pirq]);
iohub->irq_level[pirq] = 0;
- event_notifier_init_fd(&iohub->irqfds[pirq], -1);
- event_notifier_init_fd(&iohub->resamplefds[pirq], -1);
}
}
@@ -85,9 +83,12 @@ void process_set_irqfd_msg(PCIDevice *pci_dev, MPQemuMsg *msg)
pirq = remote_iohub_map_irq(pci_dev, intx);
- if (event_notifier_get_fd(&iohub->irqfds[pirq]) != -1) {
- qemu_set_fd_handler(event_notifier_get_fd(&iohub->resamplefds[pirq]),
- NULL, NULL, NULL);
+ if (event_notifier_initialized(&iohub->irqfds[pirq])) {
+ if (event_notifier_initialized(&iohub->resamplefds[pirq])) {
+ qemu_set_fd_handler(
+ event_notifier_get_fd(&iohub->resamplefds[pirq]),
+ NULL, NULL, NULL);
+ }
event_notifier_cleanup(&iohub->irqfds[pirq]);
event_notifier_cleanup(&iohub->resamplefds[pirq]);
memset(&iohub->token[pirq], 0, sizeof(ResampleToken));
diff --git a/hw/virtio/vhost-shadow-virtqueue.c b/hw/virtio/vhost-shadow-virtqueue.c
index 0fae35ba1a..c97bd9f93f 100644
--- a/hw/virtio/vhost-shadow-virtqueue.c
+++ b/hw/virtio/vhost-shadow-virtqueue.c
@@ -751,6 +751,15 @@ static void vhost_svq_handle_call(EventNotifier *n)
vhost_svq_flush(svq, true);
}
+static void event_notifier_set_or_zero(EventNotifier *e, int fd)
+{
+ if (fd == VHOST_FILE_UNBIND) {
+ memset(e, 0, sizeof(*e));
+ } else {
+ event_notifier_init_fd(e, fd);
+ }
+}
+
/**
* Set the call notifier for the SVQ to call the guest
*
@@ -761,17 +770,13 @@ static void vhost_svq_handle_call(EventNotifier *n)
*/
void vhost_svq_set_svq_call_fd(VhostShadowVirtqueue *svq, int call_fd)
{
- if (call_fd == VHOST_FILE_UNBIND) {
- /*
- * Fail event_notifier_set if called handling device call.
- *
- * SVQ still needs device notifications, since it needs to keep
- * forwarding used buffers even with the unbind.
- */
- memset(&svq->svq_call, 0, sizeof(svq->svq_call));
- } else {
- event_notifier_init_fd(&svq->svq_call, call_fd);
- }
+ /*
+ * Fail event_notifier_set if called handling device call.
+ *
+ * SVQ still needs device notifications, since it needs to keep
+ * forwarding used buffers even with the unbind.
+ */
+ event_notifier_set_or_zero(&svq->svq_call, call_fd);
}
/**
@@ -814,14 +819,14 @@ size_t vhost_svq_device_area_size(const VhostShadowVirtqueue *svq)
void vhost_svq_set_svq_kick_fd(VhostShadowVirtqueue *svq, int svq_kick_fd)
{
EventNotifier *svq_kick = &svq->svq_kick;
- bool poll_stop = VHOST_FILE_UNBIND != event_notifier_get_fd(svq_kick);
+ bool poll_stop = event_notifier_initialized(svq_kick);
bool poll_start = svq_kick_fd != VHOST_FILE_UNBIND;
if (poll_stop) {
event_notifier_set_handler(svq_kick, NULL);
}
- event_notifier_init_fd(svq_kick, svq_kick_fd);
+ event_notifier_set_or_zero(&svq->svq_kick, svq_kick_fd);
/*
* event_notifier_set_handler already checks for guest's notifications if
* they arrive at the new file descriptor in the switch, so there is no
@@ -928,7 +933,6 @@ VhostShadowVirtqueue *vhost_svq_new(const VhostShadowVirtqueueOps *ops,
{
VhostShadowVirtqueue *svq = g_new0(VhostShadowVirtqueue, 1);
- event_notifier_init_fd(&svq->svq_kick, VHOST_FILE_UNBIND);
svq->ops = ops;
svq->ops_opaque = ops_opaque;
return svq;
diff --git a/util/event_notifier-posix.c b/util/event_notifier-posix.c
index 83fdbb96bb..f595945af7 100644
--- a/util/event_notifier-posix.c
+++ b/util/event_notifier-posix.c
@@ -27,6 +27,7 @@
*/
void event_notifier_init_fd(EventNotifier *e, int fd)
{
+ assert(fd >= 0);
e->rfd = fd;
e->wfd = fd;
e->initialized = true;
@@ -96,14 +97,23 @@ void event_notifier_cleanup(EventNotifier *e)
int event_notifier_get_fd(const EventNotifier *e)
{
+ assert(e->initialized);
+ assert(e->rfd >= 0);
return e->rfd;
}
int event_notifier_get_wfd(const EventNotifier *e)
{
+ assert(e->initialized);
+ assert(e->wfd >= 0);
return e->wfd;
}
+bool event_notifier_initialized(const EventNotifier *e)
+{
+ return e->initialized;
+}
+
int event_notifier_set(EventNotifier *e)
{
static const uint64_t value = 1;
--
MST
^ permalink raw reply related [flat|nested] 84+ messages in thread
* [PULL 50/78] virtio-qmp: Fix out-of-bounds vqs index in vhost queue status
2026-10-28 17:13 [PULL 00/78] pci, vhost, virtio, iommu, acpi: features, fixes Michael S. Tsirkin
` (48 preceding siblings ...)
2026-10-28 17:16 ` [PULL 49/78] util/event_notifier: more strict API Michael S. Tsirkin
@ 2026-10-28 17:16 ` Michael S. Tsirkin
2026-10-28 17:16 ` [PULL 51/78] contrib/vhost-user-bridge: Use stderr for debug output Michael S. Tsirkin
` (28 subsequent siblings)
78 siblings, 0 replies; 84+ messages in thread
From: Michael S. Tsirkin @ 2026-10-28 17:16 UTC (permalink / raw)
To: qemu-devel; +Cc: Peter Maydell, Li RongQing
From: Li RongQing <lirongqing@baidu.com>
qmp_x_query_virtio_vhost_queue_status() validates the queue number
against the vhost device's global range (queue < hdev->vq_index +
hdev->nvqs), confirming that 'queue' is a global index. But it then
indexes hdev->vqs[] directly with that global index.
hdev->vqs is allocated as g_new0(struct vhost_virtqueue, hdev->nvqs),
so valid local indices are 0..nvqs-1. When hdev->vq_index > 0
(multiqueue vhost-net queue pairs other than the first), every access
hdev->vqs[queue] reads past the end of the array.
Use queue - hdev->vq_index to compute the local index, consistent with
the rest of vhost.c (e.g. vhost_virtqueue_pending/mask).
Fixes: 07536ddda73a ("qmp: add QMP commands for virtio/vhost queue-status")
Signed-off-by: Li RongQing <lirongqing@baidu.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Message-ID: <20260912042343.2362-1-lirongqing@baidu.com>
---
hw/virtio/virtio-qmp.c | 19 ++++++++++---------
1 file changed, 10 insertions(+), 9 deletions(-)
diff --git a/hw/virtio/virtio-qmp.c b/hw/virtio/virtio-qmp.c
index 8b7cd7a310..8d190c4c70 100644
--- a/hw/virtio/virtio-qmp.c
+++ b/hw/virtio/virtio-qmp.c
@@ -789,6 +789,7 @@ VirtVhostQueueStatus *qmp_x_query_virtio_vhost_queue_status(const char *path,
VirtioDeviceClass *vdc = VIRTIO_DEVICE_GET_CLASS(vdev);
struct vhost_dev *hdev = vdc->get_vhost(vdev);
+ int vq = queue - hdev->vq_index;
if (queue < hdev->vq_index || queue >= hdev->vq_index + hdev->nvqs) {
error_setg(errp, "Invalid vhost virtqueue number %d", queue);
@@ -797,15 +798,15 @@ VirtVhostQueueStatus *qmp_x_query_virtio_vhost_queue_status(const char *path,
status = g_new0(VirtVhostQueueStatus, 1);
status->name = g_strdup(vdev->name);
- status->kick = hdev->vqs[queue].kick;
- status->call = hdev->vqs[queue].call;
- status->num = hdev->vqs[queue].num;
- status->desc_phys = hdev->vqs[queue].desc_phys;
- status->desc_size = hdev->vqs[queue].desc_size;
- status->avail_phys = hdev->vqs[queue].avail_phys;
- status->avail_size = hdev->vqs[queue].avail_size;
- status->used_phys = hdev->vqs[queue].used_phys;
- status->used_size = hdev->vqs[queue].used_size;
+ status->kick = hdev->vqs[vq].kick;
+ status->call = hdev->vqs[vq].call;
+ status->num = hdev->vqs[vq].num;
+ status->desc_phys = hdev->vqs[vq].desc_phys;
+ status->desc_size = hdev->vqs[vq].desc_size;
+ status->avail_phys = hdev->vqs[vq].avail_phys;
+ status->avail_size = hdev->vqs[vq].avail_size;
+ status->used_phys = hdev->vqs[vq].used_phys;
+ status->used_size = hdev->vqs[vq].used_size;
return status;
}
--
MST
^ permalink raw reply related [flat|nested] 84+ messages in thread
* [PULL 51/78] contrib/vhost-user-bridge: Use stderr for debug output
2026-10-28 17:13 [PULL 00/78] pci, vhost, virtio, iommu, acpi: features, fixes Michael S. Tsirkin
` (49 preceding siblings ...)
2026-10-28 17:16 ` [PULL 50/78] virtio-qmp: Fix out-of-bounds vqs index in vhost queue status Michael S. Tsirkin
@ 2026-10-28 17:16 ` Michael S. Tsirkin
2026-10-28 17:16 ` [PULL 52/78] tests/functional/x86_64/test_vhost_user_bridge: Eliminate stdbuf usage Michael S. Tsirkin
` (27 subsequent siblings)
78 siblings, 0 replies; 84+ messages in thread
From: Michael S. Tsirkin @ 2026-10-28 17:16 UTC (permalink / raw)
To: qemu-devel
Cc: Peter Maydell, Yodel Eldar, Marc-André Lureau,
Stefano Garzarella
From: Yodel Eldar <yodel.eldar@yodel.dev>
Since vhost-user-bridge is solely used for testing, and
logging to stderr ensures proper interleaving of debug
output in automated testing, modify the DPRINT macro to
write to stderr instead of stdout.
Message ID of relevant thread:
<20260906-nohmp-next-v1-2-2b21e63974f3@redhat.com>
Suggested-by: Michael S. Tsirkin <mst@redhat.com>
Suggested-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Signed-off-by: Yodel Eldar <yodel.eldar@yodel.dev>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Message-ID: <20260914165250.88382-2-yodel.eldar@yodel.dev>
---
contrib/vhost-user-bridge/vhost-user-bridge.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/contrib/vhost-user-bridge/vhost-user-bridge.c b/contrib/vhost-user-bridge/vhost-user-bridge.c
index 3f0fd0fa49..a6f7ec226c 100644
--- a/contrib/vhost-user-bridge/vhost-user-bridge.c
+++ b/contrib/vhost-user-bridge/vhost-user-bridge.c
@@ -41,7 +41,7 @@
#define DPRINT(...) \
do { \
if (VHOST_USER_BRIDGE_DEBUG) { \
- printf(__VA_ARGS__); \
+ fprintf(stderr, __VA_ARGS__); \
} \
} while (0)
--
MST
^ permalink raw reply related [flat|nested] 84+ messages in thread
* [PULL 52/78] tests/functional/x86_64/test_vhost_user_bridge: Eliminate stdbuf usage
2026-10-28 17:13 [PULL 00/78] pci, vhost, virtio, iommu, acpi: features, fixes Michael S. Tsirkin
` (50 preceding siblings ...)
2026-10-28 17:16 ` [PULL 51/78] contrib/vhost-user-bridge: Use stderr for debug output Michael S. Tsirkin
@ 2026-10-28 17:16 ` Michael S. Tsirkin
2026-10-28 17:16 ` [PULL 53/78] pcie_aer: fix pci_set_long overflow into PCI_EXP_LNKCAP Michael S. Tsirkin
` (26 subsequent siblings)
78 siblings, 0 replies; 84+ messages in thread
From: Michael S. Tsirkin @ 2026-10-28 17:16 UTC (permalink / raw)
To: qemu-devel
Cc: Peter Maydell, Yodel Eldar, Marc-André Lureau,
Stefano Garzarella, Paolo Bonzini, Zhao Liu
From: Yodel Eldar <yodel.eldar@yodel.dev>
Usage of stdbuf triggers a runtime error when vhost-user-bridge
is built with ASAN. Now that vhost-user-bridge writes debug
output to stderr, stdbuf is obviated; remove its invocation.
Message ID of relevant thread:
<20260906-nohmp-next-v1-2-2b21e63974f3@redhat.com>
Suggested-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Suggested-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Yodel Eldar <yodel.eldar@yodel.dev>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Message-ID: <20260914165250.88382-3-yodel.eldar@yodel.dev>
---
tests/functional/x86_64/test_vhost_user_bridge.py | 6 ------
1 file changed, 6 deletions(-)
diff --git a/tests/functional/x86_64/test_vhost_user_bridge.py b/tests/functional/x86_64/test_vhost_user_bridge.py
index c36c625420..5e1db48884 100755
--- a/tests/functional/x86_64/test_vhost_user_bridge.py
+++ b/tests/functional/x86_64/test_vhost_user_bridge.py
@@ -76,12 +76,6 @@ def configure_vm(self, ud_socket_path, lport, rport, hostfwd_port, tftpdir):
def assemble_vubr_args(self, vubr_path, ud_socket_path, lport, rport):
vubr_args = []
- if (stdbuf_path := which("stdbuf")) is None:
- self.log.info("Could not find stdbuf: vhost-user-bridge "
- "log lines may appear out of order")
- else:
- vubr_args += [stdbuf_path, "-o0", "-e0"]
-
vubr_args += [vubr_path, "-u", f"{ud_socket_path}",
"-l", f"127.0.0.1:{lport}", "-r", f"127.0.0.1:{rport}"]
--
MST
^ permalink raw reply related [flat|nested] 84+ messages in thread
* [PULL 53/78] pcie_aer: fix pci_set_long overflow into PCI_EXP_LNKCAP
2026-10-28 17:13 [PULL 00/78] pci, vhost, virtio, iommu, acpi: features, fixes Michael S. Tsirkin
` (51 preceding siblings ...)
2026-10-28 17:16 ` [PULL 52/78] tests/functional/x86_64/test_vhost_user_bridge: Eliminate stdbuf usage Michael S. Tsirkin
@ 2026-10-28 17:16 ` Michael S. Tsirkin
2026-10-28 17:16 ` [PULL 54/78] MAINTAINERS: Update Junjie Cao's email address Michael S. Tsirkin
` (25 subsequent siblings)
78 siblings, 0 replies; 84+ messages in thread
From: Michael S. Tsirkin @ 2026-10-28 17:16 UTC (permalink / raw)
To: qemu-devel; +Cc: Peter Maydell, Li RongQing
From: Li RongQing <lirongqing@baidu.com>
pcie_aer_inject_uncor_error() wrote inj->devsta (a uint16_t) into
PCI_EXP_DEVSTA using pci_set_long(), which writes 4 bytes. PCI_EXP_DEVSTA
is a 16-bit register at offset 0x0a; the extra 2 bytes written by
pci_set_long() land at offset 0x0c, the low half of PCI_EXP_LNKCAP,
zeroing the link speed/width capability fields on every uncorrectable
error injection.
Use pci_set_word() instead, matching the correctable error path in
pcie_aer_inject_cor_error().
Fixes: 34e65944c035 ("pcie/aer: helper functions for pcie aer capability")
Signed-off-by: Li RongQing <lirongqing@baidu.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Message-ID: <20260915072908.2197-1-lirongqing@baidu.com>
---
hw/pci/pcie_aer.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/hw/pci/pcie_aer.c b/hw/pci/pcie_aer.c
index 22497b1fd6..43b14c1f22 100644
--- a/hw/pci/pcie_aer.c
+++ b/hw/pci/pcie_aer.c
@@ -589,7 +589,7 @@ static bool pcie_aer_inject_uncor_error(PCIEAERInject *inj, bool is_fatal)
if (inj->unsupported_request) {
inj->devsta |= PCI_EXP_DEVSTA_URD;
}
- pci_set_long(dev->config + dev->exp.exp_cap + PCI_EXP_DEVSTA, inj->devsta);
+ pci_set_word(dev->config + dev->exp.exp_cap + PCI_EXP_DEVSTA, inj->devsta);
if (inj->aer_cap) {
uint32_t mask = pci_get_long(inj->aer_cap + PCI_ERR_UNCOR_MASK);
--
MST
^ permalink raw reply related [flat|nested] 84+ messages in thread
* [PULL 54/78] MAINTAINERS: Update Junjie Cao's email address
2026-10-28 17:13 [PULL 00/78] pci, vhost, virtio, iommu, acpi: features, fixes Michael S. Tsirkin
` (52 preceding siblings ...)
2026-10-28 17:16 ` [PULL 53/78] pcie_aer: fix pci_set_long overflow into PCI_EXP_LNKCAP Michael S. Tsirkin
@ 2026-10-28 17:16 ` Michael S. Tsirkin
2026-10-28 17:16 ` [PULL 55/78] hw/arm: remove pci-bridge/pci_expander_bridge.h from virt.c Michael S. Tsirkin
` (24 subsequent siblings)
78 siblings, 0 replies; 84+ messages in thread
From: Michael S. Tsirkin @ 2026-10-28 17:16 UTC (permalink / raw)
To: qemu-devel
Cc: Peter Maydell, Junjie Cao, Philippe Mathieu-Daudé,
Pierrick Bouvier, Markus Armbruster, Alistair Francis
From: Junjie Cao <junjie.cao@linux.dev>
Update my email address to junjie.cao@linux.dev for future correspondence.
I'll continue contributing and reviewing CXL patches.
Signed-off-by: Junjie Cao <junjie.cao@linux.dev>
Reviewed-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Message-ID: <20261021134307.874867-1-junjie.cao@linux.dev>
---
MAINTAINERS | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/MAINTAINERS b/MAINTAINERS
index b36582c9d0..d7ec12ac39 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -3382,7 +3382,7 @@ F: docs/devel/migration/CPR.rst
Compute Express Link
M: Jonathan Cameron <jic23@kernel.org>
-R: Junjie Cao <junjie.cao@intel.com>
+R: Junjie Cao <junjie.cao@linux.dev>
R: Anisa Su <anisa.su@samsung.com>
L: linux-cxl@vger.kernel.org
S: Supported
--
MST
^ permalink raw reply related [flat|nested] 84+ messages in thread
* [PULL 55/78] hw/arm: remove pci-bridge/pci_expander_bridge.h from virt.c
2026-10-28 17:13 [PULL 00/78] pci, vhost, virtio, iommu, acpi: features, fixes Michael S. Tsirkin
` (53 preceding siblings ...)
2026-10-28 17:16 ` [PULL 54/78] MAINTAINERS: Update Junjie Cao's email address Michael S. Tsirkin
@ 2026-10-28 17:16 ` Michael S. Tsirkin
2026-10-28 17:16 ` [PULL 56/78] hw/pci-bridge: split CXL PXB into separate file Michael S. Tsirkin
` (23 subsequent siblings)
78 siblings, 0 replies; 84+ messages in thread
From: Michael S. Tsirkin @ 2026-10-28 17:16 UTC (permalink / raw)
To: qemu-devel
Cc: Peter Maydell, Daniel P. Berrangé,
Philippe Mathieu-Daudé, qemu-arm
From: Daniel P. Berrangé <berrange@redhat.com>
This include is redundant as the function is declares is not
referenced.
Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
Reviewed-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Message-ID: <20260928095646.3555447-2-berrange@redhat.com>
---
hw/arm/virt.c | 1 -
1 file changed, 1 deletion(-)
diff --git a/hw/arm/virt.c b/hw/arm/virt.c
index 0d56197dac..3ab3ad8c3b 100644
--- a/hw/arm/virt.c
+++ b/hw/arm/virt.c
@@ -58,7 +58,6 @@
#include "qemu/module.h"
#include "hw/pci/pci_bus.h"
#include "hw/pci-host/gpex.h"
-#include "hw/pci-bridge/pci_expander_bridge.h"
#include "hw/virtio/virtio-pci.h"
#include "hw/core/sysbus-fdt.h"
#include "hw/core/platform-bus.h"
--
MST
^ permalink raw reply related [flat|nested] 84+ messages in thread
* [PULL 56/78] hw/pci-bridge: split CXL PXB into separate file
2026-10-28 17:13 [PULL 00/78] pci, vhost, virtio, iommu, acpi: features, fixes Michael S. Tsirkin
` (54 preceding siblings ...)
2026-10-28 17:16 ` [PULL 55/78] hw/arm: remove pci-bridge/pci_expander_bridge.h from virt.c Michael S. Tsirkin
@ 2026-10-28 17:16 ` Michael S. Tsirkin
2026-10-28 17:16 ` [PULL 57/78] vhost-user: add missing stub for vhost_user_has_protocol_feature() Michael S. Tsirkin
` (22 subsequent siblings)
78 siblings, 0 replies; 84+ messages in thread
From: Michael S. Tsirkin @ 2026-10-28 17:16 UTC (permalink / raw)
To: qemu-devel
Cc: Peter Maydell, Daniel P. Berrangé,
Philippe Mathieu-Daudé, Jonathan Cameron, Junjie Cao,
Anisa Su, linux-cxl
From: Daniel P. Berrangé <berrange@redhat.com>
If CONFIG_CXL=n is added to the Kconfig, the build fails to link
the PXB device:
/usr/bin/ld.bfd: libsystem.a.p/hw_pci-bridge_pci_expander_bridge.c.o: in function `pxb_cxl_realize':
/home/berrange/src/virt/qemu/build/../hw/pci-bridge/pci_expander_bridge.c:202:(.text+0x330): undefined reference to `cxl_component_register_block_init'
/usr/bin/ld.bfd: libsystem.a.p/hw_pci-bridge_pci_expander_bridge.c.o: in function `pxb_cxl_dev_reset':
/home/berrange/src/virt/qemu/build/../hw/pci-bridge/pci_expander_bridge.c:309:(.text+0x768): undefined reference to `cxl_component_register_init_common'
We cannot use CONFIG_CXL in pci_expander_bridge.c because the
identifier is poisoned. Instead the CXL pieces must be split
off into a separate source file which can be conditionally
built.
With this change, the stubs file is no longer required, since
the code referencing the stub was already protected by CONFIG_CXL.
Fixes: 6e4e3ae936e6 ("hw/cxl/component: Implement host bridge MMIO (8.2.5, table 142)")
Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
Reviewed-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Message-ID: <20260928095646.3555447-3-berrange@redhat.com>
---
hw/pci-bridge/pci_expander_bridge_common.h | 28 +++
...der_bridge.h => pci_expander_bridge_cxl.h} | 0
hw/cxl/cxl-host.c | 2 +-
hw/pci-bridge/pci_expander_bridge.c | 194 +----------------
hw/pci-bridge/pci_expander_bridge_cxl.c | 201 ++++++++++++++++++
hw/pci-bridge/pci_expander_bridge_stubs.c | 14 --
hw/pci-bridge/meson.build | 2 +-
7 files changed, 240 insertions(+), 201 deletions(-)
create mode 100644 hw/pci-bridge/pci_expander_bridge_common.h
rename include/hw/pci-bridge/{pci_expander_bridge.h => pci_expander_bridge_cxl.h} (100%)
create mode 100644 hw/pci-bridge/pci_expander_bridge_cxl.c
delete mode 100644 hw/pci-bridge/pci_expander_bridge_stubs.c
diff --git a/hw/pci-bridge/pci_expander_bridge_common.h b/hw/pci-bridge/pci_expander_bridge_common.h
new file mode 100644
index 0000000000..d894ed0add
--- /dev/null
+++ b/hw/pci-bridge/pci_expander_bridge_common.h
@@ -0,0 +1,28 @@
+/*
+ * PCI Expander Bridge Device Emulation Common Code
+ *
+ * Copyright (C) 2015 Red Hat Inc
+ *
+ * Authors:
+ * Marcel Apfelbaum <marcel@redhat.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+enum BusType { PCI, PCIE, CXL };
+
+typedef struct PXBBus PXBBus;
+
+struct PXBBus {
+ /*< private >*/
+ PCIBus parent_obj;
+ /*< public >*/
+
+ char bus_path[8];
+};
+
+int pxb_bus_num(PCIBus *bus);
+void pxb_bus_class_init(ObjectClass *class, const void *data);
+bool pxb_dev_realize_common(PCIDevice *dev, enum BusType type,
+ Error **errp);
+void pxb_dev_exitfn(PCIDevice *pci_dev);
diff --git a/include/hw/pci-bridge/pci_expander_bridge.h b/include/hw/pci-bridge/pci_expander_bridge_cxl.h
similarity index 100%
rename from include/hw/pci-bridge/pci_expander_bridge.h
rename to include/hw/pci-bridge/pci_expander_bridge_cxl.h
diff --git a/hw/cxl/cxl-host.c b/hw/cxl/cxl-host.c
index e279ce5fe3..cda4dfed50 100644
--- a/hw/cxl/cxl-host.c
+++ b/hw/cxl/cxl-host.c
@@ -20,7 +20,7 @@
#include "hw/pci/pci_bridge.h"
#include "hw/pci/pci_host.h"
#include "hw/pci/pcie_port.h"
-#include "hw/pci-bridge/pci_expander_bridge.h"
+#include "hw/pci-bridge/pci_expander_bridge_cxl.h"
static void cxl_fixed_memory_window_config(CXLFixedMemoryWindowOptions *object,
int index, Error **errp)
diff --git a/hw/pci-bridge/pci_expander_bridge.c b/hw/pci-bridge/pci_expander_bridge.c
index 40ffbc4e08..2498df480b 100644
--- a/hw/pci-bridge/pci_expander_bridge.c
+++ b/hw/pci-bridge/pci_expander_bridge.c
@@ -18,57 +18,29 @@
#include "hw/pci/pcie_port.h"
#include "hw/core/qdev-properties.h"
#include "hw/pci/pci_bridge.h"
-#include "hw/pci-bridge/pci_expander_bridge.h"
-#include "hw/cxl/cxl.h"
#include "qemu/range.h"
#include "qemu/error-report.h"
#include "qemu/module.h"
#include "system/numa.h"
#include "hw/core/boards.h"
#include "qom/object.h"
-
-enum BusType { PCI, PCIE, CXL };
+#include "pci_expander_bridge_common.h"
#define TYPE_PXB_BUS "pxb-bus"
-typedef struct PXBBus PXBBus;
+
DECLARE_INSTANCE_CHECKER(PXBBus, PXB_BUS,
TYPE_PXB_BUS)
DECLARE_INSTANCE_CHECKER(PXBBus, PXB_PCIE_BUS,
TYPE_PXB_PCIE_BUS)
-DECLARE_INSTANCE_CHECKER(PXBBus, PXB_CXL_BUS,
- TYPE_PXB_CXL_BUS)
-
-struct PXBBus {
- /*< private >*/
- PCIBus parent_obj;
- /*< public >*/
-
- char bus_path[8];
-};
-
OBJECT_DECLARE_SIMPLE_TYPE(PXBPCIEDev, PXB_PCIE_DEV)
static GList *pxb_dev_list;
#define TYPE_PXB_HOST "pxb-host"
-CXLComponentState *cxl_get_hb_cstate(PCIHostState *hb)
-{
- CXLHost *host = PXB_CXL_HOST(hb);
-
- return &host->cxl_cstate;
-}
-
-bool cxl_get_hb_passthrough(PCIHostState *hb)
-{
- CXLHost *host = PXB_CXL_HOST(hb);
-
- return host->passthrough;
-}
-
-static int pxb_bus_num(PCIBus *bus)
+int pxb_bus_num(PCIBus *bus)
{
PXBDev *pxb = PXB_DEV(bus->parent_dev);
@@ -96,7 +68,7 @@ static void prop_pxb_uid_get(Object *obj, Visitor *v, const char *name,
visit_type_uint32(v, name, &uid, errp);
}
-static void pxb_bus_class_init(ObjectClass *class, const void *data)
+void pxb_bus_class_init(ObjectClass *class, const void *data)
{
PCIBusClass *pbc = PCI_BUS_CLASS(class);
@@ -123,20 +95,11 @@ static const TypeInfo pxb_pcie_bus_info = {
.class_init = pxb_bus_class_init,
};
-static const TypeInfo pxb_cxl_bus_info = {
- .name = TYPE_PXB_CXL_BUS,
- .parent = TYPE_CXL_BUS,
- .instance_size = sizeof(PXBBus),
- .class_init = pxb_bus_class_init,
-};
-
static const char *pxb_host_root_bus_path(PCIHostState *host_bridge,
PCIBus *rootbus)
{
- PXBBus *bus = pci_bus_is_cxl(rootbus) ?
- PXB_CXL_BUS(rootbus) :
- pci_bus_is_express(rootbus) ? PXB_PCIE_BUS(rootbus) :
- PXB_BUS(rootbus);
+ PXBBus *bus = pci_bus_is_express(rootbus) ?
+ PXB_PCIE_BUS(rootbus) : PXB_BUS(rootbus);
snprintf(bus->bus_path, 8, "0000:%02x", pxb_bus_num(rootbus));
return bus->bus_path;
@@ -192,64 +155,6 @@ static const TypeInfo pxb_host_info = {
.class_init = pxb_host_class_init,
};
-static void pxb_cxl_realize(DeviceState *dev, Error **errp)
-{
- SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
- CXLHost *cxl = PXB_CXL_HOST(dev);
- CXLComponentState *cxl_cstate = &cxl->cxl_cstate;
- struct MemoryRegion *mr = &cxl_cstate->crb.component_registers;
-
- cxl_component_register_block_init(OBJECT(dev), cxl_cstate,
- TYPE_PXB_CXL_HOST);
- sysbus_init_mmio(sbd, mr);
-}
-
-/*
- * Host bridge realization has no means of knowning state associated
- * with a particular machine. As such, it is nececssary to delay
- * final setup of the host bridge register space until later in the
- * machine bring up.
- */
-void pxb_cxl_hook_up_registers(CXLState *cxl_state, PCIBus *bus, Error **errp)
-{
- PXBCXLDev *pxb = PXB_CXL_DEV(pci_bridge_get_device(bus));
- CXLHost *cxl = pxb->cxl_host_bridge;
- CXLComponentState *cxl_cstate = &cxl->cxl_cstate;
- struct MemoryRegion *mr = &cxl_cstate->crb.component_registers;
- hwaddr offset;
-
- offset = memory_region_size(mr) * cxl_state->next_mr_idx;
- if (offset > memory_region_size(&cxl_state->host_mr)) {
- error_setg(errp, "Insufficient space for pxb cxl host register space");
- return;
- }
-
- memory_region_add_subregion(&cxl_state->host_mr, offset, mr);
- cxl_state->next_mr_idx++;
-}
-
-static void pxb_cxl_host_class_init(ObjectClass *class, const void *data)
-{
- DeviceClass *dc = DEVICE_CLASS(class);
- PCIHostBridgeClass *hc = PCI_HOST_BRIDGE_CLASS(class);
-
- hc->root_bus_path = pxb_host_root_bus_path;
- dc->fw_name = "cxl";
- dc->realize = pxb_cxl_realize;
- /* Reason: Internal part of the pxb/pxb-pcie device, not usable by itself */
- dc->user_creatable = false;
-}
-
-/*
- * This is a device to handle the MMIO for a CXL host bridge. It does nothing
- * else.
- */
-static const TypeInfo cxl_host_info = {
- .name = TYPE_PXB_CXL_HOST,
- .parent = TYPE_PCI_HOST_BRIDGE,
- .instance_size = sizeof(CXLHost),
- .class_init = pxb_cxl_host_class_init,
-};
/*
* Registers the PXB bus as a child of pci host root bus.
@@ -297,34 +202,6 @@ static int pxb_map_irq_fn(PCIDevice *pci_dev, int pin)
return pin - PCI_SLOT(pxb->devfn);
}
-static void pxb_cxl_dev_reset(DeviceState *dev)
-{
- CXLHost *cxl = PXB_CXL_DEV(dev)->cxl_host_bridge;
- CXLComponentState *cxl_cstate = &cxl->cxl_cstate;
- PCIHostState *hb = PCI_HOST_BRIDGE(cxl);
- uint32_t *reg_state = cxl_cstate->crb.cache_mem_registers;
- uint32_t *write_msk = cxl_cstate->crb.cache_mem_regs_write_mask;
- int dsp_count = 0;
-
- cxl_component_register_init_common(reg_state, write_msk, CXL2_RC, false);
- /*
- * The CXL specification allows for host bridges with no HDM decoders
- * if they only have a single root port.
- */
- if (!PXB_CXL_DEV(dev)->hdm_for_passthrough) {
- dsp_count = pcie_count_ds_ports(hb->bus);
- }
- /* Initial reset will have 0 dsp so wait until > 0 */
- if (dsp_count == 1) {
- cxl->passthrough = true;
- /* Set Capability ID in header to NONE */
- ARRAY_FIELD_DP32(reg_state, CXL_HDM_CAPABILITY_HEADER, ID, 0);
- } else {
- ARRAY_FIELD_DP32(reg_state, CXL_HDM_DECODER_CAPABILITY, TARGET_COUNT,
- 8);
- }
-}
-
static gint pxb_compare(gconstpointer a, gconstpointer b)
{
const PXBDev *pxb_a = a, *pxb_b = b;
@@ -334,8 +211,8 @@ static gint pxb_compare(gconstpointer a, gconstpointer b)
0;
}
-static bool pxb_dev_realize_common(PCIDevice *dev, enum BusType type,
- Error **errp)
+bool pxb_dev_realize_common(PCIDevice *dev, enum BusType type,
+ Error **errp)
{
PXBDev *pxb = PXB_DEV(dev);
DeviceState *ds, *bds = NULL;
@@ -417,7 +294,7 @@ static void pxb_dev_realize(PCIDevice *dev, Error **errp)
pxb_dev_realize_common(dev, PCI, errp);
}
-static void pxb_dev_exitfn(PCIDevice *pci_dev)
+void pxb_dev_exitfn(PCIDevice *pci_dev)
{
PXBDev *pxb = PXB_DEV(pci_dev);
@@ -496,67 +373,14 @@ static const TypeInfo pxb_pcie_dev_info = {
},
};
-static void pxb_cxl_dev_realize(PCIDevice *dev, Error **errp)
-{
- /* A CXL PXB's parent bus is still PCIe */
- if (!pci_bus_is_express(pci_get_bus(dev))) {
- error_setg(errp, "pxb-cxl devices cannot reside on a PCI bus");
- return;
- }
-
- if (!pxb_dev_realize_common(dev, CXL, errp)) {
- return;
- }
- pxb_cxl_dev_reset(DEVICE(dev));
-}
-
-static const Property pxb_cxl_dev_properties[] = {
- DEFINE_PROP_BOOL("hdm_for_passthrough", PXBCXLDev, hdm_for_passthrough, false),
-};
-
-static void pxb_cxl_dev_class_init(ObjectClass *klass, const void *data)
-{
- DeviceClass *dc = DEVICE_CLASS(klass);
- PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
-
- k->realize = pxb_cxl_dev_realize;
- k->exit = pxb_dev_exitfn;
- /*
- * XXX: These types of bridges don't actually show up in the hierarchy so
- * vendor, device, class, etc. ids are intentionally left out.
- */
-
- dc->desc = "CXL Host Bridge";
- device_class_set_props(dc, pxb_cxl_dev_properties);
- set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories);
-
- /* Host bridges aren't hotpluggable. FIXME: spec reference */
- dc->hotpluggable = false;
- device_class_set_legacy_reset(dc, pxb_cxl_dev_reset);
-}
-
-static const TypeInfo pxb_cxl_dev_info = {
- .name = TYPE_PXB_CXL_DEV,
- .parent = TYPE_PXB_PCIE_DEV,
- .instance_size = sizeof(PXBCXLDev),
- .class_init = pxb_cxl_dev_class_init,
- .interfaces =
- (const InterfaceInfo[]){
- { INTERFACE_CONVENTIONAL_PCI_DEVICE },
- {},
- },
-};
static void pxb_register_types(void)
{
type_register_static(&pxb_bus_info);
type_register_static(&pxb_pcie_bus_info);
- type_register_static(&pxb_cxl_bus_info);
type_register_static(&pxb_host_info);
- type_register_static(&cxl_host_info);
type_register_static(&pxb_dev_info);
type_register_static(&pxb_pcie_dev_info);
- type_register_static(&pxb_cxl_dev_info);
}
type_init(pxb_register_types)
diff --git a/hw/pci-bridge/pci_expander_bridge_cxl.c b/hw/pci-bridge/pci_expander_bridge_cxl.c
new file mode 100644
index 0000000000..ceb8928f8f
--- /dev/null
+++ b/hw/pci-bridge/pci_expander_bridge_cxl.c
@@ -0,0 +1,201 @@
+/*
+ * CXL PCI Expander Bridge Device Emulation
+ *
+ * Copyright (C) 2015 Red Hat Inc
+ *
+ * Authors:
+ * Marcel Apfelbaum <marcel@redhat.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+#include "hw/pci/pci.h"
+#include "hw/pci/pci_bus.h"
+#include "hw/pci/pci_bridge.h"
+#include "hw/pci/pcie_port.h"
+#include "hw/core/qdev-properties.h"
+#include "hw/pci-bridge/pci_expander_bridge_cxl.h"
+#include "qom/object.h"
+#include "pci_expander_bridge_common.h"
+
+DECLARE_INSTANCE_CHECKER(PXBBus, PXB_CXL_BUS,
+ TYPE_PXB_CXL_BUS)
+
+CXLComponentState *cxl_get_hb_cstate(PCIHostState *hb)
+{
+ CXLHost *host = PXB_CXL_HOST(hb);
+
+ return &host->cxl_cstate;
+}
+
+bool cxl_get_hb_passthrough(PCIHostState *hb)
+{
+ CXLHost *host = PXB_CXL_HOST(hb);
+
+ return host->passthrough;
+}
+
+static const char *pxb_cxl_host_root_bus_path(PCIHostState *host_bridge,
+ PCIBus *rootbus)
+{
+ PXBBus *bus = PXB_CXL_BUS(rootbus);
+ snprintf(bus->bus_path, 8, "0000:%02x", pxb_bus_num(rootbus));
+ return bus->bus_path;
+}
+
+
+static const TypeInfo pxb_cxl_bus_info = {
+ .name = TYPE_PXB_CXL_BUS,
+ .parent = TYPE_CXL_BUS,
+ .instance_size = sizeof(PXBBus),
+ .class_init = pxb_bus_class_init,
+};
+
+static void pxb_cxl_realize(DeviceState *dev, Error **errp)
+{
+ SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
+ CXLHost *cxl = PXB_CXL_HOST(dev);
+ CXLComponentState *cxl_cstate = &cxl->cxl_cstate;
+ struct MemoryRegion *mr = &cxl_cstate->crb.component_registers;
+
+ cxl_component_register_block_init(OBJECT(dev), cxl_cstate,
+ TYPE_PXB_CXL_HOST);
+ sysbus_init_mmio(sbd, mr);
+}
+
+/*
+ * Host bridge realization has no means of knowning state associated
+ * with a particular machine. As such, it is nececssary to delay
+ * final setup of the host bridge register space until later in the
+ * machine bring up.
+ */
+void pxb_cxl_hook_up_registers(CXLState *cxl_state, PCIBus *bus, Error **errp)
+{
+ PXBCXLDev *pxb = PXB_CXL_DEV(pci_bridge_get_device(bus));
+ CXLHost *cxl = pxb->cxl_host_bridge;
+ CXLComponentState *cxl_cstate = &cxl->cxl_cstate;
+ struct MemoryRegion *mr = &cxl_cstate->crb.component_registers;
+ hwaddr offset;
+
+ offset = memory_region_size(mr) * cxl_state->next_mr_idx;
+ if (offset > memory_region_size(&cxl_state->host_mr)) {
+ error_setg(errp, "Insufficient space for pxb cxl host register space");
+ return;
+ }
+
+ memory_region_add_subregion(&cxl_state->host_mr, offset, mr);
+ cxl_state->next_mr_idx++;
+}
+
+static void pxb_cxl_host_class_init(ObjectClass *class, const void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(class);
+ PCIHostBridgeClass *hc = PCI_HOST_BRIDGE_CLASS(class);
+
+ hc->root_bus_path = pxb_cxl_host_root_bus_path;
+ dc->fw_name = "cxl";
+ dc->realize = pxb_cxl_realize;
+ /* Reason: Internal part of the pxb/pxb-pcie device, not usable by itself */
+ dc->user_creatable = false;
+}
+
+/*
+ * This is a device to handle the MMIO for a CXL host bridge. It does nothing
+ * else.
+ */
+static const TypeInfo cxl_host_info = {
+ .name = TYPE_PXB_CXL_HOST,
+ .parent = TYPE_PCI_HOST_BRIDGE,
+ .instance_size = sizeof(CXLHost),
+ .class_init = pxb_cxl_host_class_init,
+};
+
+static void pxb_cxl_dev_reset(DeviceState *dev)
+{
+ CXLHost *cxl = PXB_CXL_DEV(dev)->cxl_host_bridge;
+ CXLComponentState *cxl_cstate = &cxl->cxl_cstate;
+ PCIHostState *hb = PCI_HOST_BRIDGE(cxl);
+ uint32_t *reg_state = cxl_cstate->crb.cache_mem_registers;
+ uint32_t *write_msk = cxl_cstate->crb.cache_mem_regs_write_mask;
+ int dsp_count = 0;
+
+ cxl_component_register_init_common(reg_state, write_msk, CXL2_RC, false);
+ /*
+ * The CXL specification allows for host bridges with no HDM decoders
+ * if they only have a single root port.
+ */
+ if (!PXB_CXL_DEV(dev)->hdm_for_passthrough) {
+ dsp_count = pcie_count_ds_ports(hb->bus);
+ }
+ /* Initial reset will have 0 dsp so wait until > 0 */
+ if (dsp_count == 1) {
+ cxl->passthrough = true;
+ /* Set Capability ID in header to NONE */
+ ARRAY_FIELD_DP32(reg_state, CXL_HDM_CAPABILITY_HEADER, ID, 0);
+ } else {
+ ARRAY_FIELD_DP32(reg_state, CXL_HDM_DECODER_CAPABILITY, TARGET_COUNT,
+ 8);
+ }
+}
+
+static void pxb_cxl_dev_realize(PCIDevice *dev, Error **errp)
+{
+ /* A CXL PXB's parent bus is still PCIe */
+ if (!pci_bus_is_express(pci_get_bus(dev))) {
+ error_setg(errp, "pxb-cxl devices cannot reside on a PCI bus");
+ return;
+ }
+
+ if (!pxb_dev_realize_common(dev, CXL, errp)) {
+ return;
+ }
+ pxb_cxl_dev_reset(DEVICE(dev));
+}
+
+static const Property pxb_cxl_dev_properties[] = {
+ DEFINE_PROP_BOOL("hdm_for_passthrough", PXBCXLDev,
+ hdm_for_passthrough, false),
+};
+
+static void pxb_cxl_dev_class_init(ObjectClass *klass, const void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
+
+ k->realize = pxb_cxl_dev_realize;
+ k->exit = pxb_dev_exitfn;
+ /*
+ * XXX: These types of bridges don't actually show up in the hierarchy so
+ * vendor, device, class, etc. ids are intentionally left out.
+ */
+
+ dc->desc = "CXL Host Bridge";
+ device_class_set_props(dc, pxb_cxl_dev_properties);
+ set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories);
+
+ /* Host bridges aren't hotpluggable. FIXME: spec reference */
+ dc->hotpluggable = false;
+ device_class_set_legacy_reset(dc, pxb_cxl_dev_reset);
+}
+
+static const TypeInfo pxb_cxl_dev_info = {
+ .name = TYPE_PXB_CXL_DEV,
+ .parent = TYPE_PXB_PCIE_DEV,
+ .instance_size = sizeof(PXBCXLDev),
+ .class_init = pxb_cxl_dev_class_init,
+ .interfaces =
+ (const InterfaceInfo[]){
+ { INTERFACE_CONVENTIONAL_PCI_DEVICE },
+ {},
+ },
+};
+
+static void pxb_cxl_register_types(void)
+{
+ type_register_static(&pxb_cxl_bus_info);
+ type_register_static(&cxl_host_info);
+ type_register_static(&pxb_cxl_dev_info);
+}
+
+type_init(pxb_cxl_register_types)
diff --git a/hw/pci-bridge/pci_expander_bridge_stubs.c b/hw/pci-bridge/pci_expander_bridge_stubs.c
deleted file mode 100644
index b35180311f..0000000000
--- a/hw/pci-bridge/pci_expander_bridge_stubs.c
+++ /dev/null
@@ -1,14 +0,0 @@
-/*
- * SPDX-License-Identifier: GPL-2.0-or-later
- *
- * Stubs for calls made from machines to handle the case where CONFIG_PXB
- * is not enabled.
- */
-#include "qemu/osdep.h"
-#include "qapi/error.h"
-#include "hw/pci/pci.h"
-#include "hw/pci/pci_bus.h"
-#include "hw/pci-bridge/pci_expander_bridge.h"
-#include "hw/cxl/cxl.h"
-
-void pxb_cxl_hook_up_registers(CXLState *state, PCIBus *bus, Error **errp) {};
diff --git a/hw/pci-bridge/meson.build b/hw/pci-bridge/meson.build
index aa9959df04..563d3f5147 100644
--- a/hw/pci-bridge/meson.build
+++ b/hw/pci-bridge/meson.build
@@ -6,7 +6,7 @@ pci_ss.add(when: 'CONFIG_PCIE_PORT', if_true: files('pcie_root_port.c', 'gen_pci
'gen_pcie_upstream_port.c', 'gen_pcie_downstream_port.c'))
pci_ss.add(when: 'CONFIG_PCIE_PCI_BRIDGE', if_true: files('pcie_pci_bridge.c'))
pci_ss.add(when: 'CONFIG_PXB', if_true: files('pci_expander_bridge.c'))
-stub_ss.add(files('pci_expander_bridge_stubs.c'))
+pci_ss.add(when: 'CONFIG_CXL', if_true: files('pci_expander_bridge_cxl.c'))
pci_ss.add(when: 'CONFIG_XIO3130', if_true: files('xio3130_upstream.c', 'xio3130_downstream.c'))
pci_ss.add(when: 'CONFIG_CXL', if_true: files('cxl_root_port.c', 'cxl_upstream.c', 'cxl_downstream.c'))
--
MST
^ permalink raw reply related [flat|nested] 84+ messages in thread
* [PULL 57/78] vhost-user: add missing stub for vhost_user_has_protocol_feature()
2026-10-28 17:13 [PULL 00/78] pci, vhost, virtio, iommu, acpi: features, fixes Michael S. Tsirkin
` (55 preceding siblings ...)
2026-10-28 17:16 ` [PULL 56/78] hw/pci-bridge: split CXL PXB into separate file Michael S. Tsirkin
@ 2026-10-28 17:16 ` Michael S. Tsirkin
2026-10-28 17:16 ` [PULL 58/78] vhost: move skip-drain feature check to vhost-user Michael S. Tsirkin
` (21 subsequent siblings)
78 siblings, 0 replies; 84+ messages in thread
From: Michael S. Tsirkin @ 2026-10-28 17:16 UTC (permalink / raw)
To: qemu-devel
Cc: Peter Maydell, Alexandr Moshkov, Clément Chigot,
Stefano Garzarella
From: Alexandr Moshkov <dtalexundeer@yandex-team.ru>
Currently, building with --disable-vhost-user fails to link when other
vhost backends are enabled: vhost.c calls vhost_user_has_protocol_feature(),
but its implementation is not built.
Add a stub returning false to fix the link error.
Signed-off-by: Alexandr Moshkov <dtalexundeer@yandex-team.ru>
Fixes: 5eb2347867ee ("vhost-user: add skip_drain param to do_vhost_virtqueue_stop")
Tested-by: Clément Chigot <chigot@adacore.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Message-ID: <20260929101623.1352299-1-dtalexundeer@yandex-team.ru>
---
hw/virtio/vhost-user-stub.c | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/hw/virtio/vhost-user-stub.c b/hw/virtio/vhost-user-stub.c
index 45559788dd..9d2e521788 100644
--- a/hw/virtio/vhost-user-stub.c
+++ b/hw/virtio/vhost-user-stub.c
@@ -4,3 +4,8 @@
void vhost_user_qmp_status(struct vhost_dev *dev, VirtioStatus *status)
{
}
+
+bool vhost_user_has_protocol_feature(struct vhost_dev *dev, uint64_t feature)
+{
+ return false;
+}
--
MST
^ permalink raw reply related [flat|nested] 84+ messages in thread
* [PULL 58/78] vhost: move skip-drain feature check to vhost-user
2026-10-28 17:13 [PULL 00/78] pci, vhost, virtio, iommu, acpi: features, fixes Michael S. Tsirkin
` (56 preceding siblings ...)
2026-10-28 17:16 ` [PULL 57/78] vhost-user: add missing stub for vhost_user_has_protocol_feature() Michael S. Tsirkin
@ 2026-10-28 17:16 ` Michael S. Tsirkin
2026-10-28 17:16 ` [PULL 59/78] virtio-snd: don't use virtio_snd_pcm_get_stream() in virtio_snd_post_load() Michael S. Tsirkin
` (20 subsequent siblings)
78 siblings, 0 replies; 84+ messages in thread
From: Michael S. Tsirkin @ 2026-10-28 17:16 UTC (permalink / raw)
To: qemu-devel
Cc: Peter Maydell, Alexandr Moshkov, Clément Chigot,
Stefano Garzarella, vhost-user
From: Alexandr Moshkov <dtalexundeer@yandex-team.ru>
Currently, do_vhost_virtqueue_stop() calls
vhost_user_has_protocol_feature(), causing a link failure with
--disable-vhost-user when other vhost backends are enabled.
Move the assertion to vhost_user_get_vring_base_skip_drain(), keeping
the protocol feature check in the vhost-user backend and removing
the dependency from the common vhost code.
Reported-by: Clément Chigot <chigot@adacore.com>
Fixes: 5eb2347867ee ("vhost-user: add skip_drain param to do_vhost_virtqueue_stop")
Signed-off-by: Alexandr Moshkov <dtalexundeer@yandex-team.ru>
Acked-by: Stefano Garzarella <sgarzare@redhat.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Message-ID: <20260929122223.2164612-1-dtalexundeer@yandex-team.ru>
---
hw/virtio/vhost-user.c | 4 +---
hw/virtio/vhost.c | 5 -----
2 files changed, 1 insertion(+), 8 deletions(-)
diff --git a/hw/virtio/vhost-user.c b/hw/virtio/vhost-user.c
index 3402accc6f..402d9603c0 100644
--- a/hw/virtio/vhost-user.c
+++ b/hw/virtio/vhost-user.c
@@ -1463,9 +1463,7 @@ static int vhost_user_get_vring_base_skip_drain(struct vhost_dev *dev,
bool skip_drain_supported = vhost_user_has_protocol_feature(dev,
VHOST_USER_PROTOCOL_F_GET_VRING_BASE_SKIP_DRAIN);
- if (!skip_drain_supported) {
- return 0;
- }
+ assert(skip_drain_supported);
return get_vring_base(dev, ring, true);
}
diff --git a/hw/virtio/vhost.c b/hw/virtio/vhost.c
index cbf6e82afb..b25919c5c7 100644
--- a/hw/virtio/vhost.c
+++ b/hw/virtio/vhost.c
@@ -23,7 +23,6 @@
#include "qemu/log.h"
#include "standard-headers/linux/vhost_types.h"
#include "hw/virtio/virtio-bus.h"
-#include "hw/virtio/vhost-user.h"
#include "hw/mem/memory-device.h"
#include "migration/blocker.h"
#include "migration/misc.h"
@@ -1554,10 +1553,6 @@ static int do_vhost_virtqueue_stop(struct vhost_dev *dev,
unsigned idx, bool force,
bool skip_drain)
{
- if (skip_drain) {
- assert(vhost_user_has_protocol_feature(dev,
- VHOST_USER_PROTOCOL_F_GET_VRING_BASE_SKIP_DRAIN));
- }
int vhost_vq_index = dev->vhost_ops->vhost_get_vq_index(dev, idx);
struct vhost_vring_state state = {
.index = vhost_vq_index,
--
MST
^ permalink raw reply related [flat|nested] 84+ messages in thread
* [PULL 59/78] virtio-snd: don't use virtio_snd_pcm_get_stream() in virtio_snd_post_load()
2026-10-28 17:13 [PULL 00/78] pci, vhost, virtio, iommu, acpi: features, fixes Michael S. Tsirkin
` (57 preceding siblings ...)
2026-10-28 17:16 ` [PULL 58/78] vhost: move skip-drain feature check to vhost-user Michael S. Tsirkin
@ 2026-10-28 17:16 ` Michael S. Tsirkin
2026-10-28 17:16 ` [PULL 60/78] vhost-user: add dedicated mailing list Michael S. Tsirkin
` (19 subsequent siblings)
78 siblings, 0 replies; 84+ messages in thread
From: Michael S. Tsirkin @ 2026-10-28 17:16 UTC (permalink / raw)
To: qemu-devel
Cc: Peter Maydell, Alexander Mikhalitsyn, Gerd Hoffmann,
Manos Pitsidianakis
From: Alexander Mikhalitsyn <aleksandr.mikhalitsyn@futurfusion.io>
Coverity suspects nullptr-deref, because we don't check a return value
of virtio_snd_pcm_get_stream() call in virtio_snd_post_load().
While, techically this code has no issue it's better to change it and
as Peter has pointed out [1] just follow the same pattern as in other places
of virtio-snd code:
for (uint32_t i = 0; i < vsnd->snd_conf.streams; i++) {
VirtIOSoundPCMStream *stream = &vsnd->streams[i];
...
}
this way it's perfectly clear that an index is always within the bounds.
Link: https://lore.kernel.org/qemu-devel/CAFEAcA-K7L5NWk0cC9R8yVvojSS6O6Eh+i9KXP8Lijo4Tjnd-Q@mail.gmail.com [1]
Resolves: Coverity CID 1687010
Reported-by: Peter Maydell <peter.maydell@linaro.org>
Signed-off-by: Alexander Mikhalitsyn <aleksandr.mikhalitsyn@futurfusion.io>
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Message-ID: <20261002165039.212819-1-alexander@mihalicyn.com>
---
hw/audio/virtio-snd.c | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/hw/audio/virtio-snd.c b/hw/audio/virtio-snd.c
index 6df7ea0eb0..fb22d79949 100644
--- a/hw/audio/virtio-snd.c
+++ b/hw/audio/virtio-snd.c
@@ -1448,9 +1448,8 @@ static int virtio_snd_post_load(VirtIODevice *vdev)
uint32_t i;
for (i = 0; i < s->snd_conf.streams; i++) {
- struct VirtIOSoundPCMStream *stream;
+ VirtIOSoundPCMStream *stream = &s->streams[i];
- stream = virtio_snd_pcm_get_stream(s, i);
if (virtio_snd_pcm_state_prepared(stream->state)) {
virtio_snd_pcm_open(stream);
--
MST
^ permalink raw reply related [flat|nested] 84+ messages in thread
* [PULL 60/78] vhost-user: add dedicated mailing list
2026-10-28 17:13 [PULL 00/78] pci, vhost, virtio, iommu, acpi: features, fixes Michael S. Tsirkin
` (58 preceding siblings ...)
2026-10-28 17:16 ` [PULL 59/78] virtio-snd: don't use virtio_snd_pcm_get_stream() in virtio_snd_post_load() Michael S. Tsirkin
@ 2026-10-28 17:16 ` Michael S. Tsirkin
2026-10-28 17:16 ` [PULL 61/78] amd_iommu: Assign ioapic_as only when interrupt remapping is supported Michael S. Tsirkin
` (18 subsequent siblings)
78 siblings, 0 replies; 84+ messages in thread
From: Michael S. Tsirkin @ 2026-10-28 17:16 UTC (permalink / raw)
To: qemu-devel
Cc: Peter Maydell, Stefano Garzarella, Pierrick Bouvier, vhost-user
From: Stefano Garzarella <sgarzare@redhat.com>
During the "Virtio and Vhost-User Ecosystem for Production and
Automotive Systems" BoF at LPC 2026 [1] we discussed and agreed on
creating a new vhost-user mailing list, mainly for changes to the
specification and discussions around the protocol.
The main reason is that the vhost-user specification is also used
by other VMMs and implemented by several back-ends outside the QEMU
repository, whose developers often do not follow qemu-devel.
Add a paragraph in the specification about the new mailing list and
a dedicated MAINTAINERS entry for the vhost-user protocol, covering
the specification, the QEMU front-end and libvhost-user (back-end
library).
[1] https://lpc.events/event/20/contributions/2365/
Signed-off-by: Stefano Garzarella <sgarzare@redhat.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Message-ID: <20261023162441.89450-1-sgarzare@redhat.com>
---
MAINTAINERS | 12 ++++++++++--
docs/interop/vhost-user.rst | 6 ++++++
2 files changed, 16 insertions(+), 2 deletions(-)
diff --git a/MAINTAINERS b/MAINTAINERS
index d7ec12ac39..04842adba6 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -2544,19 +2544,27 @@ M: Michael S. Tsirkin <mst@redhat.com>
R: Stefano Garzarella <sgarzare@redhat.com>
S: Supported
F: hw/*/*vhost*
-F: docs/interop/vhost-user*
F: docs/system/devices/virtio/vhost-user*
F: contrib/vhost-user-*/
F: backends/*vhost*
F: include/system/vhost-user-backend.h
F: include/hw/virtio/vhost*
F: include/*/vhost*
-F: subprojects/libvhost-user/
F: block/export/vhost-user*
F: util/vhost-user-server.c
F: net/vhost*
F: tests/functional/x86_64/test_vhost_user_bridge.py
+vhost-user protocol
+M: Michael S. Tsirkin <mst@redhat.com>
+R: Stefano Garzarella <sgarzare@redhat.com>
+L: vhost-user@lists.linux.dev
+S: Supported
+F: docs/interop/vhost-user*
+F: hw/virtio/vhost-user.c
+F: include/hw/virtio/vhost-user.h
+F: subprojects/libvhost-user/
+
vhost-shadow-virtqueue
R: Eugenio Pérez <eperezma@redhat.com>
F: hw/virtio/vhost-shadow-virtqueue.*
diff --git a/docs/interop/vhost-user.rst b/docs/interop/vhost-user.rst
index 488214a306..c31a75def3 100644
--- a/docs/interop/vhost-user.rst
+++ b/docs/interop/vhost-user.rst
@@ -38,6 +38,12 @@ conventions <backend_conventions>`.
The *front-end* and *back-end* can be either a client (i.e. connecting) or
server (listening) in the socket communication.
+The protocol is developed and discussed on the `vhost-user mailing list
+<mailto:vhost-user@lists.linux.dev>`_, archived at
+https://lore.kernel.org/vhost-user/. Until October 2026, when this list
+was created, discussions took place on the qemu-devel mailing list,
+archived at https://lore.kernel.org/qemu-devel/.
+
Support for platforms other than Linux
--------------------------------------
--
MST
^ permalink raw reply related [flat|nested] 84+ messages in thread
* [PULL 61/78] amd_iommu: Assign ioapic_as only when interrupt remapping is supported.
2026-10-28 17:13 [PULL 00/78] pci, vhost, virtio, iommu, acpi: features, fixes Michael S. Tsirkin
` (59 preceding siblings ...)
2026-10-28 17:16 ` [PULL 60/78] vhost-user: add dedicated mailing list Michael S. Tsirkin
@ 2026-10-28 17:16 ` Michael S. Tsirkin
2026-10-28 17:16 ` [PULL 62/78] x86-iommu: Introduce x86_iommu_device_get() for x86-iommu retrieval Michael S. Tsirkin
` (17 subsequent siblings)
78 siblings, 0 replies; 84+ messages in thread
From: Michael S. Tsirkin @ 2026-10-28 17:16 UTC (permalink / raw)
To: qemu-devel
Cc: Peter Maydell, Sairaj Kodilkar, Alejandro Jimenez, Paolo Bonzini,
Richard Henderson
From: Sairaj Kodilkar <sarunkod@amd.com>
When interrupt remapping is disabled (intremap=off), the IOAPIC should
use the system address space instead of the IOMMU address space.
Currently, the IOAPIC is unconditionally assigned to the IOMMU address
space, which causes IOAPIC-triggered interrupts to be incorrectly
translated by the IOMMU even when interrupt remapping is disabled.
Signed-off-by: Sairaj Kodilkar <sarunkod@amd.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Message-ID: <20261014053433.24564-2-sarunkod@amd.com>
---
hw/i386/amd_iommu.c | 12 ++++++++++--
1 file changed, 10 insertions(+), 2 deletions(-)
diff --git a/hw/i386/amd_iommu.c b/hw/i386/amd_iommu.c
index 56e7815412..817320c51c 100644
--- a/hw/i386/amd_iommu.c
+++ b/hw/i386/amd_iommu.c
@@ -2733,8 +2733,16 @@ static void amdvi_sysbus_realize(DeviceState *dev, Error **errp)
memory_region_add_subregion_overlap(&s->mr_sys, AMDVI_INT_ADDR_FIRST,
&s->mr_ir, 1);
- /* Pseudo address space under root PCI bus. */
- x86ms->ioapic_as = amdvi_host_dma_iommu(bus, s, AMDVI_IOAPIC_SB_DEVID);
+ /**
+ * Pseudo address space under root PCI bus.
+ * The linux kernel disables the intremap when it cannot find IOAPIC under
+ * AMD IOMMU IVRS, hence create a ioapic_as with root bus even though AMD
+ * IOMMU is not serving devices attached to root bus.
+ */
+ if (x86_iommu_ir_supported(X86_IOMMU_DEVICE(s))) {
+ x86ms->ioapic_as = amdvi_host_dma_iommu(pcms->pcibus, s,
+ AMDVI_IOAPIC_SB_DEVID);
+ }
if (kvm_enabled() && x86ms->apic_id_limit > 255 && !s->xtsup) {
error_report("AMD IOMMU with x2APIC configuration requires xtsup=on");
--
MST
^ permalink raw reply related [flat|nested] 84+ messages in thread
* [PULL 62/78] x86-iommu: Introduce x86_iommu_device_get() for x86-iommu retrieval
2026-10-28 17:13 [PULL 00/78] pci, vhost, virtio, iommu, acpi: features, fixes Michael S. Tsirkin
` (60 preceding siblings ...)
2026-10-28 17:16 ` [PULL 61/78] amd_iommu: Assign ioapic_as only when interrupt remapping is supported Michael S. Tsirkin
@ 2026-10-28 17:16 ` Michael S. Tsirkin
2026-10-28 17:16 ` [PULL 63/78] amd_iommu: Add support for get_x86_iommu callback Michael S. Tsirkin
` (16 subsequent siblings)
78 siblings, 0 replies; 84+ messages in thread
From: Michael S. Tsirkin @ 2026-10-28 17:16 UTC (permalink / raw)
To: qemu-devel
Cc: Peter Maydell, Sairaj Kodilkar, Paolo Bonzini, Richard Henderson
From: Sairaj Kodilkar <sarunkod@amd.com>
kvm_arch_fixup_msi_route() and kvm_arch_add_msi_route_post() use
x86_iommu_get_default() whenever an IOMMU is present. This selects the
correct unit only when a single IOMMU is attached to the system bus.
The following patches allow an IOMMU to attach to a different bus and
isolate the subset of devices below it. Each IOMMU then serves only part
of the PCI topology, so interrupt remapping must determine the IOMMU that
actually serves a given device.
Add x86_iommu_device_get() to locate the IOMMU bus for a PCI device and
retrieve its X86IOMMUState through a new get_x86_iommu callback.
Signed-off-by: Sairaj Kodilkar <sarunkod@amd.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Message-ID: <20261014053433.24564-3-sarunkod@amd.com>
---
include/hw/i386/x86-iommu.h | 6 ++++++
include/hw/pci/pci.h | 12 ++++++++++++
hw/i386/x86-iommu-stub.c | 5 +++++
hw/i386/x86-iommu.c | 16 ++++++++++++++++
4 files changed, 39 insertions(+)
diff --git a/include/hw/i386/x86-iommu.h b/include/hw/i386/x86-iommu.h
index 0a1e1ba725..eb575271e9 100644
--- a/include/hw/i386/x86-iommu.h
+++ b/include/hw/i386/x86-iommu.h
@@ -134,6 +134,12 @@ struct X86IOMMU_MSIMessage {
*/
X86IOMMUState *x86_iommu_get_default(void);
+/**
+ * x86_iommu_device_get - get IOMMU device for given PCI device
+ * @return: pointer to default IOMMU device
+ */
+X86IOMMUState *x86_iommu_device_get(PCIDevice *dev);
+
/**
* x86_iommu_iec_register_notifier - register IEC (Interrupt Entry
* Cache) notifiers
diff --git a/include/hw/pci/pci.h b/include/hw/pci/pci.h
index 6520886855..359331102f 100644
--- a/include/hw/pci/pci.h
+++ b/include/hw/pci/pci.h
@@ -700,6 +700,18 @@ typedef struct PCIIOMMUOps {
* Returns: the guest physical address of the MSI doorbell.
*/
uint64_t (*get_msi_direct_gpa)(PCIBus *bus, void *opaque, int devfn);
+
+ /**
+ * @get_x86_iommu: get parent x86-iommu for given hardware (AMD/Intel)
+ *
+ * Callback is required if IOMMU defines x86->int_remap()
+ *
+ * @opaque: the data passed to pci_setup_iommu()
+ *
+ * @x86_iommu: A pointer to store the x86-iommu object
+ */
+ int (*get_x86_iommu)(void *opaque, void **x86_iommu);
+
} PCIIOMMUOps;
bool pci_device_get_iommu_bus_devfn(PCIDevice *dev, PCIBus **piommu_bus,
diff --git a/hw/i386/x86-iommu-stub.c b/hw/i386/x86-iommu-stub.c
index 781b5ff922..b0383d2290 100644
--- a/hw/i386/x86-iommu-stub.c
+++ b/hw/i386/x86-iommu-stub.c
@@ -32,6 +32,11 @@ X86IOMMUState *x86_iommu_get_default(void)
return NULL;
}
+X86IOMMUState *x86_iommu_device_get(PCIDevice *dev)
+{
+ return NULL;
+}
+
bool x86_iommu_ir_supported(X86IOMMUState *s)
{
return false;
diff --git a/hw/i386/x86-iommu.c b/hw/i386/x86-iommu.c
index a57445404e..601db5cfea 100644
--- a/hw/i386/x86-iommu.c
+++ b/hw/i386/x86-iommu.c
@@ -26,6 +26,7 @@
#include "qemu/error-report.h"
#include "trace.h"
#include "system/kvm.h"
+#include "hw/pci/pci_bus.h"
void x86_iommu_iec_register_notifier(X86IOMMUState *iommu,
iec_notify_fn fn, void *data)
@@ -90,6 +91,21 @@ X86IOMMUState *x86_iommu_get_default(void)
return NULL;
}
+X86IOMMUState *x86_iommu_device_get(PCIDevice *dev)
+{
+ PCIBus *iommu_bus;
+ X86IOMMUState *iommu = NULL;
+
+ pci_device_get_iommu_bus_devfn(dev, &iommu_bus, NULL, NULL);
+
+ if (iommu_bus && iommu_bus->iommu_ops->get_x86_iommu) {
+ iommu_bus->iommu_ops->get_x86_iommu(iommu_bus->iommu_opaque,
+ (void **)&iommu);
+ }
+
+ return iommu;
+}
+
static void x86_iommu_realize(DeviceState *dev, Error **errp)
{
X86IOMMUState *x86_iommu = X86_IOMMU_DEVICE(dev);
--
MST
^ permalink raw reply related [flat|nested] 84+ messages in thread
* [PULL 63/78] amd_iommu: Add support for get_x86_iommu callback
2026-10-28 17:13 [PULL 00/78] pci, vhost, virtio, iommu, acpi: features, fixes Michael S. Tsirkin
` (61 preceding siblings ...)
2026-10-28 17:16 ` [PULL 62/78] x86-iommu: Introduce x86_iommu_device_get() for x86-iommu retrieval Michael S. Tsirkin
@ 2026-10-28 17:16 ` Michael S. Tsirkin
2026-10-28 17:16 ` [PULL 64/78] intel_iommu: " Michael S. Tsirkin
` (15 subsequent siblings)
78 siblings, 0 replies; 84+ messages in thread
From: Michael S. Tsirkin @ 2026-10-28 17:16 UTC (permalink / raw)
To: qemu-devel
Cc: Peter Maydell, Sairaj Kodilkar, Alejandro Jimenez, Paolo Bonzini,
Richard Henderson
From: Sairaj Kodilkar <sarunkod@amd.com>
Return X86IOMMUState during get_x86_iommu callback.
Signed-off-by: Sairaj Kodilkar <sarunkod@amd.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Message-ID: <20261014053433.24564-4-sarunkod@amd.com>
---
hw/i386/amd_iommu.c | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/hw/i386/amd_iommu.c b/hw/i386/amd_iommu.c
index 817320c51c..b2df0e9533 100644
--- a/hw/i386/amd_iommu.c
+++ b/hw/i386/amd_iommu.c
@@ -2472,8 +2472,16 @@ static AddressSpace *amdvi_host_dma_iommu(PCIBus *bus, void *opaque, int devfn)
return &amdvi_dev_as->as;
}
+static int amdvi_get_x86_iommu(void *opaque, void **x86_iommu)
+{
+ AMDVIState *s = opaque;
+ *x86_iommu = X86_IOMMU_DEVICE(s);
+ return 0;
+}
+
static const PCIIOMMUOps amdvi_iommu_ops = {
.get_address_space = amdvi_host_dma_iommu,
+ .get_x86_iommu = amdvi_get_x86_iommu,
};
static const MemoryRegionOps mmio_mem_ops = {
--
MST
^ permalink raw reply related [flat|nested] 84+ messages in thread
* [PULL 64/78] intel_iommu: Add support for get_x86_iommu callback
2026-10-28 17:13 [PULL 00/78] pci, vhost, virtio, iommu, acpi: features, fixes Michael S. Tsirkin
` (62 preceding siblings ...)
2026-10-28 17:16 ` [PULL 63/78] amd_iommu: Add support for get_x86_iommu callback Michael S. Tsirkin
@ 2026-10-28 17:16 ` Michael S. Tsirkin
2026-10-28 17:16 ` [PULL 65/78] i386/kvm: Use x86_iommu_device_get() for per-device IOMMU lookup Michael S. Tsirkin
` (14 subsequent siblings)
78 siblings, 0 replies; 84+ messages in thread
From: Michael S. Tsirkin @ 2026-10-28 17:16 UTC (permalink / raw)
To: qemu-devel
Cc: Peter Maydell, Sairaj Kodilkar, Clement Mathieu--Drif, Jason Wang,
Yi Liu, Paolo Bonzini, Richard Henderson
From: Sairaj Kodilkar <sarunkod@amd.com>
Return X86IOMMUState during get_x86_iommu callback.
Signed-off-by: Sairaj Kodilkar <sarunkod@amd.com>
Reviewed-by: Clement Mathieu--Drif <clement.mathieu--drif@bull.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Message-ID: <20261014053433.24564-5-sarunkod@amd.com>
---
hw/i386/intel_iommu.c | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/hw/i386/intel_iommu.c b/hw/i386/intel_iommu.c
index 350d2b7753..3686ca8ba7 100644
--- a/hw/i386/intel_iommu.c
+++ b/hw/i386/intel_iommu.c
@@ -5485,6 +5485,13 @@ out:
return ret;
}
+static int vtd_get_x86_iommu(void *opaque, void **x86_iommu)
+{
+ IntelIOMMUState *s = opaque;
+ *x86_iommu = X86_IOMMU_DEVICE(s);
+ return 0;
+}
+
static void vtd_init_iotlb_notifier(PCIBus *bus, void *opaque, int devfn,
IOMMUNotifier *n, IOMMUNotify fn,
void *user_opaque)
@@ -5559,6 +5566,7 @@ static PCIIOMMUOps vtd_iommu_ops = {
.pri_unregister_notifier = vtd_pri_unregister_notifier,
.pri_request_page = vtd_pri_request_page,
.get_viommu_flags = vtd_get_viommu_flags,
+ .get_x86_iommu = vtd_get_x86_iommu,
};
static bool vtd_decide_config(IntelIOMMUState *s, Error **errp)
--
MST
^ permalink raw reply related [flat|nested] 84+ messages in thread
* [PULL 65/78] i386/kvm: Use x86_iommu_device_get() for per-device IOMMU lookup
2026-10-28 17:13 [PULL 00/78] pci, vhost, virtio, iommu, acpi: features, fixes Michael S. Tsirkin
` (63 preceding siblings ...)
2026-10-28 17:16 ` [PULL 64/78] intel_iommu: " Michael S. Tsirkin
@ 2026-10-28 17:16 ` Michael S. Tsirkin
2026-10-28 17:16 ` [PULL 66/78] amd_iommu: Introduce root bus Michael S. Tsirkin
` (13 subsequent siblings)
78 siblings, 0 replies; 84+ messages in thread
From: Michael S. Tsirkin @ 2026-10-28 17:16 UTC (permalink / raw)
To: qemu-devel
Cc: Peter Maydell, Sairaj Kodilkar, Paolo Bonzini, Marcelo Tosatti,
kvm
From: Sairaj Kodilkar <sarunkod@amd.com>
Currently, kvm_arch_fixup_msi_route() unconditionally uses the default
system IOMMU for interrupt remapping when IOMMU is present. This
approach fails when an IOMMU is attached to a non-system bus and serves
only a subset of devices, because the kernel does not program IRT
(Interrupt Remapping Table) entries for devices outside the IOMMU's
scope.
This patch changes the behavior to perform per-device IOMMU lookup:
- For PCI devices: Use pci_device_get_x86_iommu() to find the specific
IOMMU serving that device, if any
- For non-PCI devices (IOAPIC): Fall back to the default IOMMU
only if interrupt remapping is enabled (ioapic_as != address_space_memory)
This ensures MSI route translation occurs only for devices actually
managed by an IOMMU, enabling proper interrupt remapping in configurations
where IOMMUs are attached to arbitrary buses.
NOTE: This patch assumes that only non-PCI device calling is IOAPIC and
checks ioapic_as to determine interrupt remapping.
Signed-off-by: Sairaj Kodilkar <sarunkod@amd.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Message-ID: <20261014053433.24564-6-sarunkod@amd.com>
---
hw/intc/ioapic.c | 3 ++-
target/i386/kvm/kvm.c | 15 ++++++++++++---
2 files changed, 14 insertions(+), 4 deletions(-)
diff --git a/hw/intc/ioapic.c b/hw/intc/ioapic.c
index 1932b14642..e171cf8432 100644
--- a/hw/intc/ioapic.c
+++ b/hw/intc/ioapic.c
@@ -438,8 +438,9 @@ static void ioapic_machine_done_notify(Notifier *notifier, void *data)
#ifdef ACCEL_GSI_IRQFD_POSSIBLE
IOAPICCommonState *s = container_of(notifier, IOAPICCommonState,
machine_done);
+ X86MachineState *x86ms = X86_MACHINE(qdev_get_machine());
- if (accel_irqchip_is_split()) {
+ if (accel_irqchip_is_split() && x86ms->ioapic_as != &address_space_memory) {
X86IOMMUState *iommu = x86_iommu_get_default();
if (iommu) {
/* Register this IOAPIC with IOMMU IEC notifier, so that
diff --git a/target/i386/kvm/kvm.c b/target/i386/kvm/kvm.c
index da922f72d7..072da8b9f1 100644
--- a/target/i386/kvm/kvm.c
+++ b/target/i386/kvm/kvm.c
@@ -6746,7 +6746,15 @@ uint64_t kvm_swizzle_msi_ext_dest_id(uint64_t address)
int kvm_arch_fixup_msi_route(struct kvm_irq_routing_entry *route,
uint64_t address, uint32_t data, PCIDevice *dev)
{
- X86IOMMUState *iommu = x86_iommu_get_default();
+ X86IOMMUState *iommu = NULL;
+ MachineState *ms = MACHINE(qdev_get_machine());
+ X86MachineState *x86ms = X86_MACHINE(ms);
+
+ if (dev) {
+ iommu = x86_iommu_device_get(dev);
+ } else if (x86ms->ioapic_as != &address_space_memory) {
+ iommu = x86_iommu_get_default();
+ }
if (iommu) {
X86IOMMUClass *class = X86_IOMMU_DEVICE_GET_CLASS(iommu);
@@ -6848,6 +6856,7 @@ int kvm_arch_add_msi_route_post(struct kvm_irq_routing_entry *route,
{
static bool notify_list_inited = false;
MSIRouteEntry *entry;
+ X86IOMMUState *iommu = NULL;
if (!dev) {
/* These are (possibly) IOAPIC routes only used for split
@@ -6867,13 +6876,13 @@ int kvm_arch_add_msi_route_post(struct kvm_irq_routing_entry *route,
if (!notify_list_inited) {
/* For the first time we do add route, add ourselves into
* IOMMU's IEC notify list if needed. */
- X86IOMMUState *iommu = x86_iommu_get_default();
+ iommu = x86_iommu_device_get(dev);
if (iommu) {
x86_iommu_iec_register_notifier(iommu,
kvm_update_msi_routes_all,
NULL);
+ notify_list_inited = true;
}
- notify_list_inited = true;
}
return 0;
}
--
MST
^ permalink raw reply related [flat|nested] 84+ messages in thread
* [PULL 66/78] amd_iommu: Introduce root bus
2026-10-28 17:13 [PULL 00/78] pci, vhost, virtio, iommu, acpi: features, fixes Michael S. Tsirkin
` (64 preceding siblings ...)
2026-10-28 17:16 ` [PULL 65/78] i386/kvm: Use x86_iommu_device_get() for per-device IOMMU lookup Michael S. Tsirkin
@ 2026-10-28 17:16 ` Michael S. Tsirkin
2026-10-28 17:16 ` [PULL 67/78] acpi-build: Create PCI topology using amd iommu " Michael S. Tsirkin
` (12 subsequent siblings)
78 siblings, 0 replies; 84+ messages in thread
From: Michael S. Tsirkin @ 2026-10-28 17:16 UTC (permalink / raw)
To: qemu-devel
Cc: Peter Maydell, Sairaj Kodilkar, Alejandro Jimenez, Paolo Bonzini,
Richard Henderson
From: Sairaj Kodilkar <sarunkod@amd.com>
Following patch will create IVRS entries starting from the root bus
Signed-off-by: Sairaj Kodilkar <sarunkod@amd.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Message-ID: <20261014053433.24564-7-sarunkod@amd.com>
---
hw/i386/amd_iommu.h | 3 +++
hw/i386/amd_iommu.c | 2 ++
2 files changed, 5 insertions(+)
diff --git a/hw/i386/amd_iommu.h b/hw/i386/amd_iommu.h
index c5b346cc35..948d8611b1 100644
--- a/hw/i386/amd_iommu.h
+++ b/hw/i386/amd_iommu.h
@@ -388,6 +388,9 @@ struct AMDVIState {
/* DMA address translation */
bool dma_remap;
+
+ /* root bus to register IOMMU ops */
+ PCIBus *root_bus;
};
uint64_t amdvi_extended_feature_register(AMDVIState *s);
diff --git a/hw/i386/amd_iommu.c b/hw/i386/amd_iommu.c
index b2df0e9533..14ea8274b2 100644
--- a/hw/i386/amd_iommu.c
+++ b/hw/i386/amd_iommu.c
@@ -2713,6 +2713,8 @@ static void amdvi_sysbus_realize(DeviceState *dev, Error **errp)
}
}
+ s->root_bus = bus;
+
s->iotlb = g_hash_table_new_full(amdvi_iotlb_hash,
amdvi_iotlb_equal, g_free, g_free);
--
MST
^ permalink raw reply related [flat|nested] 84+ messages in thread
* [PULL 67/78] acpi-build: Create PCI topology using amd iommu root bus
2026-10-28 17:13 [PULL 00/78] pci, vhost, virtio, iommu, acpi: features, fixes Michael S. Tsirkin
` (65 preceding siblings ...)
2026-10-28 17:16 ` [PULL 66/78] amd_iommu: Introduce root bus Michael S. Tsirkin
@ 2026-10-28 17:16 ` Michael S. Tsirkin
2026-10-28 17:16 ` [PULL 68/78] amd_iommu: Allow IOMMU to attachment on non-system (pcie.0) bus Michael S. Tsirkin
` (11 subsequent siblings)
78 siblings, 0 replies; 84+ messages in thread
From: Michael S. Tsirkin @ 2026-10-28 17:16 UTC (permalink / raw)
To: qemu-devel
Cc: Peter Maydell, Sairaj Kodilkar, Igor Mammedov, Ani Sinha,
Paolo Bonzini, Richard Henderson
From: Sairaj Kodilkar <sarunkod@amd.com>
Current AMD IOMMU ACPI build adds all the devices and buses under the
IVHD. In order to support the IOMMU attached on a non system bus and
serving only child devices of that bus, start exploring devices from the
child bus. Also, use child list provided by the PCIBus, instead of
iterating over all the objects in the qemu to find pci bridges.
Signed-off-by: Sairaj Kodilkar <sarunkod@amd.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Message-ID: <20261014053433.24564-8-sarunkod@amd.com>
---
hw/i386/acpi-build.c | 152 ++++++++++++++++++++++---------------------
1 file changed, 79 insertions(+), 73 deletions(-)
diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c
index 6d7308b4e4..9855154349 100644
--- a/hw/i386/acpi-build.c
+++ b/hw/i386/acpi-build.c
@@ -1763,83 +1763,89 @@ insert_ivhd(PCIBus *bus, PCIDevice *dev, void *opaque)
/* "Select" IVHD entry, type 0x2 */
entry = PCI_BUILD_BDF(pci_bus_num(bus), dev->devfn) << 8 | 0x2;
build_append_int_noprefix(table_data, entry, 4);
-
- if (object_dynamic_cast(OBJECT(dev), TYPE_PCI_BRIDGE)) {
- PCIBus *sec_bus = pci_bridge_get_sec_bus(PCI_BRIDGE(dev));
- uint8_t sec = pci_bus_num(sec_bus);
- uint8_t sub = dev->config[PCI_SUBORDINATE_BUS];
-
- if (pci_bus_is_express(sec_bus)) {
- /*
- * Walk the bus if there are subordinates, otherwise use a range
- * to cover an entire leaf bus. We could potentially also use a
- * range for traversed buses, but we'd need to take care not to
- * create both Select and Range entries covering the same device.
- * This is easier and potentially more compact.
- *
- * An example bare metal system seems to use Select entries for
- * root ports without a slot (ie. built-ins) and Range entries
- * when there is a slot. The same system also only hard-codes
- * the alias range for an onboard PCIe-to-PCI bridge, apparently
- * making no effort to support nested bridges. We attempt to
- * be more thorough here.
- */
- if (sec == sub) { /* leaf bus */
- /* "Start of Range" IVHD entry, type 0x3 */
- entry = PCI_BUILD_BDF(sec, PCI_DEVFN(0, 0)) << 8 | 0x3;
- build_append_int_noprefix(table_data, entry, 4);
- /* "End of Range" IVHD entry, type 0x4 */
- entry = PCI_BUILD_BDF(sub, PCI_DEVFN(31, 7)) << 8 | 0x4;
- build_append_int_noprefix(table_data, entry, 4);
- } else {
- pci_for_each_device(sec_bus, sec, insert_ivhd, table_data);
- }
- } else {
- /*
- * If the secondary bus is conventional, then we need to create an
- * Alias range for everything downstream. The range covers the
- * first devfn on the secondary bus to the last devfn on the
- * subordinate bus. The alias target depends on legacy versus
- * express bridges, just as in pci_device_iommu_address_space().
- * DeviceIDa vs DeviceIDb as per the AMD IOMMU spec.
- */
- uint16_t dev_id_a, dev_id_b;
-
- dev_id_a = PCI_BUILD_BDF(sec, PCI_DEVFN(0, 0));
-
- if (pci_is_express(dev) &&
- pcie_cap_get_type(dev) == PCI_EXP_TYPE_PCI_BRIDGE) {
- dev_id_b = dev_id_a;
- } else {
- dev_id_b = PCI_BUILD_BDF(pci_bus_num(bus), dev->devfn);
- }
-
- /* "Alias Start of Range" IVHD entry, type 0x43, 8 bytes */
- build_append_int_noprefix(table_data, dev_id_a << 8 | 0x43, 4);
- build_append_int_noprefix(table_data, dev_id_b << 8 | 0x0, 4);
-
- /* "End of Range" IVHD entry, type 0x4 */
- entry = PCI_BUILD_BDF(sub, PCI_DEVFN(31, 7)) << 8 | 0x4;
- build_append_int_noprefix(table_data, entry, 4);
- }
- }
}
-/* For all PCI host bridges, walk and insert IVHD entries */
-static int
-ivrs_host_bridges(Object *obj, void *opaque)
+/*
+ * Insert IVHD entry for device and recurse, insert alias, or insert range as
+ * necessary for the PCI topology.
+ */
+static void add_ivhd_entries_from_bus(PCIBus *bus, GArray *table_data)
{
- GArray *ivhd_blob = opaque;
+ uint8_t sub;
+ uint32_t entry;
+ PCIBus *child_bus;
+ PCIDevice *parent_dev;
- if (object_dynamic_cast(obj, TYPE_PCI_HOST_BRIDGE)) {
- PCIBus *bus = PCI_HOST_BRIDGE(obj)->bus;
-
- if (bus && !pci_bus_bypass_iommu(bus)) {
- pci_for_each_device_under_bus(bus, insert_ivhd, ivhd_blob);
- }
+ if (!bus->parent_dev) {
+ goto select_devices;
}
- return 0;
+ parent_dev = bus->parent_dev;
+ sub = parent_dev->config[PCI_SUBORDINATE_BUS];
+
+ if (pci_bus_is_express(bus)) {
+ /*
+ * Walk the bus if there are subordinates, otherwise use a range
+ * to cover an entire leaf bus. We could potentially also use a
+ * range for traversed buses, but we'd need to take care not to
+ * create both Select and Range entries covering the same device.
+ * This is easier and potentially more compact.
+ *
+ * An example bare metal system seems to use Select entries for
+ * root ports without a slot (ie. built-ins) and Range entries
+ * when there is a slot. The same system also only hard-codes
+ * the alias range for an onboard PCIe-to-PCI bridge, apparently
+ * making no effort to support nested bridges. We attempt to
+ * be more thorough here.
+ */
+ if (sub != pci_bus_num(bus)) {
+ goto select_devices;
+ }
+ /* "Start of Range" IVHD entry, type 0x3 */
+ entry = PCI_BUILD_BDF(sub, PCI_DEVFN(0, 0)) << 8 | 0x3;
+ build_append_int_noprefix(table_data, entry, 4);
+ /* "End of Range" IVHD entry, type 0x4 */
+ entry = PCI_BUILD_BDF(sub, PCI_DEVFN(31, 7)) << 8 | 0x4;
+ build_append_int_noprefix(table_data, entry, 4);
+
+ return;
+ } else {
+ /*
+ * If the secondary bus is conventional, then we need to create an
+ * Alias range for everything downstream. The range covers the
+ * first devfn on the secondary bus to the last devfn on the
+ * subordinate bus. The alias target depends on legacy versus
+ * express bridges, just as in pci_device_iommu_address_space().
+ * DeviceIDa vs DeviceIDb as per the AMD IOMMU spec.
+ */
+ uint16_t dev_id_a, dev_id_b;
+
+ dev_id_a = PCI_BUILD_BDF(pci_bus_num(bus), PCI_DEVFN(0, 0));
+
+ if (pci_is_express(parent_dev) &&
+ pcie_cap_get_type(parent_dev) == PCI_EXP_TYPE_PCI_BRIDGE) {
+ dev_id_b = dev_id_a;
+ } else {
+ dev_id_b = PCI_BUILD_BDF(pci_bus_num(pci_get_bus(parent_dev)),
+ parent_dev->devfn);
+ }
+
+ /* "Alias Start of Range" IVHD entry, type 0x43, 8 bytes */
+ build_append_int_noprefix(table_data, dev_id_a << 8 | 0x43, 4);
+ build_append_int_noprefix(table_data, dev_id_b << 8 | 0x0, 4);
+
+ /* "End of Range" IVHD entry, type 0x4 */
+ entry = PCI_BUILD_BDF(sub, PCI_DEVFN(31, 7)) << 8 | 0x4;
+ build_append_int_noprefix(table_data, entry, 4);
+ return;
+ }
+
+select_devices:
+ pci_for_each_device_under_bus(bus, insert_ivhd, table_data);
+
+ QLIST_FOREACH(child_bus, &bus->child, sibling) {
+ add_ivhd_entries_from_bus(child_bus, table_data);
+ }
}
/*
@@ -1904,8 +1910,8 @@ build_amd_iommu(GArray *table_data, BIOSLinker *linker, const char *oem_id,
* blob further below. Fall back to an entry covering all devices, which
* is sufficient when no aliases are present.
*/
- object_child_foreach_recursive(object_get_root(),
- ivrs_host_bridges, ivhd_blob);
+ assert(s->root_bus);
+ add_ivhd_entries_from_bus(s->root_bus, ivhd_blob);
if (!ivhd_blob->len) {
/*
--
MST
^ permalink raw reply related [flat|nested] 84+ messages in thread
* [PULL 68/78] amd_iommu: Allow IOMMU to attachment on non-system (pcie.0) bus
2026-10-28 17:13 [PULL 00/78] pci, vhost, virtio, iommu, acpi: features, fixes Michael S. Tsirkin
` (66 preceding siblings ...)
2026-10-28 17:16 ` [PULL 67/78] acpi-build: Create PCI topology using amd iommu " Michael S. Tsirkin
@ 2026-10-28 17:16 ` Michael S. Tsirkin
2026-10-28 17:17 ` [PULL 69/78] intel_iommu_accel: Guard VTDAccelPASIDCacheEntry definition Michael S. Tsirkin
` (10 subsequent siblings)
78 siblings, 0 replies; 84+ messages in thread
From: Michael S. Tsirkin @ 2026-10-28 17:16 UTC (permalink / raw)
To: qemu-devel
Cc: Peter Maydell, Sairaj Kodilkar, Alejandro Jimenez, Paolo Bonzini,
Richard Henderson
From: Sairaj Kodilkar <sarunkod@amd.com>
Allow the AMD IOMMU to attach on the pcie-root-port using pci device's "bus"
property. This helps in isolating the set of devices by creating a pcie
hierarchy under the pci expansion bus (pxb-pci). The IOMMU always tries
to find the first root bus by traversing up the PCI hierarchy and serves
all the devices under that bus.
Example1, for below PCI configuration, IOMMU will serve all the devices
under pxb-pci (i.e. Device1 and Device2) since its a first root bus,
but will not serve Device3 and Device3, effectively isolating the
devices under pxb-pci bus.
pcie.0 -+- pxb-pci -+- pcie-root-port1 --- AMD IOMMU
| +- pcie-root-port2 --- Device1
| \- pcie-root-port3 --- Device2
+- Device3
\- Device4
Example2, for below PCI configuration, IOMMU will serve all the devices
Device[1-4], since first root bus in the parent hierarchy is pcie.0
pcie.0 -+- pcie-root-port1 --- AMD IOMMU
+- pcie-root-port2 --- Device1
+- pcie-root-port3 --- Device2
+- Device3
\- Device4
AMD IOMMU uses this root bus to register pci IOMMU ops and build the IVRS
table.
Signed-off-by: Sairaj Kodilkar <sarunkod@amd.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Message-ID: <20261014053433.24564-9-sarunkod@amd.com>
---
hw/i386/amd_iommu.c | 55 +++++++++++++++++++++++++++++++++++++++++----
1 file changed, 51 insertions(+), 4 deletions(-)
diff --git a/hw/i386/amd_iommu.c b/hw/i386/amd_iommu.c
index 14ea8274b2..2d5c7b5cdf 100644
--- a/hw/i386/amd_iommu.c
+++ b/hw/i386/amd_iommu.c
@@ -24,6 +24,7 @@
#include "hw/i386/pc.h"
#include "hw/pci/msi.h"
#include "hw/pci/pci_bus.h"
+#include "hw/pci/pci_bridge.h"
#include "migration/vmstate.h"
#include "amd_iommu.h"
#include "qapi/error.h"
@@ -2680,6 +2681,50 @@ static const VMStateDescription vmstate_amdvi_sysbus_migratable = {
}
};
+/* Go up in the PCI hierarchy and find the root bus pcie.0 or pxb-pci */
+static PCIBus *get_root_bus(struct PCIBus *bus)
+{
+ while (bus) {
+
+ if (!pci_bus_is_express(bus)) {
+ error_report("attached to non PCI express bus %s",
+ bus->qbus.name);
+ goto err_out;
+ }
+
+ if (bus->iommu_ops) {
+ error_report("different IOMMU already serves bus %s",
+ bus->qbus.name);
+ goto err_out;
+ }
+
+ if (pci_bus_is_root(bus) &&
+ object_dynamic_cast(OBJECT(bus)->parent, TYPE_PCI_HOST_BRIDGE)) {
+
+ if (bus->parent_dev &&
+ !object_dynamic_cast(OBJECT(bus), TYPE_PXB_PCIE_BUS)) {
+
+ error_report("only supports PXB-PCI as extra root bus");
+ goto err_out;
+ }
+ /* We found the valid root bus so return it to the user*/
+ goto out;
+ }
+
+ if (bus->parent_dev) {
+ bus = pci_get_bus(bus->parent_dev);
+ } else {
+ error_report("could not find valid PCIE bus");
+ goto err_out;
+ }
+ };
+
+err_out:
+ exit(EXIT_FAILURE);
+out:
+ return bus;
+}
+
static void amdvi_sysbus_realize(DeviceState *dev, Error **errp)
{
DeviceClass *dc = (DeviceClass *) object_get_class(OBJECT(dev));
@@ -2687,7 +2732,7 @@ static void amdvi_sysbus_realize(DeviceState *dev, Error **errp)
MachineState *ms = MACHINE(qdev_get_machine());
PCMachineState *pcms = PC_MACHINE(ms);
X86MachineState *x86ms = X86_MACHINE(ms);
- PCIBus *bus = pcms->pcibus;
+ PCIBus *iommu_bus;
if (s->pci_id) {
PCIDevice *pdev = NULL;
@@ -2703,17 +2748,19 @@ static void amdvi_sysbus_realize(DeviceState *dev, Error **errp)
return;
}
+ iommu_bus = pci_get_bus(pdev);
s->pci = AMD_IOMMU_PCI(pdev);
dc->vmsd = &vmstate_amdvi_sysbus_migratable;
} else {
+ iommu_bus = pcms->pcibus;
s->pci = AMD_IOMMU_PCI(object_new(TYPE_AMD_IOMMU_PCI));
/* This device should take care of IOMMU PCI properties */
- if (!qdev_realize(DEVICE(s->pci), &bus->qbus, errp)) {
+ if (!qdev_realize(DEVICE(s->pci), &iommu_bus->qbus, errp)) {
return;
}
}
- s->root_bus = bus;
+ s->root_bus = get_root_bus(iommu_bus);
s->iotlb = g_hash_table_new_full(amdvi_iotlb_hash,
amdvi_iotlb_equal, g_free, g_free);
@@ -2767,7 +2814,7 @@ static void amdvi_sysbus_realize(DeviceState *dev, Error **errp)
}
}
- pci_setup_iommu(bus, &amdvi_iommu_ops, s);
+ pci_setup_iommu(s->root_bus, &amdvi_iommu_ops, s);
amdvi_init(s);
}
--
MST
^ permalink raw reply related [flat|nested] 84+ messages in thread
* [PULL 69/78] intel_iommu_accel: Guard VTDAccelPASIDCacheEntry definition
2026-10-28 17:13 [PULL 00/78] pci, vhost, virtio, iommu, acpi: features, fixes Michael S. Tsirkin
` (67 preceding siblings ...)
2026-10-28 17:16 ` [PULL 68/78] amd_iommu: Allow IOMMU to attachment on non-system (pcie.0) bus Michael S. Tsirkin
@ 2026-10-28 17:17 ` Michael S. Tsirkin
2026-10-28 17:17 ` [PULL 70/78] backends/iommufd: Introduce iommufd_backend_alloc_faultq Michael S. Tsirkin
` (9 subsequent siblings)
78 siblings, 0 replies; 84+ messages in thread
From: Michael S. Tsirkin @ 2026-10-28 17:17 UTC (permalink / raw)
To: qemu-devel
Cc: Peter Maydell, Zhenzhong Duan, Philippe Mathieu-Daudé,
Clement Mathieu--Drif, Yi Liu, Jason Wang, Paolo Bonzini,
Richard Henderson
From: Zhenzhong Duan <zhenzhong.duan@intel.com>
Move the VTDAccelPASIDCacheEntry typedef structure inside the
CONFIG_VTD_ACCEL ifdef block.
When CONFIG_VTD_ACCEL is disabled, this structure is entirely unused
throughout the code. Moving the layout under the existing preprocessor
guard prevents exposing dead type definitions and ensures the header
remains clean when building without VTD acceleration support.
Suggested-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com>
Signed-off-by: Zhenzhong Duan <zhenzhong.duan@intel.com>
Reviewed-by: Clement Mathieu--Drif <clement.mathieu--drif@bull.com>
Reviewed-by: Yi Liu <yi.l.liu@intel.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Message-ID: <20261020091955.1607161-2-zhenzhong.duan@intel.com>
---
hw/i386/intel_iommu_accel.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/hw/i386/intel_iommu_accel.h b/hw/i386/intel_iommu_accel.h
index 4a9003c92d..46c1a29409 100644
--- a/hw/i386/intel_iommu_accel.h
+++ b/hw/i386/intel_iommu_accel.h
@@ -12,6 +12,7 @@
#define HW_I386_INTEL_IOMMU_ACCEL_H
#include CONFIG_DEVICES
+#ifdef CONFIG_VTD_ACCEL
typedef struct VTDAccelPASIDCacheEntry {
VTDHostIOMMUDevice *vtd_hiod;
VTDPASIDEntry pasid_entry;
@@ -20,7 +21,6 @@ typedef struct VTDAccelPASIDCacheEntry {
QLIST_ENTRY(VTDAccelPASIDCacheEntry) next;
} VTDAccelPASIDCacheEntry;
-#ifdef CONFIG_VTD_ACCEL
bool vtd_check_hiod_accel(IntelIOMMUState *s, VTDHostIOMMUDevice *vtd_hiod,
Error **errp);
VTDHostIOMMUDevice *vtd_find_hiod_iommufd(VTDAddressSpace *as);
--
MST
^ permalink raw reply related [flat|nested] 84+ messages in thread
* [PULL 70/78] backends/iommufd: Introduce iommufd_backend_alloc_faultq
2026-10-28 17:13 [PULL 00/78] pci, vhost, virtio, iommu, acpi: features, fixes Michael S. Tsirkin
` (68 preceding siblings ...)
2026-10-28 17:17 ` [PULL 69/78] intel_iommu_accel: Guard VTDAccelPASIDCacheEntry definition Michael S. Tsirkin
@ 2026-10-28 17:17 ` Michael S. Tsirkin
2026-10-28 17:17 ` [PULL 71/78] backends/iommufd: Extend iommufd_backend_alloc_hwpt() with fault_id Michael S. Tsirkin
` (8 subsequent siblings)
78 siblings, 0 replies; 84+ messages in thread
From: Michael S. Tsirkin @ 2026-10-28 17:17 UTC (permalink / raw)
To: qemu-devel
Cc: Peter Maydell, Zhenzhong Duan, Clement Mathieu--Drif, Yi Liu,
Nicolin Chen, Xudong Hao, Eric Auger
From: Zhenzhong Duan <zhenzhong.duan@intel.com>
Add a new helper for IOMMU_FAULT_QUEUE_ALLOC ioctl to allocate a fault
handling object which will be used in hwpt allocation.
Signed-off-by: Zhenzhong Duan <zhenzhong.duan@intel.com>
Reviewed-by: Clement Mathieu--Drif <clement.mathieu--drif@bull.com>
Reviewed-by: Yi Liu <yi.l.liu@intel.com>
Reviewed-by: Nicolin Chen <nicolinc@nvidia.com>
Tested-by: Xudong Hao <xudong.hao@intel.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Message-ID: <20261020091955.1607161-3-zhenzhong.duan@intel.com>
---
include/system/iommufd.h | 3 +++
backends/iommufd.c | 21 +++++++++++++++++++++
backends/trace-events | 1 +
3 files changed, 25 insertions(+)
diff --git a/include/system/iommufd.h b/include/system/iommufd.h
index 17bf1a3727..36b34b4899 100644
--- a/include/system/iommufd.h
+++ b/include/system/iommufd.h
@@ -106,6 +106,9 @@ bool iommufd_backend_alloc_veventq(IOMMUFDBackend *be, uint32_t viommu_id,
uint32_t *out_veventq_id,
uint32_t *out_veventq_fd, Error **errp);
+bool iommufd_backend_alloc_faultq(IOMMUFDBackend *be, uint32_t *out_fault_id,
+ uint32_t *out_fault_fd, Error **errp);
+
bool iommufd_backend_alloc_hw_queue(IOMMUFDBackend *be, uint32_t viommu_id,
uint32_t queue_type, uint32_t index,
uint64_t addr, uint64_t length,
diff --git a/backends/iommufd.c b/backends/iommufd.c
index 45fd2e44c7..457b74a558 100644
--- a/backends/iommufd.c
+++ b/backends/iommufd.c
@@ -556,6 +556,27 @@ bool iommufd_backend_alloc_veventq(IOMMUFDBackend *be, uint32_t viommu_id,
return true;
}
+bool iommufd_backend_alloc_faultq(IOMMUFDBackend *be, uint32_t *out_fault_id,
+ uint32_t *out_fault_fd, Error **errp)
+{
+ int ret, fd = be->fd;
+ struct iommu_fault_alloc cmd = {
+ .size = sizeof(cmd),
+ };
+
+ ret = ioctl(fd, IOMMU_FAULT_QUEUE_ALLOC, &cmd);
+ trace_iommufd_backend_alloc_faultq(fd, cmd.out_fault_id, cmd.out_fault_fd,
+ ret);
+ if (ret) {
+ error_setg_errno(errp, errno, "Failed to allocate fault queue");
+ return false;
+ }
+
+ *out_fault_id = cmd.out_fault_id;
+ *out_fault_fd = cmd.out_fault_fd;
+ return true;
+}
+
bool iommufd_backend_alloc_hw_queue(IOMMUFDBackend *be, uint32_t viommu_id,
uint32_t queue_type, uint32_t index,
uint64_t addr, uint64_t length,
diff --git a/backends/trace-events b/backends/trace-events
index 9b7b9b7d68..b1c1d89dd8 100644
--- a/backends/trace-events
+++ b/backends/trace-events
@@ -17,6 +17,7 @@ iommufd_backend_unmap_dma_non_exist(int iommufd, uint32_t ioas, uint64_t iova, u
iommufd_backend_unmap_dma(int iommufd, uint32_t ioas, uint64_t iova, uint64_t size, int ret) " iommufd=%d ioas=%d iova=0x%"PRIx64" size=0x%"PRIx64" (%d)"
iommufd_backend_alloc_ioas(int iommufd, uint32_t ioas) " iommufd=%d ioas=%d"
iommufd_backend_alloc_hwpt(int iommufd, uint32_t dev_id, uint32_t pt_id, uint32_t flags, uint32_t hwpt_type, uint32_t len, uint64_t data_ptr, uint32_t out_hwpt_id, int ret) " iommufd=%d dev_id=%u pt_id=%u flags=0x%x hwpt_type=%u len=%u data_ptr=0x%"PRIx64" out_hwpt=%u (%d)"
+iommufd_backend_alloc_faultq(int iommufd, uint32_t fault_id, uint32_t fault_fd, int ret) " iommufd=%d fault_id=%u fault_fd=%u (%d)"
iommufd_backend_free_id(int iommufd, uint32_t id, int ret) " iommufd=%d id=%d (%d)"
iommufd_backend_set_dirty(int iommufd, uint32_t hwpt_id, bool start, int ret) " iommufd=%d hwpt=%u enable=%d (%d)"
iommufd_backend_get_dirty_bitmap(int iommufd, uint32_t hwpt_id, uint64_t iova, uint64_t size, uint64_t flags, uint64_t page_size, int ret) " iommufd=%d hwpt=%u iova=0x%"PRIx64" size=0x%"PRIx64" flags=0x%"PRIx64" page_size=0x%"PRIx64" (%d)"
--
MST
^ permalink raw reply related [flat|nested] 84+ messages in thread
* [PULL 71/78] backends/iommufd: Extend iommufd_backend_alloc_hwpt() with fault_id
2026-10-28 17:13 [PULL 00/78] pci, vhost, virtio, iommu, acpi: features, fixes Michael S. Tsirkin
` (69 preceding siblings ...)
2026-10-28 17:17 ` [PULL 70/78] backends/iommufd: Introduce iommufd_backend_alloc_faultq Michael S. Tsirkin
@ 2026-10-28 17:17 ` Michael S. Tsirkin
2026-10-28 17:17 ` [PULL 72/78] intel_iommu_accel: Add PRQ injection for passthrough device Michael S. Tsirkin
` (7 subsequent siblings)
78 siblings, 0 replies; 84+ messages in thread
From: Michael S. Tsirkin @ 2026-10-28 17:17 UTC (permalink / raw)
To: qemu-devel
Cc: Peter Maydell, Zhenzhong Duan, Clement Mathieu--Drif, Yi Liu,
Nicolin Chen, Xudong Hao, Eric Auger, Jason Wang, Paolo Bonzini,
Richard Henderson, Alex Williamson, Cédric Le Goater,
qemu-arm
From: Zhenzhong Duan <zhenzhong.duan@intel.com>
We should set flags IOMMU_HWPT_FAULT_ID_VALID when pass in a fault
object id in the caller.
Signed-off-by: Zhenzhong Duan <zhenzhong.duan@intel.com>
Reviewed-by: Clement Mathieu--Drif <clement.mathieu--drif@bull.com>
Reviewed-by: Yi Liu <yi.l.liu@intel.com>
Reviewed-by: Nicolin Chen <nicolinc@nvidia.com>
Tested-by: Xudong Hao <xudong.hao@intel.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Message-ID: <20261020091955.1607161-4-zhenzhong.duan@intel.com>
---
include/system/iommufd.h | 4 ++--
backends/iommufd.c | 7 ++++---
hw/arm/smmuv3-accel.c | 6 +++---
hw/i386/intel_iommu_accel.c | 2 +-
hw/vfio/iommufd.c | 2 +-
backends/trace-events | 2 +-
6 files changed, 12 insertions(+), 11 deletions(-)
diff --git a/include/system/iommufd.h b/include/system/iommufd.h
index 36b34b4899..79b8dabfb6 100644
--- a/include/system/iommufd.h
+++ b/include/system/iommufd.h
@@ -90,8 +90,8 @@ bool iommufd_backend_get_device_info(IOMMUFDBackend *be, uint32_t devid,
bool iommufd_backend_alloc_hwpt(IOMMUFDBackend *be, uint32_t dev_id,
uint32_t pt_id, uint32_t flags,
uint32_t data_type, uint32_t data_len,
- void *data_ptr, uint32_t *out_hwpt,
- Error **errp);
+ void *data_ptr, uint32_t fault_id,
+ uint32_t *out_hwpt, Error **errp);
bool iommufd_backend_alloc_viommu(IOMMUFDBackend *be, uint32_t dev_id,
uint32_t viommu_type, uint32_t hwpt_id,
void *data_ptr, uint32_t data_len,
diff --git a/backends/iommufd.c b/backends/iommufd.c
index 457b74a558..ecf4caee1a 100644
--- a/backends/iommufd.c
+++ b/backends/iommufd.c
@@ -317,8 +317,8 @@ int iommufd_backend_unmap_dma(IOMMUFDBackend *be, uint32_t ioas_id,
bool iommufd_backend_alloc_hwpt(IOMMUFDBackend *be, uint32_t dev_id,
uint32_t pt_id, uint32_t flags,
uint32_t data_type, uint32_t data_len,
- void *data_ptr, uint32_t *out_hwpt,
- Error **errp)
+ void *data_ptr, uint32_t fault_id,
+ uint32_t *out_hwpt, Error **errp)
{
int ret, fd = be->fd;
struct iommu_hwpt_alloc alloc_hwpt = {
@@ -329,11 +329,12 @@ bool iommufd_backend_alloc_hwpt(IOMMUFDBackend *be, uint32_t dev_id,
.data_type = data_type,
.data_len = data_len,
.data_uptr = (uintptr_t)data_ptr,
+ .fault_id = fault_id,
};
ret = ioctl(fd, IOMMU_HWPT_ALLOC, &alloc_hwpt);
trace_iommufd_backend_alloc_hwpt(fd, dev_id, pt_id, flags, data_type,
- data_len, (uintptr_t)data_ptr,
+ data_len, (uintptr_t)data_ptr, fault_id,
alloc_hwpt.out_hwpt_id, ret);
if (ret) {
error_setg_errno(errp, errno, "Failed to allocate hwpt");
diff --git a/hw/arm/smmuv3-accel.c b/hw/arm/smmuv3-accel.c
index 91aca1aa14..66e6a5011a 100644
--- a/hw/arm/smmuv3-accel.c
+++ b/hw/arm/smmuv3-accel.c
@@ -268,7 +268,7 @@ smmuv3_accel_dev_alloc_translate(SMMUv3AccelDevice *accel_dev, STE *ste,
accel->viommu->viommu_id, flags,
IOMMU_HWPT_DATA_ARM_SMMUV3,
sizeof(nested_data), &nested_data,
- &hwpt_id, errp)) {
+ 0, &hwpt_id, errp)) {
return NULL;
}
@@ -632,14 +632,14 @@ smmuv3_accel_alloc_viommu(SMMUv3State *s, HostIOMMUDeviceIOMMUFD *hiodi,
if (!iommufd_backend_alloc_hwpt(hiodi->iommufd, hiodi->devid, viommu_id,
0, IOMMU_HWPT_DATA_ARM_SMMUV3,
sizeof(abort_data), &abort_data,
- &accel->abort_hwpt_id, errp)) {
+ 0, &accel->abort_hwpt_id, errp)) {
goto free_viommu;
}
if (!iommufd_backend_alloc_hwpt(hiodi->iommufd, hiodi->devid, viommu_id,
0, IOMMU_HWPT_DATA_ARM_SMMUV3,
sizeof(bypass_data), &bypass_data,
- &accel->bypass_hwpt_id, errp)) {
+ 0, &accel->bypass_hwpt_id, errp)) {
goto free_abort_hwpt;
}
diff --git a/hw/i386/intel_iommu_accel.c b/hw/i386/intel_iommu_accel.c
index c3677775e9..c8f2835204 100644
--- a/hw/i386/intel_iommu_accel.c
+++ b/hw/i386/intel_iommu_accel.c
@@ -100,7 +100,7 @@ static bool vtd_create_fs_hwpt(VTDHostIOMMUDevice *vtd_hiod,
return iommufd_backend_alloc_hwpt(hiodi->iommufd, hiodi->devid,
hiodi->hwpt_id, flags,
IOMMU_HWPT_DATA_VTD_S1, sizeof(vtd), &vtd,
- fs_hwpt_id, errp);
+ 0, fs_hwpt_id, errp);
}
static void vtd_destroy_old_fs_hwpt(VTDAccelPASIDCacheEntry *vtd_pce)
diff --git a/hw/vfio/iommufd.c b/hw/vfio/iommufd.c
index de482128bf..90ddb54f4c 100644
--- a/hw/vfio/iommufd.c
+++ b/hw/vfio/iommufd.c
@@ -486,7 +486,7 @@ static bool iommufd_cdev_autodomains_get(VFIODevice *vbasedev,
if (!iommufd_backend_alloc_hwpt(iommufd, vbasedev->devid,
container->ioas_id, flags,
IOMMU_HWPT_DATA_NONE, 0, NULL,
- &hwpt_id, errp)) {
+ 0, &hwpt_id, errp)) {
return false;
}
diff --git a/backends/trace-events b/backends/trace-events
index b1c1d89dd8..2f242feb78 100644
--- a/backends/trace-events
+++ b/backends/trace-events
@@ -16,7 +16,7 @@ iommufd_backend_map_file_dma(int iommufd, uint32_t ioas, uint64_t iova, uint64_t
iommufd_backend_unmap_dma_non_exist(int iommufd, uint32_t ioas, uint64_t iova, uint64_t size, int ret) " Unmap nonexistent mapping: iommufd=%d ioas=%d iova=0x%"PRIx64" size=0x%"PRIx64" (%d)"
iommufd_backend_unmap_dma(int iommufd, uint32_t ioas, uint64_t iova, uint64_t size, int ret) " iommufd=%d ioas=%d iova=0x%"PRIx64" size=0x%"PRIx64" (%d)"
iommufd_backend_alloc_ioas(int iommufd, uint32_t ioas) " iommufd=%d ioas=%d"
-iommufd_backend_alloc_hwpt(int iommufd, uint32_t dev_id, uint32_t pt_id, uint32_t flags, uint32_t hwpt_type, uint32_t len, uint64_t data_ptr, uint32_t out_hwpt_id, int ret) " iommufd=%d dev_id=%u pt_id=%u flags=0x%x hwpt_type=%u len=%u data_ptr=0x%"PRIx64" out_hwpt=%u (%d)"
+iommufd_backend_alloc_hwpt(int iommufd, uint32_t dev_id, uint32_t pt_id, uint32_t flags, uint32_t hwpt_type, uint32_t len, uint64_t data_ptr, uint32_t fault_id, uint32_t out_hwpt_id, int ret) " iommufd=%d dev_id=%u pt_id=%u flags=0x%x hwpt_type=%u len=%u data_ptr=0x%"PRIx64" fault_id=%u out_hwpt=%u (%d)"
iommufd_backend_alloc_faultq(int iommufd, uint32_t fault_id, uint32_t fault_fd, int ret) " iommufd=%d fault_id=%u fault_fd=%u (%d)"
iommufd_backend_free_id(int iommufd, uint32_t id, int ret) " iommufd=%d id=%d (%d)"
iommufd_backend_set_dirty(int iommufd, uint32_t hwpt_id, bool start, int ret) " iommufd=%d hwpt=%u enable=%d (%d)"
--
MST
^ permalink raw reply related [flat|nested] 84+ messages in thread
* [PULL 72/78] intel_iommu_accel: Add PRQ injection for passthrough device
2026-10-28 17:13 [PULL 00/78] pci, vhost, virtio, iommu, acpi: features, fixes Michael S. Tsirkin
` (70 preceding siblings ...)
2026-10-28 17:17 ` [PULL 71/78] backends/iommufd: Extend iommufd_backend_alloc_hwpt() with fault_id Michael S. Tsirkin
@ 2026-10-28 17:17 ` Michael S. Tsirkin
2026-10-28 17:17 ` [PULL 73/78] intel_iommu_accel: Accept PRQ response " Michael S. Tsirkin
` (6 subsequent siblings)
78 siblings, 0 replies; 84+ messages in thread
From: Michael S. Tsirkin @ 2026-10-28 17:17 UTC (permalink / raw)
To: qemu-devel
Cc: Peter Maydell, Zhenzhong Duan, Clement Mathieu--Drif, Xudong Hao,
Yi Liu, Jason Wang, Paolo Bonzini, Richard Henderson
From: Zhenzhong Duan <zhenzhong.duan@intel.com>
When the guest enables the PRQ in vIOMMU, allocate a FAULTQ object so that
host-side recoverable fault events can be received and propagated back to
the guest.
Install an event handler on the FAULTQ fd to read and propagate host
generated recoverable fault events to the guest.
The handler runs in QEMU's main loop, using a non-blocking fd registered
via qemu_set_fd_handler().
Signed-off-by: Zhenzhong Duan <zhenzhong.duan@intel.com>
Reviewed-by: Clement Mathieu--Drif <clement.mathieu--drif@bull.com>
Tested-by: Xudong Hao <xudong.hao@intel.com>
Reviewed-by: Yi Liu <yi.l.liu@intel.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Message-ID: <20261020091955.1607161-5-zhenzhong.duan@intel.com>
---
hw/i386/intel_iommu_accel.h | 2 +
hw/i386/intel_iommu_internal.h | 3 +
hw/i386/intel_iommu.c | 14 ++-
hw/i386/intel_iommu_accel.c | 168 +++++++++++++++++++++++++++++++--
hw/i386/trace-events | 1 +
5 files changed, 177 insertions(+), 11 deletions(-)
diff --git a/hw/i386/intel_iommu_accel.h b/hw/i386/intel_iommu_accel.h
index 46c1a29409..e0319749a5 100644
--- a/hw/i386/intel_iommu_accel.h
+++ b/hw/i386/intel_iommu_accel.h
@@ -18,6 +18,8 @@ typedef struct VTDAccelPASIDCacheEntry {
VTDPASIDEntry pasid_entry;
uint32_t pasid;
uint32_t fs_hwpt_id;
+ uint32_t fault_id;
+ int fault_fd;
QLIST_ENTRY(VTDAccelPASIDCacheEntry) next;
} VTDAccelPASIDCacheEntry;
diff --git a/hw/i386/intel_iommu_internal.h b/hw/i386/intel_iommu_internal.h
index df7a0efa6e..4792035668 100644
--- a/hw/i386/intel_iommu_internal.h
+++ b/hw/i386/intel_iommu_internal.h
@@ -787,4 +787,7 @@ int vtd_dev_to_context_entry(IntelIOMMUState *s, uint8_t bus_num,
VTDAddressSpace *vtd_get_as_by_sid(IntelIOMMUState *s, uint16_t sid);
int vtd_dev_get_pe_from_pasid(IntelIOMMUState *s, PCIBus *bus, uint8_t devfn,
uint32_t pasid, VTDPASIDEntry *pe);
+int vtd_pri_request_page(PCIBus *bus, void *opaque, int devfn, uint32_t pasid,
+ bool priv_req, bool exec_req, hwaddr addr, bool lpig,
+ uint16_t prgi, bool is_read, bool is_write);
#endif
diff --git a/hw/i386/intel_iommu.c b/hw/i386/intel_iommu.c
index 3686ca8ba7..adb8c79d57 100644
--- a/hw/i386/intel_iommu.c
+++ b/hw/i386/intel_iommu.c
@@ -5383,11 +5383,15 @@ static int vtd_pri_perform_implicit_invalidation(VTDAddressSpace *vtd_as,
return ret;
}
-/* Page Request Descriptor : 7.4.1.1 */
-static int vtd_pri_request_page(PCIBus *bus, void *opaque, int devfn,
- uint32_t pasid, bool priv_req, bool exec_req,
- hwaddr addr, bool lpig, uint16_t prgi,
- bool is_read, bool is_write)
+/*
+ * Page Request Descriptor : 7.4.1.1
+ *
+ * Because it is facing the emulated device models, so this should be PCI
+ * PASID instead of the pasids used within vIOMMU.
+ */
+int vtd_pri_request_page(PCIBus *bus, void *opaque, int devfn, uint32_t pasid,
+ bool priv_req, bool exec_req, hwaddr addr, bool lpig,
+ uint16_t prgi, bool is_read, bool is_write)
{
IntelIOMMUState *s = opaque;
VTDAddressSpace *vtd_as;
diff --git a/hw/i386/intel_iommu_accel.c b/hw/i386/intel_iommu_accel.c
index c8f2835204..bcd361bae3 100644
--- a/hw/i386/intel_iommu_accel.c
+++ b/hw/i386/intel_iommu_accel.c
@@ -9,6 +9,7 @@
*/
#include "qemu/osdep.h"
+#include "qemu/error-report.h"
#include "system/iommufd.h"
#include "intel_iommu_internal.h"
#include "intel_iommu_accel.h"
@@ -83,14 +84,141 @@ VTDHostIOMMUDevice *vtd_find_hiod_iommufd(VTDAddressSpace *as)
return NULL;
}
-static bool vtd_create_fs_hwpt(VTDHostIOMMUDevice *vtd_hiod,
- VTDPASIDEntry *pe, uint32_t *fs_hwpt_id,
- Error **errp)
+static void vtd_propagate_recoverable_faults(VTDAccelPASIDCacheEntry *vtd_pce,
+ struct iommu_hwpt_pgfault *fault,
+ unsigned cnt)
+{
+ VTDHostIOMMUDevice *vtd_hiod = vtd_pce->vtd_hiod;
+ uint32_t pasid =
+ vtd_pce->pasid == IOMMU_NO_PASID ? PCI_NO_PASID : vtd_pce->pasid;
+
+ for (; cnt--; fault++) {
+ bool last_page = fault->flags & IOMMU_PGFAULT_FLAGS_LAST_PAGE;
+
+ vtd_pri_request_page(vtd_hiod->bus, vtd_hiod->iommu_state,
+ vtd_hiod->devfn, pasid,
+ fault->perm & IOMMU_PGFAULT_PERM_PRIV,
+ fault->perm & IOMMU_PGFAULT_PERM_EXEC,
+ fault->addr, last_page, fault->grpid,
+ fault->perm & IOMMU_PGFAULT_PERM_READ,
+ fault->perm & IOMMU_PGFAULT_PERM_WRITE);
+ }
+}
+
+/* Batch size per read(); remaining faults trigger another callback */
+#define FAULTQ_BUF_SIZE 2048
+
+static void vtd_read_fs_faultq(void *opaque)
+{
+ VTDAccelPASIDCacheEntry *vtd_pce = opaque;
+ struct iommu_hwpt_pgfault faults[FAULTQ_BUF_SIZE];
+ uint32_t id = vtd_pce->fault_id;
+ int fd = vtd_pce->fault_fd;
+ ssize_t bytes, last_bytes;
+
+ bytes = read(fd, faults, sizeof(faults));
+ trace_vtd_read_fs_faultq(id, fd, bytes);
+ if (bytes < 0) {
+ if (errno != EAGAIN && errno != EINTR) {
+ error_report_once("FAULTQ(id %u): read failed (%m)", id);
+ }
+ return;
+ } else if (!bytes) {
+ error_report_once("FAULTQ(id %u): fault group empty unexpectedly", id);
+ return;
+ }
+
+ last_bytes = bytes % sizeof(faults[0]);
+ if (last_bytes) {
+ error_report_once("FAULTQ(id %u): discard partial fault data: %zd/%zu",
+ id, last_bytes, sizeof(faults[0]));
+ }
+
+ vtd_propagate_recoverable_faults(vtd_pce, faults,
+ bytes / sizeof(faults[0]));
+}
+
+static void vtd_destroy_fs_faultq(VTDHostIOMMUDevice *vtd_hiod,
+ uint32_t fault_id, int fault_fd)
+{
+ HostIOMMUDeviceIOMMUFD *hiodi = HOST_IOMMU_DEVICE_IOMMUFD(vtd_hiod->hiod);
+
+ if (fault_fd < 0) {
+ return;
+ }
+
+ close(fault_fd);
+ iommufd_backend_free_id(hiodi->iommufd, fault_id);
+}
+
+static bool vtd_create_fs_faultq(VTDHostIOMMUDevice *vtd_hiod,
+ uint32_t *fault_id_p, int *fault_fd_p,
+ Error **errp)
+{
+ HostIOMMUDeviceIOMMUFD *hiodi = HOST_IOMMU_DEVICE_IOMMUFD(vtd_hiod->hiod);
+ uint32_t fault_id, fault_fd;
+ int flags;
+
+ if (!iommufd_backend_alloc_faultq(hiodi->iommufd, &fault_id, &fault_fd,
+ errp)) {
+ return false;
+ }
+
+ flags = fcntl(fault_fd, F_GETFL);
+ if (flags < 0) {
+ error_setg_errno(errp, errno, "Failed to get flags for FAULTQ fd");
+ goto free_faultq;
+ }
+
+ if (fcntl(fault_fd, F_SETFL, flags | O_NONBLOCK) < 0) {
+ error_setg_errno(errp, errno, "Failed to set O_NONBLOCK on FAULTQ fd");
+ goto free_faultq;
+ }
+
+ *fault_id_p = fault_id;
+ *fault_fd_p = fault_fd;
+ return true;
+
+free_faultq:
+ vtd_destroy_fs_faultq(vtd_hiod, fault_id, fault_fd);
+ return false;
+}
+
+static void vtd_destroy_old_fs_faultq(VTDAccelPASIDCacheEntry *vtd_pce)
+{
+ if (vtd_pce->fault_fd < 0) {
+ return;
+ }
+
+ qemu_set_fd_handler(vtd_pce->fault_fd, NULL, NULL, NULL);
+ vtd_destroy_fs_faultq(vtd_pce->vtd_hiod, vtd_pce->fault_id,
+ vtd_pce->fault_fd);
+ vtd_pce->fault_id = 0;
+ vtd_pce->fault_fd = -1;
+}
+
+static void vtd_setup_fs_faultq(VTDAccelPASIDCacheEntry *vtd_pce,
+ uint32_t fault_id, int fault_fd)
+{
+ if (fault_fd < 0) {
+ return;
+ }
+
+ vtd_pce->fault_id = fault_id;
+ vtd_pce->fault_fd = fault_fd;
+ qemu_set_fd_handler(fault_fd, vtd_read_fs_faultq, NULL, vtd_pce);
+}
+
+static bool vtd_create_fs_hwpt(VTDHostIOMMUDevice *vtd_hiod, VTDPASIDEntry *pe,
+ bool has_fault_id, uint32_t fault_id,
+ uint32_t *fs_hwpt_id, Error **errp)
{
HostIOMMUDeviceIOMMUFD *hiodi = HOST_IOMMU_DEVICE_IOMMUFD(vtd_hiod->hiod);
struct iommu_hwpt_vtd_s1 vtd = {};
uint32_t flags = vtd_hiod->iommu_state->pasid ? IOMMU_HWPT_ALLOC_PASID : 0;
+ flags |= has_fault_id ? IOMMU_HWPT_FAULT_ID_VALID : 0;
+
vtd.flags = (VTD_SM_PASID_ENTRY_SRE(pe) ? IOMMU_VTD_S1_SRE : 0) |
(VTD_SM_PASID_ENTRY_WPE(pe) ? IOMMU_VTD_S1_WPE : 0) |
(VTD_SM_PASID_ENTRY_EAFE(pe) ? IOMMU_VTD_S1_EAFE : 0);
@@ -100,7 +228,7 @@ static bool vtd_create_fs_hwpt(VTDHostIOMMUDevice *vtd_hiod,
return iommufd_backend_alloc_hwpt(hiodi->iommufd, hiodi->devid,
hiodi->hwpt_id, flags,
IOMMU_HWPT_DATA_VTD_S1, sizeof(vtd), &vtd,
- 0, fs_hwpt_id, errp);
+ fault_id, fs_hwpt_id, errp);
}
static void vtd_destroy_old_fs_hwpt(VTDAccelPASIDCacheEntry *vtd_pce)
@@ -121,7 +249,8 @@ static bool vtd_device_attach_iommufd(VTDAccelPASIDCacheEntry *vtd_pce,
VTDHostIOMMUDevice *vtd_hiod = vtd_pce->vtd_hiod;
HostIOMMUDeviceIOMMUFD *hiodi = HOST_IOMMU_DEVICE_IOMMUFD(vtd_hiod->hiod);
VTDPASIDEntry *pe = &vtd_pce->pasid_entry;
- uint32_t hwpt_id = hiodi->hwpt_id, pasid = vtd_pce->pasid;
+ uint32_t hwpt_id = hiodi->hwpt_id, pasid = vtd_pce->pasid, fault_id = 0;
+ int fault_fd = -1;
bool ret;
/*
@@ -136,7 +265,29 @@ static bool vtd_device_attach_iommufd(VTDAccelPASIDCacheEntry *vtd_pce,
}
if (vtd_pe_pgtt_is_fst(pe)) {
- if (!vtd_create_fs_hwpt(vtd_hiod, pe, &hwpt_id, errp)) {
+ IntelIOMMUState *s = vtd_hiod->iommu_state;
+ bool is_pre = false;
+
+ if (s->svm) {
+ uint8_t bus_n = pci_bus_num(vtd_hiod->bus);
+ VTDContextEntry ce;
+
+ if (vtd_dev_to_context_entry(s, bus_n, vtd_hiod->devfn, &ce)) {
+ error_setg(errp, "Failed to fetch context entry");
+ return false;
+ }
+
+ is_pre = !!VTD_CE_GET_PRE(&ce);
+
+ if (is_pre &&
+ !vtd_create_fs_faultq(vtd_hiod, &fault_id, &fault_fd, errp)) {
+ return false;
+ }
+ }
+
+ if (!vtd_create_fs_hwpt(vtd_hiod, pe, is_pre, fault_id, &hwpt_id,
+ errp)) {
+ vtd_destroy_fs_faultq(vtd_hiod, fault_id, fault_fd);
return false;
}
}
@@ -146,11 +297,14 @@ static bool vtd_device_attach_iommufd(VTDAccelPASIDCacheEntry *vtd_pce,
if (ret) {
/* Destroy old fs_hwpt if it's a replacement */
vtd_destroy_old_fs_hwpt(vtd_pce);
+ vtd_destroy_old_fs_faultq(vtd_pce);
if (vtd_pe_pgtt_is_fst(pe)) {
vtd_pce->fs_hwpt_id = hwpt_id;
+ vtd_setup_fs_faultq(vtd_pce, fault_id, fault_fd);
}
} else if (vtd_pe_pgtt_is_fst(pe)) {
iommufd_backend_free_id(hiodi->iommufd, hwpt_id);
+ vtd_destroy_fs_faultq(vtd_hiod, fault_id, fault_fd);
}
return ret;
@@ -183,6 +337,7 @@ static bool vtd_device_detach_iommufd(VTDAccelPASIDCacheEntry *vtd_pce,
if (ret) {
vtd_destroy_old_fs_hwpt(vtd_pce);
+ vtd_destroy_old_fs_faultq(vtd_pce);
}
return ret;
@@ -282,6 +437,7 @@ static void vtd_accel_fill_pc(VTDHostIOMMUDevice *vtd_hiod, uint32_t pasid,
vtd_pce->vtd_hiod = vtd_hiod;
vtd_pce->pasid = pasid;
vtd_pce->pasid_entry = *pe;
+ vtd_pce->fault_fd = -1;
QLIST_INSERT_HEAD(&vtd_hiod->pasid_cache_list, vtd_pce, next);
if (!vtd_device_attach_iommufd(vtd_pce, &local_err)) {
diff --git a/hw/i386/trace-events b/hw/i386/trace-events
index a1a50d0910..ad84d106a4 100644
--- a/hw/i386/trace-events
+++ b/hw/i386/trace-events
@@ -77,6 +77,7 @@ vtd_reset_exit(void) ""
vtd_device_attach_hwpt(uint32_t dev_id, uint32_t pasid, uint32_t hwpt_id, int ret) "dev_id %d pasid %d hwpt_id %d, ret: %d"
vtd_device_detach_hwpt(uint32_t dev_id, uint32_t pasid, int ret) "dev_id %d pasid %d ret: %d"
vtd_device_reattach_def_hwpt(uint32_t dev_id, uint32_t pasid, uint32_t hwpt_id, int ret) "dev_id %d pasid %d hwpt_id %d, ret: %d"
+vtd_read_fs_faultq(uint32_t fault_id, uint32_t fault_fd, ssize_t ret) "fault_id %d fault_fd %d ret: %zd"
# amd_iommu.c
amdvi_evntlog_fail(uint64_t addr, uint32_t head) "error: fail to write at addr 0x%"PRIx64" + offset 0x%"PRIx32
--
MST
^ permalink raw reply related [flat|nested] 84+ messages in thread
* [PULL 73/78] intel_iommu_accel: Accept PRQ response for passthrough device
2026-10-28 17:13 [PULL 00/78] pci, vhost, virtio, iommu, acpi: features, fixes Michael S. Tsirkin
` (71 preceding siblings ...)
2026-10-28 17:17 ` [PULL 72/78] intel_iommu_accel: Add PRQ injection for passthrough device Michael S. Tsirkin
@ 2026-10-28 17:17 ` Michael S. Tsirkin
2026-10-28 17:17 ` [PULL 74/78] intel_iommu_accel: Teardown FAULTQ resources in bottom half Michael S. Tsirkin
` (5 subsequent siblings)
78 siblings, 0 replies; 84+ messages in thread
From: Michael S. Tsirkin @ 2026-10-28 17:17 UTC (permalink / raw)
To: qemu-devel
Cc: Peter Maydell, Zhenzhong Duan, Clement Mathieu--Drif, Yi Liu,
Xudong Hao, Jason Wang, Paolo Bonzini, Richard Henderson
From: Zhenzhong Duan <zhenzhong.duan@intel.com>
Propagate guest's PRQ response to host by writing to fault_fd.
Create a new VTDPRQEntry to cache cookie for each fault group,
this cookie is used to mark the fault group on host side.
If there is either PRQ overflow or full error condition, propagate
a success response to host for LPIG request with the PRQ requests
dropped. For other error conditions, propagate response of invalid
request.
Signed-off-by: Zhenzhong Duan <zhenzhong.duan@intel.com>
Reviewed-by: Clement Mathieu--Drif <clement.mathieu--drif@bull.com>
Reviewed-by: Yi Liu <yi.l.liu@intel.com>
Tested-by: Xudong Hao <xudong.hao@intel.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Message-ID: <20261020091955.1607161-6-zhenzhong.duan@intel.com>
---
hw/i386/intel_iommu_accel.h | 12 ++++
include/hw/i386/intel_iommu.h | 6 ++
hw/i386/intel_iommu.c | 4 ++
hw/i386/intel_iommu_accel.c | 102 +++++++++++++++++++++++++++++++---
hw/i386/trace-events | 1 +
5 files changed, 118 insertions(+), 7 deletions(-)
diff --git a/hw/i386/intel_iommu_accel.h b/hw/i386/intel_iommu_accel.h
index e0319749a5..0cc22b2cbb 100644
--- a/hw/i386/intel_iommu_accel.h
+++ b/hw/i386/intel_iommu_accel.h
@@ -20,6 +20,7 @@ typedef struct VTDAccelPASIDCacheEntry {
uint32_t fs_hwpt_id;
uint32_t fault_id;
int fault_fd;
+ QLIST_HEAD(, VTDPRQEntry) vtd_prq_list;
QLIST_ENTRY(VTDAccelPASIDCacheEntry) next;
} VTDAccelPASIDCacheEntry;
@@ -31,6 +32,9 @@ void vtd_flush_host_piotlb_all_locked(IntelIOMMUState *s, uint16_t domain_id,
uint64_t npages, bool ih);
void vtd_accel_pasid_cache_sync(IntelIOMMUState *s, VTDPASIDCacheInfo *pc_info);
void vtd_accel_pasid_cache_reset(IntelIOMMUState *s);
+bool vtd_accel_propagate_page_group_response(IntelIOMMUState *s,
+ uint16_t rid, uint32_t pasid,
+ IOMMUPRIResponse *response);
void vtd_iommu_ops_update_accel(PCIIOMMUOps *ops);
#else
static inline bool vtd_check_hiod_accel(IntelIOMMUState *s,
@@ -69,6 +73,14 @@ static inline void vtd_accel_pasid_cache_reset(IntelIOMMUState *s)
{
}
+static inline
+bool vtd_accel_propagate_page_group_response(IntelIOMMUState *s,
+ uint16_t rid, uint32_t pasid,
+ IOMMUPRIResponse *response)
+{
+ return false;
+}
+
static inline void vtd_iommu_ops_update_accel(PCIIOMMUOps *ops)
{
}
diff --git a/include/hw/i386/intel_iommu.h b/include/hw/i386/intel_iommu.h
index 1842ba5840..5d44eac0ed 100644
--- a/include/hw/i386/intel_iommu.h
+++ b/include/hw/i386/intel_iommu.h
@@ -100,6 +100,12 @@ typedef struct VTDPASIDCacheEntry {
bool valid;
} VTDPASIDCacheEntry;
+typedef struct VTDPRQEntry {
+ uint32_t grpid;
+ uint32_t cookie;
+ QLIST_ENTRY(VTDPRQEntry) next;
+} VTDPRQEntry;
+
struct VTDAddressSpace {
PCIBus *bus;
uint8_t devfn;
diff --git a/hw/i386/intel_iommu.c b/hw/i386/intel_iommu.c
index adb8c79d57..b7da5d086d 100644
--- a/hw/i386/intel_iommu.c
+++ b/hw/i386/intel_iommu.c
@@ -3380,6 +3380,10 @@ static bool vtd_process_page_group_response_desc(IntelIOMMUState *s,
response.response_code = IOMMU_PRI_RESP_FAILURE;
}
+ if (vtd_accel_propagate_page_group_response(s, rid, pasid, &response)) {
+ return true;
+ }
+
if (vtd_dev_as->pri_notifier) {
vtd_dev_as->pri_notifier->notify(vtd_dev_as->pri_notifier, &response);
}
diff --git a/hw/i386/intel_iommu_accel.c b/hw/i386/intel_iommu_accel.c
index bcd361bae3..243501f137 100644
--- a/hw/i386/intel_iommu_accel.c
+++ b/hw/i386/intel_iommu_accel.c
@@ -17,6 +17,9 @@
#include "hw/pci/pci_bus.h"
#include "trace.h"
+static void vtd_write_fs_faultq(VTDAccelPASIDCacheEntry *vtd_pce,
+ uint32_t cookie, uint32_t code);
+
bool vtd_check_hiod_accel(IntelIOMMUState *s, VTDHostIOMMUDevice *vtd_hiod,
Error **errp)
{
@@ -84,6 +87,46 @@ VTDHostIOMMUDevice *vtd_find_hiod_iommufd(VTDAddressSpace *as)
return NULL;
}
+static void vtd_prq_handle_pasid_response(VTDAccelPASIDCacheEntry *vtd_pce,
+ IOMMUPRIResponse *response)
+{
+ VTDPRQEntry *prqe, *tmp;
+
+ QLIST_FOREACH_SAFE(prqe, &vtd_pce->vtd_prq_list, next, tmp) {
+ if (prqe->grpid != response->prgi) {
+ continue;
+ }
+
+ vtd_write_fs_faultq(vtd_pce, prqe->cookie, response->response_code);
+
+ QLIST_REMOVE(prqe, next);
+ g_free(prqe);
+ break;
+ }
+}
+
+bool vtd_accel_propagate_page_group_response(IntelIOMMUState *s,
+ uint16_t rid, uint32_t pasid,
+ IOMMUPRIResponse *response)
+{
+ VTDAddressSpace *vtd_as = vtd_get_as_by_sid(s, rid);
+ VTDAccelPASIDCacheEntry *vtd_pce;
+ VTDHostIOMMUDevice *vtd_hiod = vtd_find_hiod_iommufd(vtd_as);
+
+ if (!vtd_hiod) {
+ return false;
+ }
+
+ QLIST_FOREACH(vtd_pce, &vtd_hiod->pasid_cache_list, next) {
+ if (vtd_pce->pasid == pasid) {
+ vtd_prq_handle_pasid_response(vtd_pce, response);
+ return true;
+ }
+ }
+
+ return false;
+}
+
static void vtd_propagate_recoverable_faults(VTDAccelPASIDCacheEntry *vtd_pce,
struct iommu_hwpt_pgfault *fault,
unsigned cnt)
@@ -94,14 +137,42 @@ static void vtd_propagate_recoverable_faults(VTDAccelPASIDCacheEntry *vtd_pce,
for (; cnt--; fault++) {
bool last_page = fault->flags & IOMMU_PGFAULT_FLAGS_LAST_PAGE;
+ int ret;
- vtd_pri_request_page(vtd_hiod->bus, vtd_hiod->iommu_state,
- vtd_hiod->devfn, pasid,
- fault->perm & IOMMU_PGFAULT_PERM_PRIV,
- fault->perm & IOMMU_PGFAULT_PERM_EXEC,
- fault->addr, last_page, fault->grpid,
- fault->perm & IOMMU_PGFAULT_PERM_READ,
- fault->perm & IOMMU_PGFAULT_PERM_WRITE);
+ ret = vtd_pri_request_page(vtd_hiod->bus, vtd_hiod->iommu_state,
+ vtd_hiod->devfn, pasid,
+ fault->perm & IOMMU_PGFAULT_PERM_PRIV,
+ fault->perm & IOMMU_PGFAULT_PERM_EXEC,
+ fault->addr, last_page, fault->grpid,
+ fault->perm & IOMMU_PGFAULT_PERM_READ,
+ fault->perm & IOMMU_PGFAULT_PERM_WRITE);
+ if (!last_page) {
+ continue;
+ }
+
+ if (!ret) {
+ VTDPRQEntry *prqe = g_malloc0(sizeof(*prqe));
+
+ prqe->grpid = fault->grpid;
+ prqe->cookie = fault->cookie;
+ QLIST_INSERT_HEAD(&vtd_pce->vtd_prq_list, prqe, next);
+ continue;
+ }
+
+ /*
+ * vt-d spec 7.4.1:
+ *
+ * Hardware generates page group response with code of success for
+ * PRQ overflow and PRQ full, with code of invalid request for
+ * other error conditions.
+ */
+ if (ret == -ENOSPC) {
+ vtd_write_fs_faultq(vtd_pce, fault->cookie,
+ IOMMUFD_PAGE_RESP_SUCCESS);
+ } else {
+ vtd_write_fs_faultq(vtd_pce, fault->cookie,
+ IOMMUFD_PAGE_RESP_INVALID);
+ }
}
}
@@ -138,6 +209,22 @@ static void vtd_read_fs_faultq(void *opaque)
bytes / sizeof(faults[0]));
}
+static void vtd_write_fs_faultq(VTDAccelPASIDCacheEntry *vtd_pce,
+ uint32_t cookie, uint32_t code)
+{
+ struct iommu_hwpt_page_response resp = {.cookie = cookie, .code = code};
+ uint32_t id = vtd_pce->fault_id;
+ int fd = vtd_pce->fault_fd;
+ ssize_t bytes;
+
+ bytes = write(fd, &resp, sizeof(resp));
+ trace_vtd_write_fs_faultq(id, fd, cookie, code, bytes);
+ if (bytes < 0) {
+ error_report_once("FAULTQ(id %u): write failed "
+ "[cookie 0x%x code 0x%x] (%m)", id, cookie, code);
+ }
+}
+
static void vtd_destroy_fs_faultq(VTDHostIOMMUDevice *vtd_hiod,
uint32_t fault_id, int fault_fd)
{
@@ -438,6 +525,7 @@ static void vtd_accel_fill_pc(VTDHostIOMMUDevice *vtd_hiod, uint32_t pasid,
vtd_pce->pasid = pasid;
vtd_pce->pasid_entry = *pe;
vtd_pce->fault_fd = -1;
+ QLIST_INIT(&vtd_pce->vtd_prq_list);
QLIST_INSERT_HEAD(&vtd_hiod->pasid_cache_list, vtd_pce, next);
if (!vtd_device_attach_iommufd(vtd_pce, &local_err)) {
diff --git a/hw/i386/trace-events b/hw/i386/trace-events
index ad84d106a4..7bf4cddd38 100644
--- a/hw/i386/trace-events
+++ b/hw/i386/trace-events
@@ -78,6 +78,7 @@ vtd_device_attach_hwpt(uint32_t dev_id, uint32_t pasid, uint32_t hwpt_id, int re
vtd_device_detach_hwpt(uint32_t dev_id, uint32_t pasid, int ret) "dev_id %d pasid %d ret: %d"
vtd_device_reattach_def_hwpt(uint32_t dev_id, uint32_t pasid, uint32_t hwpt_id, int ret) "dev_id %d pasid %d hwpt_id %d, ret: %d"
vtd_read_fs_faultq(uint32_t fault_id, uint32_t fault_fd, ssize_t ret) "fault_id %d fault_fd %d ret: %zd"
+vtd_write_fs_faultq(uint32_t fault_id, uint32_t fault_fd, uint32_t cookie, uint32_t code, ssize_t ret) "fault_id %d fault_fd %d cookie %d code %d ret: %zd"
# amd_iommu.c
amdvi_evntlog_fail(uint64_t addr, uint32_t head) "error: fail to write at addr 0x%"PRIx64" + offset 0x%"PRIx32
--
MST
^ permalink raw reply related [flat|nested] 84+ messages in thread
* [PULL 74/78] intel_iommu_accel: Teardown FAULTQ resources in bottom half
2026-10-28 17:13 [PULL 00/78] pci, vhost, virtio, iommu, acpi: features, fixes Michael S. Tsirkin
` (72 preceding siblings ...)
2026-10-28 17:17 ` [PULL 73/78] intel_iommu_accel: Accept PRQ response " Michael S. Tsirkin
@ 2026-10-28 17:17 ` Michael S. Tsirkin
2026-10-28 17:17 ` [PULL 75/78] hw/virtio/virtio-mem: Fix error message on invalid NUMA node Michael S. Tsirkin
` (4 subsequent siblings)
78 siblings, 0 replies; 84+ messages in thread
From: Michael S. Tsirkin @ 2026-10-28 17:17 UTC (permalink / raw)
To: qemu-devel
Cc: Peter Maydell, Zhenzhong Duan, Shameer Kolothum, Yi Liu,
Xudong Hao, Clement Mathieu--Drif, Jason Wang, Paolo Bonzini,
Richard Henderson
From: Zhenzhong Duan <zhenzhong.duan@intel.com>
When a PASID entry becomes invalid, all resources allocated for that entry,
including the FAULTQ object and fault_fd, must be released.
Calling qemu_set_fd_handler() detaches the fault_fd I/O read handler and
wakes up the main loop thread from poll(). However, a race window exists
where iommufd_backend_free_id(fault_id) can be invoked before poll() exits
and releases its reference to the fault_fd file. In this scenario, freeing
the FAULTQ object returns -EBUSY because the opened fault_fd file still
holds an active reference to the FAULTQ object.
Defer FAULTQ resource teardown to a bottom half. This ensures poll() has
fully exited and dropped the file reference before the fault_fd is closed
and the FAULTQ object ID is freed. Additionally, keep the IOMMUFD backend
FD open via iommufd_backend_connect() across the asynchronous bottom half
execution to ensure FAULTQ object freed successfully.
Suggested-by: Shameer Kolothum <skolothumtho@nvidia.com>
Signed-off-by: Zhenzhong Duan <zhenzhong.duan@intel.com>
Reviewed-by: Yi Liu <yi.l.liu@intel.com>
Tested-by: Xudong Hao <xudong.hao@intel.com>
Reviewed-by: Clement Mathieu--Drif <clement.mathieu--drif@bull.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Message-ID: <20261020091955.1607161-7-zhenzhong.duan@intel.com>
---
hw/i386/intel_iommu_accel.c | 59 +++++++++++++++++++++++++++++++++++--
1 file changed, 56 insertions(+), 3 deletions(-)
diff --git a/hw/i386/intel_iommu_accel.c b/hw/i386/intel_iommu_accel.c
index 243501f137..7db8319ad4 100644
--- a/hw/i386/intel_iommu_accel.c
+++ b/hw/i386/intel_iommu_accel.c
@@ -271,17 +271,70 @@ free_faultq:
return false;
}
+typedef struct IOMMUFaultQueue {
+ IOMMUFDBackend *iommufd;
+ uint32_t id;
+ int fd;
+ QLIST_HEAD(, VTDPRQEntry) vtd_prq_list;
+} IOMMUFaultQueue;
+
+static void faultq_teardown_bh(void *opaque)
+{
+ IOMMUFaultQueue *fq = opaque;
+ VTDPRQEntry *prqe, *next;
+
+ QLIST_FOREACH_SAFE(prqe, &fq->vtd_prq_list, next, next) {
+ QLIST_REMOVE(prqe, next);
+ g_free(prqe);
+ }
+
+ qemu_set_fd_handler(fq->fd, NULL, NULL, NULL);
+ close(fq->fd);
+ iommufd_backend_free_id(fq->iommufd, fq->id);
+ iommufd_backend_disconnect(fq->iommufd);
+
+ g_free(fq);
+}
+
static void vtd_destroy_old_fs_faultq(VTDAccelPASIDCacheEntry *vtd_pce)
{
+ HostIOMMUDeviceIOMMUFD *idev =
+ HOST_IOMMU_DEVICE_IOMMUFD(vtd_pce->vtd_hiod->hiod);
+ IOMMUFaultQueue *fq;
+
if (vtd_pce->fault_fd < 0) {
return;
}
- qemu_set_fd_handler(vtd_pce->fault_fd, NULL, NULL, NULL);
- vtd_destroy_fs_faultq(vtd_pce->vtd_hiod, vtd_pce->fault_id,
- vtd_pce->fault_fd);
+ fq = g_malloc(sizeof(*fq));
+ fq->iommufd = idev->iommufd;
+ fq->fd = vtd_pce->fault_fd;
+ fq->id = vtd_pce->fault_id;
vtd_pce->fault_id = 0;
vtd_pce->fault_fd = -1;
+ fq->vtd_prq_list.lh_first = vtd_pce->vtd_prq_list.lh_first;
+ QLIST_INIT(&vtd_pce->vtd_prq_list);
+
+ /*
+ * During a VFIO device hot-unplug, if this is the last device connected
+ * to the IOMMUFDBackend, the backend user count (iommufd->users) will
+ * drop to zero, triggering iommufd_backend_disconnect() which closes
+ * the backend's opened container FD.
+ *
+ * Since the teardown of this fault queue (faultq_teardown_bh) is
+ * scheduled asynchronously as a bottom half, we must temporarily
+ * increment the connection user count of the iommufd backend here.
+ * This guarantees the backend connection remains open and its opened
+ * FD remains active until the async BH completes.
+ *
+ * Incrementing the user count is guaranteed to succeed here because the
+ * VFIO device is still registered as an active user of the backend at
+ * this point in the teardown sequence.
+ */
+ iommufd_backend_connect(fq->iommufd, &error_abort);
+
+ aio_bh_schedule_oneshot(iohandler_get_aio_context(),
+ faultq_teardown_bh, fq);
}
static void vtd_setup_fs_faultq(VTDAccelPASIDCacheEntry *vtd_pce,
--
MST
^ permalink raw reply related [flat|nested] 84+ messages in thread
* [PULL 75/78] hw/virtio/virtio-mem: Fix error message on invalid NUMA node
2026-10-28 17:13 [PULL 00/78] pci, vhost, virtio, iommu, acpi: features, fixes Michael S. Tsirkin
` (73 preceding siblings ...)
2026-10-28 17:17 ` [PULL 74/78] intel_iommu_accel: Teardown FAULTQ resources in bottom half Michael S. Tsirkin
@ 2026-10-28 17:17 ` Michael S. Tsirkin
2026-10-28 17:17 ` [PULL 76/78] net/tap: add tap_fd_get_mtu() to query host tap MTU Michael S. Tsirkin
` (3 subsequent siblings)
78 siblings, 0 replies; 84+ messages in thread
From: Michael S. Tsirkin @ 2026-10-28 17:17 UTC (permalink / raw)
To: qemu-devel; +Cc: Peter Maydell, Gavin Shan, David Hildenbrand
From: Gavin Shan <gshan@redhat.com>
Add space as the separator between "exceeds" and "the number of numa
nodes:". Otherwise, we will see error message with twisted format like
below.
qemu-system-aarch64: -device virtio-mem-pci,id=vmem1,bus=pcie.3, \
memdev=virtio-mem1-mem,requested-size=0,node=1: 'node' property \
has value '1', which exceedsthe number of numa nodes: 1
Signed-off-by: Gavin Shan <gshan@redhat.com>
Reviewed-by: David Hildenbrand <david@kernel.org>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Message-ID: <20261020012404.191329-1-gshan@redhat.com>
---
hw/virtio/virtio-mem.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/hw/virtio/virtio-mem.c b/hw/virtio/virtio-mem.c
index 7130ed852d..ee6a0c206a 100644
--- a/hw/virtio/virtio-mem.c
+++ b/hw/virtio/virtio-mem.c
@@ -846,7 +846,7 @@ static void virtio_mem_device_realize(DeviceState *dev, Error **errp)
if ((nb_numa_nodes && vmem->node >= nb_numa_nodes) ||
(!nb_numa_nodes && vmem->node)) {
error_setg(errp, "'%s' property has value '%" PRIu32 "', which exceeds"
- "the number of numa nodes: %d", VIRTIO_MEM_NODE_PROP,
+ " the number of numa nodes: %d", VIRTIO_MEM_NODE_PROP,
vmem->node, nb_numa_nodes ? nb_numa_nodes : 1);
return;
}
--
MST
^ permalink raw reply related [flat|nested] 84+ messages in thread
* [PULL 76/78] net/tap: add tap_fd_get_mtu() to query host tap MTU
2026-10-28 17:13 [PULL 00/78] pci, vhost, virtio, iommu, acpi: features, fixes Michael S. Tsirkin
` (74 preceding siblings ...)
2026-10-28 17:17 ` [PULL 75/78] hw/virtio/virtio-mem: Fix error message on invalid NUMA node Michael S. Tsirkin
@ 2026-10-28 17:17 ` Michael S. Tsirkin
2026-10-28 17:17 ` [PULL 77/78] net: add generic get_mtu NetClientInfo callback Michael S. Tsirkin
` (2 subsequent siblings)
78 siblings, 0 replies; 84+ messages in thread
From: Michael S. Tsirkin @ 2026-10-28 17:17 UTC (permalink / raw)
To: qemu-devel
Cc: Peter Maydell, Nariman Sayed, Aniket Sahu, Eugenio Pérez,
Jason Wang
From: Nariman Sayed <narimansayed28@gmail.com>
Add a helper function to read the host tap device's configured
MTU via SIOCGIFMTU. Since this ioctl requires a socket rather than
the tap character device fd itself, a separate control socket is
opened to perform the query, following the same pattern used by
net/can/can_socketcan.c.
This is implemented for Linux only, with stub implementations
returning -1 for BSD, Solaris, and the tap-stub backend, matching
the existing pattern used by tap_fd_get_ifname() and
tap_fd_set_steering_ebpf() in those files.
This is a preparatory patch; the function is not yet called from
anywhere.
Signed-off-by: Nariman Sayed <narimansayed28@gmail.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Tested-by: Aniket Sahu <asahu1x@linux.ibm.com>
Acked-by: Eugenio Pérez <eperezma@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Message-ID: <20261007050718.926-2-narimansayed28@gmail.com>
---
net/tap_int.h | 1 +
net/tap-bsd.c | 5 +++++
net/tap-linux.c | 30 ++++++++++++++++++++++++++++++
net/tap-solaris.c | 5 +++++
net/tap-stub.c | 5 +++++
5 files changed, 46 insertions(+)
diff --git a/net/tap_int.h b/net/tap_int.h
index dc4f484006..360d61c889 100644
--- a/net/tap_int.h
+++ b/net/tap_int.h
@@ -45,6 +45,7 @@ int tap_fd_set_vnet_be(int fd, int vnet_is_be);
int tap_fd_enable(int fd);
int tap_fd_disable(int fd);
int tap_fd_get_ifname(int fd, char *ifname);
+int tap_fd_get_mtu(int fd);
int tap_fd_set_steering_ebpf(int fd, int prog_fd);
#endif /* NET_TAP_INT_H */
diff --git a/net/tap-bsd.c b/net/tap-bsd.c
index c39daf9385..17cedccf9e 100644
--- a/net/tap-bsd.c
+++ b/net/tap-bsd.c
@@ -264,6 +264,11 @@ int tap_fd_get_ifname(int fd, char *ifname)
return -1;
}
+int tap_fd_get_mtu(int fd)
+{
+ return -1;
+}
+
int tap_fd_set_steering_ebpf(int fd, int prog_fd)
{
return -1;
diff --git a/net/tap-linux.c b/net/tap-linux.c
index 3cd7d26710..23a10151c1 100644
--- a/net/tap-linux.c
+++ b/net/tap-linux.c
@@ -34,6 +34,7 @@
#include "qapi/error.h"
#include "qemu/error-report.h"
#include "qemu/cutils.h"
+#include "qemu/sockets.h"
#define PATH_NET_TUN "/dev/net/tun"
@@ -346,6 +347,35 @@ int tap_fd_get_ifname(int fd, char *ifname)
return 0;
}
+int tap_fd_get_mtu(int fd)
+{
+ struct ifreq ifr;
+ int s, ret, mtu;
+
+ memset(&ifr, 0, sizeof(ifr));
+ if (tap_fd_get_ifname(fd, ifr.ifr_name) != 0) {
+ return -1;
+ }
+
+ s = qemu_socket(AF_INET, SOCK_DGRAM, 0);
+ if (s < 0) {
+ error_report("could not create control socket: %s",
+ strerror(errno));
+ return -1;
+ }
+
+ ret = ioctl(s, SIOCGIFMTU, &ifr);
+ if (ret != 0) {
+ error_report("SIOCGIFMTU ioctl() failed: %s", strerror(errno));
+ close(s);
+ return -1;
+ }
+
+ mtu = ifr.ifr_mtu;
+ close(s);
+ return mtu;
+}
+
int tap_fd_set_steering_ebpf(int fd, int prog_fd)
{
if (ioctl(fd, TUNSETSTEERINGEBPF, (void *) &prog_fd) != 0) {
diff --git a/net/tap-solaris.c b/net/tap-solaris.c
index 2c6c7af15e..f91a317a9f 100644
--- a/net/tap-solaris.c
+++ b/net/tap-solaris.c
@@ -293,6 +293,11 @@ int tap_fd_get_ifname(int fd, char *ifname)
return -1;
}
+int tap_fd_get_mtu(int fd)
+{
+ return -1;
+}
+
int tap_fd_set_steering_ebpf(int fd, int prog_fd)
{
return -1;
diff --git a/net/tap-stub.c b/net/tap-stub.c
index 6aa60d96ad..756ce882a7 100644
--- a/net/tap-stub.c
+++ b/net/tap-stub.c
@@ -91,6 +91,11 @@ int tap_fd_get_ifname(int fd, char *ifname)
return -1;
}
+int tap_fd_get_mtu(int fd)
+{
+ return -1;
+}
+
int tap_fd_set_steering_ebpf(int fd, int prog_fd)
{
return -1;
--
MST
^ permalink raw reply related [flat|nested] 84+ messages in thread
* [PULL 77/78] net: add generic get_mtu NetClientInfo callback
2026-10-28 17:13 [PULL 00/78] pci, vhost, virtio, iommu, acpi: features, fixes Michael S. Tsirkin
` (75 preceding siblings ...)
2026-10-28 17:17 ` [PULL 76/78] net/tap: add tap_fd_get_mtu() to query host tap MTU Michael S. Tsirkin
@ 2026-10-28 17:17 ` Michael S. Tsirkin
2026-10-28 17:17 ` [PULL 78/78] virtio-net: add mtu-from-tap property for automatic MTU detection Michael S. Tsirkin
2026-10-29 14:45 ` [PULL 00/78] pci, vhost, virtio, iommu, acpi: features, fixes Richard Henderson
78 siblings, 0 replies; 84+ messages in thread
From: Michael S. Tsirkin @ 2026-10-28 17:17 UTC (permalink / raw)
To: qemu-devel; +Cc: Peter Maydell, Nariman Sayed, Eugenio Perez Martin, Jason Wang
From: Nariman Sayed <narimansayed28@gmail.com>
Add a get_mtu callback to NetClientInfo, along with a qemu_get_mtu()
wrapper following the existing qemu_has_ufo()-style pattern, so that
device models can query a netdev backend's host MTU generically
without needing to know or check its backend type.
Wire this up for the tap backend via tap_get_mtu(), which is now
static and only referenced through net_tap_info.get_mtu, matching
the existing pattern used by tap_has_ufo() and similar callbacks.
This allows other backends (e.g. vhost-user, vhost-vdpa) to
implement get_mtu in the future without requiring changes to any
code that calls qemu_get_mtu().
Suggested-by: Eugenio Perez Martin <eperezma@redhat.com>
Signed-off-by: Nariman Sayed <narimansayed28@gmail.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Acked-by: Eugenio Pérez <eperezma@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Message-ID: <20261007050718.926-3-narimansayed28@gmail.com>
---
include/net/net.h | 3 +++
net/net.c | 9 +++++++++
net/tap.c | 8 ++++++++
3 files changed, 20 insertions(+)
diff --git a/include/net/net.h b/include/net/net.h
index fdf6a2ddd4..de4f03c301 100644
--- a/include/net/net.h
+++ b/include/net/net.h
@@ -73,6 +73,7 @@ typedef bool (HasVnetHdr)(NetClientState *);
typedef bool (HasVnetHdrLen)(NetClientState *, int);
typedef void (SetOffload)(NetClientState *, const NetOffloads *);
typedef int (GetVnetHdrLen)(NetClientState *);
+typedef int (GetMtu)(NetClientState *);
typedef void (SetVnetHdrLen)(NetClientState *, int);
typedef bool (GetVnetHashSupportedTypes)(NetClientState *, uint32_t *);
typedef int (SetVnetLE)(NetClientState *, bool);
@@ -113,6 +114,7 @@ typedef struct NetClientInfo {
NetCheckPeerType *check_peer_type;
IsWaitIncoming *is_wait_incoming;
GetVHostNet *get_vhost_net;
+ GetMtu *get_mtu;
} NetClientInfo;
struct NetClientState {
@@ -214,6 +216,7 @@ bool qemu_has_uso(NetClientState *nc);
bool qemu_has_tunnel(NetClientState *nc);
bool qemu_has_vnet_hdr(NetClientState *nc);
bool qemu_has_vnet_hdr_len(NetClientState *nc, int len);
+int qemu_get_mtu(NetClientState *nc);
void qemu_set_offload(NetClientState *nc, const NetOffloads *ol);
int qemu_get_vnet_hdr_len(NetClientState *nc);
void qemu_set_vnet_hdr_len(NetClientState *nc, int len);
diff --git a/net/net.c b/net/net.c
index d7fa637ce5..bf7e5d9b12 100644
--- a/net/net.c
+++ b/net/net.c
@@ -514,6 +514,15 @@ bool qemu_has_ufo(NetClientState *nc)
return nc->info->has_ufo(nc);
}
+int qemu_get_mtu(NetClientState *nc)
+{
+ if (!nc || !nc->info->get_mtu) {
+ return -1;
+ }
+
+ return nc->info->get_mtu(nc);
+}
+
bool qemu_has_uso(NetClientState *nc)
{
if (!nc || !nc->info->has_uso) {
diff --git a/net/tap.c b/net/tap.c
index acccabca56..665dee800d 100644
--- a/net/tap.c
+++ b/net/tap.c
@@ -446,6 +446,13 @@ int tap_get_fd(NetClientState *nc)
return s->fd;
}
+static int tap_get_mtu(NetClientState *nc)
+{
+ TAPState *s = container_of(nc, TAPState, nc);
+ assert(nc->info->type == NET_CLIENT_DRIVER_TAP);
+ return tap_fd_get_mtu(s->fd);
+}
+
/*
* tap_get_vhost_net() can return NULL if a tap net-device backend is
* created with 'vhost=off' option, 'vhostforce=off' or no vhost or
@@ -607,6 +614,7 @@ static NetClientInfo net_tap_info = {
.set_steering_ebpf = tap_set_steering_ebpf,
.is_wait_incoming = tap_is_wait_incoming,
.get_vhost_net = tap_get_vhost_net,
+ .get_mtu = tap_get_mtu,
};
static TAPState *new_tap(NetClientState *peer,
--
MST
^ permalink raw reply related [flat|nested] 84+ messages in thread
* [PULL 78/78] virtio-net: add mtu-from-tap property for automatic MTU detection
2026-10-28 17:13 [PULL 00/78] pci, vhost, virtio, iommu, acpi: features, fixes Michael S. Tsirkin
` (76 preceding siblings ...)
2026-10-28 17:17 ` [PULL 77/78] net: add generic get_mtu NetClientInfo callback Michael S. Tsirkin
@ 2026-10-28 17:17 ` Michael S. Tsirkin
2026-10-29 14:45 ` [PULL 00/78] pci, vhost, virtio, iommu, acpi: features, fixes Richard Henderson
78 siblings, 0 replies; 84+ messages in thread
From: Michael S. Tsirkin @ 2026-10-28 17:17 UTC (permalink / raw)
To: qemu-devel
Cc: Peter Maydell, Nariman Sayed, Aniket Sahu, Eugenio Pérez,
Jason Wang
From: Nariman Sayed <narimansayed28@gmail.com>
Currently, exposing the host tap device's MTU to the guest via
VIRTIO_NET_F_MTU requires manually specifying host_mtu= on the
virtio-net-pci device, duplicating the MTU already configured on
the host's tap interface.
Add a new mtu-from-tap boolean property (default off, preserving
existing behavior). When enabled, the netdev peer's MTU is queried
via the generic qemu_get_mtu() and used to set net_conf.mtu and
enable VIRTIO_NET_F_MTU, regardless of the backend's type.
This detection must happen in virtio_net_device_realize(), before
virtio_net_set_config_size() computes the virtio config space
size from host_features. Performing the detection later (e.g. in
the get_features_ex() callback, which runs after the NIC and its
config space are already sized) results in the mtu config field
falling outside the sized config space, and the guest reading
back an unmapped garbage value (0xffff) instead of the intended
MTU.
Since qemu_new_nic() has not yet been called at this point in
realize(), the peer is accessed via n->nic_conf.peers.ncs[0]
(populated during property parsing, before realize() runs, via
the netdev= property) rather than through n->nic.
Signed-off-by: Nariman Sayed <narimansayed28@gmail.com>
Tested-by: Aniket Sahu <asahu1x@linux.ibm.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Acked-by: Eugenio Pérez <eperezma@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Message-ID: <20261007050718.926-4-narimansayed28@gmail.com>
---
include/hw/virtio/virtio-net.h | 1 +
hw/net/virtio-net.c | 10 ++++++++++
2 files changed, 11 insertions(+)
diff --git a/include/hw/virtio/virtio-net.h b/include/hw/virtio/virtio-net.h
index 8c967760c2..fed37245be 100644
--- a/include/hw/virtio/virtio-net.h
+++ b/include/hw/virtio/virtio-net.h
@@ -52,6 +52,7 @@ typedef struct virtio_net_conf
uint16_t rx_queue_size;
uint16_t tx_queue_size;
uint16_t mtu;
+ bool mtu_from_tap;
int32_t speed;
char *duplex_str;
uint8_t duplex;
diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c
index 4794496b96..54e42cfec9 100644
--- a/hw/net/virtio-net.c
+++ b/hw/net/virtio-net.c
@@ -4006,6 +4006,15 @@ static void virtio_net_device_realize(DeviceState *dev, Error **errp)
n->host_features |= (1ULL << VIRTIO_NET_F_MTU);
}
+ if (n->net_conf.mtu_from_tap) {
+ NetClientState *peer = n->nic_conf.peers.ncs[0];
+ int mtu = qemu_get_mtu(peer);
+ if (mtu > 0) {
+ n->net_conf.mtu = mtu;
+ n->host_features |= (1ULL << VIRTIO_NET_F_MTU);
+ }
+ }
+
if (n->net_conf.duplex_str) {
if (strncmp(n->net_conf.duplex_str, "half", 5) == 0) {
n->net_conf.duplex = DUPLEX_HALF;
@@ -4386,6 +4395,7 @@ static const Property virtio_net_properties[] = {
DEFINE_PROP_UINT16("tx_queue_size", VirtIONet, net_conf.tx_queue_size,
VIRTIO_NET_TX_QUEUE_DEFAULT_SIZE),
DEFINE_PROP_UINT16("host_mtu", VirtIONet, net_conf.mtu, 0),
+ DEFINE_PROP_BOOL("mtu-from-tap", VirtIONet, net_conf.mtu_from_tap, false),
DEFINE_PROP_INT32("speed", VirtIONet, net_conf.speed, SPEED_UNKNOWN),
DEFINE_PROP_STRING("duplex", VirtIONet, net_conf.duplex_str),
DEFINE_PROP_BOOL("failover", VirtIONet, failover, false),
--
MST
^ permalink raw reply related [flat|nested] 84+ messages in thread
* Re: [PULL 40/78] pcie_sriov: register user created virtual function before realizing it
2026-10-28 17:15 ` [PULL 40/78] pcie_sriov: register user created virtual function before realizing it Michael S. Tsirkin
@ 2026-10-28 17:34 ` Daniel Paziyski
0 siblings, 0 replies; 84+ messages in thread
From: Daniel Paziyski @ 2026-10-28 17:34 UTC (permalink / raw)
To: Michael S. Tsirkin; +Cc: qemu-devel, Peter Maydell, qemu-stable
Hello Michael,
I think you have queued this patch for pulling on accident. There is a bug here,
which is yet to be solved, as you have pointed out in
https://lore.kernel.org/qemu-devel/20261028074508-mutt-send-email-mst@kernel.org/T/#mca344ac148a62107cdc298e97e6713b6657b5c55
Please, hold off this patch for a while, until I come up with a fix soon.
Thank you,
Daniel
On Wed, 28 Oct 2026 at 18:15, Michael S. Tsirkin <mst@redhat.com> wrote:
>
> From: Daniel Paziyski <danielpaziyski@gmail.com>
>
> There are two ways of creating virtual functions: by using the sriov-pf device
> parameter (this way, creating a user created VF and binding it to a PF), or
> by using a device specific parameter, where the device manually creates a given
> amount of VFs.
>
> When a PCI device is realized, the device specific realize function is called
> first, and then the pcie_sriov_register_device function is called, which checks
> whether the device that has been created is a user created VF, and if it is the
> case and the device allows this kind of VFs, it inserts it into a hashmap, with
> the key being the ID of the PF, and the items being arrays of VFs.
>
> User created VFs are instantiated independently, and later, when the PF calls
> pcie_sriov_pf_init_from_user_created_vfs, it discovers its VFs from the hashmap
> mentioned previously, and sets up in their PCIDevice.ex.sriov_pf structure
> the pointer to the PF. This means that, during realization, VFs have no access
> to the PF.
>
> Device created VFs are instantiated during or after PF realization using the
> pcie_sriov_pf_init function, which sets up for the VFs the pointer to the PF
> before their realization, so when they're realized, they can access the PF
> with no issues.
>
> The problem here is that pcie_sriov_register_device is called after the
> realization, and not before. This means that when a user created VF is created
> for a device which does not support user created VFs, but supports device
> created VFs, the realize function will notice that a VF is being created, and
> may try to access the PF, causing a null pointer dereference fault.
>
> Fix this by placing the user created VF check and registering before the
> realization. This way, incorrectly created user created VFs will be noticed,
> and device creation will be aborted.
>
> Additionally, remove from the pcie_sriov_register_device function the top
> check. It seems that this check is done to error out if the PF failed for some
> reason to initialize its list of user created VFs. However, in such situations,
> the pcie_sriov_pf_init_from_user_created_vfs function will error during
> realization, and it will be caught before the check is done at all. Moreover,
> since now pcie_sriov_register_device is called before realization, the check
> will always fail for PFs with user created VFs, because the list will be
> populated during realization.
>
> The rest of the function though, correctly errors out if a user created VF
> is created for an unsupported device type, if a VF is created for a non-PCIe
> device, or if the PF is already instantiated, with now the advantage being
> that the check is done before the VF instantiation.
>
> Moreover, move the unregistering of a device's acpi-index and the removal
> of a VF from its PF list from pci_qdev_unrealize to do_pci_unregister_device.
> We do the former so that the acpi-index is unclaimed on user created VF
> creation error, and the latter to not have extra code on the device specific
> realization error path.
>
> When instantiating a user created VF for a NVME controller:
>
> Command line:
>
> qemu-system-x86_64 -device nvme-subsys,id=subsys0 \
> -device nvme,id=vctrl0,sriov-pf=ctrl0,subsys=subsys0 \
> -device nvme,id=ctrl0,subsys=subsys0,serial=s
>
> ASAN splat:
>
> ../hw/nvme/ctrl.c:9613:28: runtime error: member access within null pointer of type 'struct NvmeCtrl'
> AddressSanitizer:DEADLYSIGNAL
> =================================================================
> ==91050==ERROR: AddressSanitizer: SEGV on unknown address 0x000000001cf0 (pc 0x7f79a8573dcd bp 0x7ffd622c1bc0 sp 0x7ffd622c1b68 T0)
> ==91050==The signal is caused by a READ memory access.
> #0 0x7f79a8573dcd (/usr/lib/libc.so.6+0x173dcd) (BuildId: 1fa174a830cef40a5b2388add4318ee2795f573e)
> #1 0x5644c6c0266e in nvme_realize ../hw/nvme/ctrl.c:9613
> #2 0x5644c6c67213 in pci_qdev_realize ../hw/pci/pci.c:2316
> #3 0x5644c7a2cfc1 in device_set_realized ../hw/core/qdev.c:514
> #4 0x5644c7a4f767 in property_set_bool ../qom/object.c:2484
> #5 0x5644c7a48d0b in object_property_set ../qom/object.c:1548
> #6 0x5644c7a568a5 in object_property_set_qobject ../qom/qom-qobject.c:28
> #7 0x5644c7a49385 in object_property_set_bool ../qom/object.c:1618
> #8 0x5644c7a2aeb0 in qdev_realize ../hw/core/qdev.c:277
> #9 0x5644c735f29f in qdev_device_add_from_qdict ../system/qdev-monitor.c:740
> #10 0x5644c735f3ab in qdev_device_add ../system/qdev-monitor.c:758
> #11 0x5644c72b1901 in device_init_func ../system/vl.c:1217
> #12 0x5644c829d4a3 in qemu_opts_foreach ../util/qemu-option.c:1148
> #13 0x5644c72bc33e in qemu_create_cli_devices ../system/vl.c:2762
> #14 0x5644c72bcaa1 in qmp_x_exit_preconfig ../system/vl.c:2822
> #15 0x5644c72c3241 in qemu_init ../system/vl.c:3862
> #16 0x5644c801abf8 in main ../system/main.c:71
> #17 0x7f79a8427780 (/usr/lib/libc.so.6+0x27780) (BuildId: 1fa174a830cef40a5b2388add4318ee2795f573e)
> #18 0x7f79a84278b8 in __libc_start_main (/usr/lib/libc.so.6+0x278b8) (BuildId: 1fa174a830cef40a5b2388add4318ee2795f573e)
> #19 0x5644c5f0a1f4 in _start (BuildId: 8483f952216d9e345c3744300d0aefa38feb50d7)
>
> ==91050==Register values:
> rax = 0x00007e49988640f0 rbx = 0x00007e49988640f0 rcx = 0x00000fc9b3104828 rdx = 0x0000000000000058
> rdi = 0x00007e49988640f0 rsi = 0x0000000000001cf0 rbp = 0x00007ffd622c1bc0 rsp = 0x00007ffd622c1b68
> r8 = 0x00000fc9b3104829 r9 = 0x00000fc9b3104828 r10 = 0x00000fc9b310481e r11 = 0x00000fc9b310481e
> r12 = 0x0000000000001cf0 r13 = 0x00000f6f32e78578 r14 = 0x00000000ffffffff r15 = 0x00007ffd622c1c10
> AddressSanitizer can not provide additional info.
> SUMMARY: AddressSanitizer: SEGV (/usr/lib/libc.so.6+0x173dcd) (BuildId: 1fa174a830cef40a5b2388add4318ee2795f573e)
> ==91050==ABORTING
>
> Cc: qemu-stable@nongnu.org
> Fixes: 19e55471d4e8 ("pcie_sriov: Allow user to create SR-IOV device")
> Signed-off-by: Daniel Paziyski <danielpaziyski@gmail.com>
> Message-ID: <20260905122924.53813-1-danielpaziyski@gmail.com>
> ---
> hw/pci/pci.c | 42 ++++++++++++++++++++++++------------------
> hw/pci/pcie_sriov.c | 6 ------
> 2 files changed, 24 insertions(+), 24 deletions(-)
>
> diff --git a/hw/pci/pci.c b/hw/pci/pci.c
> index 0efb4eb4bb..2d2fe02c68 100644
> --- a/hw/pci/pci.c
> +++ b/hw/pci/pci.c
> @@ -1227,6 +1227,19 @@ static void pci_config_free(PCIDevice *pci_dev)
> g_free(pci_dev->used);
> }
>
> +static void pci_unregister_acpi_index(PCIDevice *pci_dev)
> +{
> + if (pci_dev->acpi_index) {
> + GSequence *used_indexes = pci_acpi_index_list();
> +
> + g_sequence_remove(g_sequence_lookup(used_indexes,
> + GINT_TO_POINTER(pci_dev->acpi_index),
> + g_cmp_uint32, NULL));
> +
> + pci_dev->acpi_index = 0;
> + }
> +}
> +
> static void do_pci_unregister_device(PCIDevice *pci_dev)
> {
> pci_get_bus(pci_dev)->devices[pci_dev->devfn] = NULL;
> @@ -1240,6 +1253,9 @@ static void do_pci_unregister_device(PCIDevice *pci_dev)
> &pci_dev->bus_master_enable_region);
> }
> address_space_destroy(&pci_dev->bus_master_as);
> +
> + pcie_sriov_unregister_device(pci_dev);
> + pci_unregister_acpi_index(pci_dev);
> }
>
> /* Extract PCIReqIDCache into BDF format */
> @@ -1491,7 +1507,6 @@ static void pci_qdev_unrealize(DeviceState *dev)
>
> pci_unregister_io_regions(pci_dev);
> pci_del_option_rom(pci_dev);
> - pcie_sriov_unregister_device(pci_dev);
>
> if (pc->exit) {
> pc->exit(pci_dev);
> @@ -1501,17 +1516,6 @@ static void pci_qdev_unrealize(DeviceState *dev)
> do_pci_unregister_device(pci_dev);
>
> pci_dev->msi_trigger = NULL;
> -
> - /*
> - * clean up acpi-index so it could reused by another device
> - */
> - if (pci_dev->acpi_index) {
> - GSequence *used_indexes = pci_acpi_index_list();
> -
> - g_sequence_remove(g_sequence_lookup(used_indexes,
> - GINT_TO_POINTER(pci_dev->acpi_index),
> - g_cmp_uint32, NULL));
> - }
> }
>
> void pci_register_bar(PCIDevice *pci_dev, int region_num,
> @@ -2326,8 +2330,15 @@ static void pci_qdev_realize(DeviceState *qdev, Error **errp)
> pci_dev = do_pci_register_device(pci_dev,
> object_get_typename(OBJECT(qdev)),
> pci_dev->devfn, errp);
> - if (pci_dev == NULL)
> + if (pci_dev == NULL) {
> + pci_unregister_acpi_index(pci_dev);
> return;
> + }
> +
> + if (!pcie_sriov_register_device(pci_dev, errp)) {
> + do_pci_unregister_device(pci_dev);
> + return;
> + }
>
> if (pc->realize) {
> pc->realize(pci_dev, &local_err);
> @@ -2338,11 +2349,6 @@ static void pci_qdev_realize(DeviceState *qdev, Error **errp)
> }
> }
>
> - if (!pcie_sriov_register_device(pci_dev, errp)) {
> - pci_qdev_unrealize(DEVICE(pci_dev));
> - return;
> - }
> -
> /*
> * A PCIe Downstream Port that do not have ARI Forwarding enabled must
> * associate only Device 0 with the device attached to the bus
> diff --git a/hw/pci/pcie_sriov.c b/hw/pci/pcie_sriov.c
> index c41ac95bee..69930c7b8c 100644
> --- a/hw/pci/pcie_sriov.c
> +++ b/hw/pci/pcie_sriov.c
> @@ -357,12 +357,6 @@ int16_t pcie_sriov_pf_init_from_user_created_vfs(PCIDevice *dev,
>
> bool pcie_sriov_register_device(PCIDevice *dev, Error **errp)
> {
> - if (!dev->exp.sriov_pf.vf && dev->qdev.id &&
> - pfs && g_hash_table_contains(pfs, dev->qdev.id)) {
> - error_setg(errp, "attaching user-created SR-IOV VF unsupported");
> - return false;
> - }
> -
> if (dev->sriov_pf) {
> PCIDevice *pci_pf;
> GPtrArray *pf;
> --
> MST
>
^ permalink raw reply [flat|nested] 84+ messages in thread
* Re: [PULL 00/78] pci, vhost, virtio, iommu, acpi: features, fixes
2026-10-28 17:13 [PULL 00/78] pci, vhost, virtio, iommu, acpi: features, fixes Michael S. Tsirkin
` (77 preceding siblings ...)
2026-10-28 17:17 ` [PULL 78/78] virtio-net: add mtu-from-tap property for automatic MTU detection Michael S. Tsirkin
@ 2026-10-29 14:45 ` Richard Henderson
2026-10-29 18:31 ` Michael S. Tsirkin
78 siblings, 1 reply; 84+ messages in thread
From: Richard Henderson @ 2026-10-29 14:45 UTC (permalink / raw)
To: Michael S. Tsirkin, qemu-devel; +Cc: Peter Maydell
On 10/28/26 12:13, Michael S. Tsirkin wrote:
> The following changes since commit 29b1dc631d6ac6bfc39464e0be8bf4cad556a591:
>
> Merge tag 'block-pull-request' ofhttps://gitlab.com/stefanha/qemu into staging (2026-10-27 20:02:57 -0500)
>
> are available in the Git repository at:
>
> https://git.kernel.org/pub/scm/virt/kvm/mst/qemu.git tags/for_upstream
>
> for you to fetch changes up to c155e7e9e025b60ce17e26a7c382d86f3f9c9fee:
>
> virtio-net: add mtu-from-tap property for automatic MTU detection (2026-10-28 13:11:16 -0400)
>
> ----------------------------------------------------------------
> pci, vhost, virtio, iommu, acpi: features, fixes
>
> multiboot can again load 64 bit images
> vdpa-dev now implements guest_notifier_mask/pending
> pci bridge can now configure the negotiated bandwidth
> tap can now auto-detect host mtu and expose to guest
> amd iommu can now attach on non system bus
> vhost-user spec now has its own mailing list
>
> Fixes, cleanups all over the place.
New failures:
https://gitlab.com/qemu-project/qemu/-/jobs/17577211343#L867
https://gitlab.com/qemu-project/qemu/-/jobs/17576922092#L185
Use of sudo in containers where it is not installed
(functional-system-centos, functional-system-opensuse).
r~
^ permalink raw reply [flat|nested] 84+ messages in thread
* Re: [PULL 00/78] pci, vhost, virtio, iommu, acpi: features, fixes
2026-10-29 14:45 ` [PULL 00/78] pci, vhost, virtio, iommu, acpi: features, fixes Richard Henderson
@ 2026-10-29 18:31 ` Michael S. Tsirkin
2026-10-29 22:07 ` Richard Henderson
0 siblings, 1 reply; 84+ messages in thread
From: Michael S. Tsirkin @ 2026-10-29 18:31 UTC (permalink / raw)
To: Richard Henderson; +Cc: qemu-devel, Peter Maydell, Vladimir Sementsov-Ogievskiy
On Thu, Oct 29, 2026 at 09:45:59AM -0500, Richard Henderson wrote:
> On 10/28/26 12:13, Michael S. Tsirkin wrote:
> > The following changes since commit 29b1dc631d6ac6bfc39464e0be8bf4cad556a591:
> >
> > Merge tag 'block-pull-request' ofhttps://gitlab.com/stefanha/qemu into staging (2026-10-27 20:02:57 -0500)
> >
> > are available in the Git repository at:
> >
> > https://git.kernel.org/pub/scm/virt/kvm/mst/qemu.git tags/for_upstream
> >
> > for you to fetch changes up to c155e7e9e025b60ce17e26a7c382d86f3f9c9fee:
> >
> > virtio-net: add mtu-from-tap property for automatic MTU detection (2026-10-28 13:11:16 -0400)
> >
> > ----------------------------------------------------------------
> > pci, vhost, virtio, iommu, acpi: features, fixes
> >
> > multiboot can again load 64 bit images
> > vdpa-dev now implements guest_notifier_mask/pending
> > pci bridge can now configure the negotiated bandwidth
> > tap can now auto-detect host mtu and expose to guest
> > amd iommu can now attach on non system bus
> > vhost-user spec now has its own mailing list
> >
> > Fixes, cleanups all over the place.
>
> New failures:
>
> https://gitlab.com/qemu-project/qemu/-/jobs/17577211343#L867
> https://gitlab.com/qemu-project/qemu/-/jobs/17576922092#L185
>
> Use of sudo in containers where it is not installed
> (functional-system-centos, functional-system-opensuse).
>
>
> r~
I dropped the test patch, thanks!
head at 592bb52d87a1a07172656a91cf5c38f63b5f6c60 now.
Vladimir, pls look into fixing all this up.
--
MST
^ permalink raw reply [flat|nested] 84+ messages in thread
* Re: [PULL 00/78] pci, vhost, virtio, iommu, acpi: features, fixes
2026-10-29 18:31 ` Michael S. Tsirkin
@ 2026-10-29 22:07 ` Richard Henderson
2026-10-29 22:44 ` Daniel Paziyski
0 siblings, 1 reply; 84+ messages in thread
From: Richard Henderson @ 2026-10-29 22:07 UTC (permalink / raw)
To: Michael S. Tsirkin
Cc: qemu-devel, Peter Maydell, Vladimir Sementsov-Ogievskiy
On 10/29/26 13:31, Michael S. Tsirkin wrote:
> On Thu, Oct 29, 2026 at 09:45:59AM -0500, Richard Henderson wrote:
>> On 10/28/26 12:13, Michael S. Tsirkin wrote:
>>> The following changes since commit 29b1dc631d6ac6bfc39464e0be8bf4cad556a591:
>>>
>>> Merge tag 'block-pull-request' ofhttps://gitlab.com/stefanha/qemu into staging (2026-10-27 20:02:57 -0500)
>>>
>>> are available in the Git repository at:
>>>
>>> https://git.kernel.org/pub/scm/virt/kvm/mst/qemu.git tags/for_upstream
>>>
>>> for you to fetch changes up to c155e7e9e025b60ce17e26a7c382d86f3f9c9fee:
>>>
>>> virtio-net: add mtu-from-tap property for automatic MTU detection (2026-10-28 13:11:16 -0400)
>>>
>>> ----------------------------------------------------------------
>>> pci, vhost, virtio, iommu, acpi: features, fixes
>>>
>>> multiboot can again load 64 bit images
>>> vdpa-dev now implements guest_notifier_mask/pending
>>> pci bridge can now configure the negotiated bandwidth
>>> tap can now auto-detect host mtu and expose to guest
>>> amd iommu can now attach on non system bus
>>> vhost-user spec now has its own mailing list
>>>
>>> Fixes, cleanups all over the place.
>>
>> New failures:
>>
>> https://gitlab.com/qemu-project/qemu/-/jobs/17577211343#L867
>> https://gitlab.com/qemu-project/qemu/-/jobs/17576922092#L185
>>
>> Use of sudo in containers where it is not installed
>> (functional-system-centos, functional-system-opensuse).
>>
>>
>> r~
>
> I dropped the test patch, thanks!
> head at 592bb52d87a1a07172656a91cf5c38f63b5f6c60 now.
Applied, thanks.
r~
^ permalink raw reply [flat|nested] 84+ messages in thread
* Re: [PULL 00/78] pci, vhost, virtio, iommu, acpi: features, fixes
2026-10-29 22:07 ` Richard Henderson
@ 2026-10-29 22:44 ` Daniel Paziyski
0 siblings, 0 replies; 84+ messages in thread
From: Daniel Paziyski @ 2026-10-29 22:44 UTC (permalink / raw)
To: richard.henderson
Cc: mst, peter.maydell, qemu-devel, vsementsov, danielpaziyski
Hi,
Please revert commit 432ea8cc7d27 ("pcie_sriov: register user created virtual function before realizing it"),
for the reason explained in https://lore.kernel.org/qemu-devel/CANukVxsB2n2GCGxQk3X2SUzNTJQ0M30ZxxmF=oGZtS-OCmbw_A@mail.gmail.com/
The v2 is at https://lore.kernel.org/qemu-devel/20261028213800.236067-1-danielpaziyski@gmail.com/
Sorry, I should have made the case as a reply to the pull for greater visibility.
Thanks,
Daniel
^ permalink raw reply [flat|nested] 84+ messages in thread
end of thread, other threads:[~2026-10-29 22:46 UTC | newest]
Thread overview: 84+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-28 17:13 [PULL 00/78] pci, vhost, virtio, iommu, acpi: features, fixes Michael S. Tsirkin
2026-10-28 17:13 ` [PULL 01/78] libvhost-user: protect against OOB memory table access Michael S. Tsirkin
2026-10-28 17:13 ` [PULL 02/78] vhost-user: fix SET_MEM_TABLE number of regions Michael S. Tsirkin
2026-10-28 17:14 ` [PULL 03/78] hw/virtio/vhost-shadow-virtqueue: fix range boundary in SVQ translation Michael S. Tsirkin
2026-10-28 17:14 ` [PULL 04/78] tests/functional: add test_tap_migration Michael S. Tsirkin
2026-10-28 17:14 ` [PULL 05/78] net/tap: tap_post_load(): poll only on success Michael S. Tsirkin
2026-10-28 17:14 ` [PULL 06/78] hw/net: finish backend initialization earler for local migration Michael S. Tsirkin
2026-10-28 17:14 ` [PULL 07/78] net/tap: move vmstate initialization to new_tap() Michael S. Tsirkin
2026-10-28 17:14 ` [PULL 08/78] net/tap: net_init_tap_one(): fix failure path Michael S. Tsirkin
2026-10-28 17:14 ` [PULL 09/78] Revert 9696846600ca ("multiboot: Prevent loading of x86_64 images") Michael S. Tsirkin
2026-10-28 17:14 ` [PULL 10/78] migration/cpr: forbid saving duplicate CprFds into hash table Michael S. Tsirkin
2026-10-28 17:14 ` [PULL 11/78] tests/unit: add test for duplicate keys in CPR FD table Michael S. Tsirkin
2026-10-28 17:14 ` [PULL 12/78] migration: forbid snapshots in CPR migration modes Michael S. Tsirkin
2026-10-28 17:14 ` [PULL 13/78] migration: forbid snapshots under Xen Michael S. Tsirkin
2026-10-28 17:14 ` [PULL 14/78] tests/qtest/migration: add test for snapshot rejection in CPR modes Michael S. Tsirkin
2026-10-28 17:14 ` [PULL 15/78] vhost-vsock: block CPR migration modes Michael S. Tsirkin
2026-10-28 17:14 ` [PULL 16/78] vhost: add vhost_reset_owner op Michael S. Tsirkin
2026-10-28 17:14 ` [PULL 17/78] vhost-vsock: don't reset connections during CPR Michael S. Tsirkin
2026-10-28 17:14 ` [PULL 18/78] vhost-vsock: fix FD leak in realize() Michael S. Tsirkin
2026-10-28 17:14 ` [PULL 19/78] vhost-vsock: preserve vhost FD during CPR Michael S. Tsirkin
2026-10-28 17:14 ` [PULL 20/78] vhost: factor out vhost_dev_init_backend() Michael S. Tsirkin
2026-10-28 17:14 ` [PULL 21/78] vhost: make vhost_dev_cleanup() safe on a partially initialized device Michael S. Tsirkin
2026-10-28 17:14 ` [PULL 22/78] vhost: add vhost_dev_set_owner() / vhost_dev_reset_owner() helpers Michael S. Tsirkin
2026-10-28 17:14 ` [PULL 23/78] vhost: add vhost_dev_is_initialized() helper Michael S. Tsirkin
2026-10-28 17:15 ` [PULL 24/78] vhost: release ownership in vhost_dev_cleanup() Michael S. Tsirkin
2026-10-28 17:15 ` [PULL 25/78] migration: add MIG_EVENT_INCOMING_FAILED Michael S. Tsirkin
2026-10-28 17:15 ` [PULL 26/78] vhost: release ownership when the incoming migration fails Michael S. Tsirkin
2026-10-28 17:15 ` [PULL 27/78] vhost: release ownership on exit Michael S. Tsirkin
2026-10-28 17:15 ` [PULL 28/78] vhost-vsock: hand off device ownership across CPR Michael S. Tsirkin
2026-10-28 17:15 ` [PULL 29/78] tests/qtest/migration: add vhost-vsock tests for cpr-transfer and cpr-exec Michael S. Tsirkin
2026-10-28 17:15 ` [PULL 30/78] tests/functional: add vhost-vsock CPR test with a live guest Michael S. Tsirkin
2026-10-28 17:15 ` [PULL 31/78] tests/functional: cover a cpr-transfer target going away with vhost-vsock Michael S. Tsirkin
2026-10-28 17:15 ` [PULL 32/78] qapi/virtio: Restore backend features in x-query-virtio-status Michael S. Tsirkin
2026-10-28 17:15 ` [PULL 33/78] qapi/monitor: Handle missing vhost-user protocol features Michael S. Tsirkin
2026-10-28 17:15 ` [PULL 34/78] hw/cxl: destroy primary CCI before re-initialization on reset Michael S. Tsirkin
2026-10-28 17:15 ` [PULL 35/78] hw/cxl: convert cxl-type3 to three-phase reset Michael S. Tsirkin
2026-10-28 17:15 ` [PULL 36/78] hw/cxl: add cxl_destroy_mailbox_t3() as the mailbox teardown counterpart Michael S. Tsirkin
2026-10-28 17:15 ` [PULL 37/78] hw/cxl: discard in-flight sanitize state on mailbox teardown Michael S. Tsirkin
2026-10-28 17:15 ` [PULL 38/78] hw/cxl: clear event logs, scan media and interrupt policy on reset Michael S. Tsirkin
2026-10-28 17:15 ` [PULL 39/78] hw/cxl: clear poison lists and feature transfer state " Michael S. Tsirkin
2026-10-28 17:15 ` [PULL 40/78] pcie_sriov: register user created virtual function before realizing it Michael S. Tsirkin
2026-10-28 17:34 ` Daniel Paziyski
2026-10-28 17:15 ` [PULL 41/78] hw/pci-bridge: Add generic PCIe switch upstream and downstream ports Michael S. Tsirkin
2026-10-28 17:15 ` [PULL 42/78] hw/pci-bridge: Make speed and width for PCIe ports non-experimental Michael S. Tsirkin
2026-10-28 17:15 ` [PULL 43/78] hw/pci-bridge: Default the generic PCIe root port as Gen6 capable Michael S. Tsirkin
2026-10-28 17:15 ` [PULL 44/78] hw/pci: Ensure PCIe link speed/width is consistent between both ends Michael S. Tsirkin
2026-10-28 17:15 ` [PULL 45/78] vdpa-dev: implement guest_notifier_mask/pending Michael S. Tsirkin
2026-10-28 17:15 ` [PULL 46/78] vhost-user: remove shared object entries during cleanup Michael S. Tsirkin
2026-10-28 17:16 ` [PULL 47/78] vhost: fix off-by-one in vhost_svq_next_desc with IN_ORDER Michael S. Tsirkin
2026-10-28 17:16 ` [PULL 48/78] pcie_sriov: fix wrong write width and off-by-one in pf_reset Michael S. Tsirkin
2026-10-28 17:16 ` [PULL 49/78] util/event_notifier: more strict API Michael S. Tsirkin
2026-10-28 17:16 ` [PULL 50/78] virtio-qmp: Fix out-of-bounds vqs index in vhost queue status Michael S. Tsirkin
2026-10-28 17:16 ` [PULL 51/78] contrib/vhost-user-bridge: Use stderr for debug output Michael S. Tsirkin
2026-10-28 17:16 ` [PULL 52/78] tests/functional/x86_64/test_vhost_user_bridge: Eliminate stdbuf usage Michael S. Tsirkin
2026-10-28 17:16 ` [PULL 53/78] pcie_aer: fix pci_set_long overflow into PCI_EXP_LNKCAP Michael S. Tsirkin
2026-10-28 17:16 ` [PULL 54/78] MAINTAINERS: Update Junjie Cao's email address Michael S. Tsirkin
2026-10-28 17:16 ` [PULL 55/78] hw/arm: remove pci-bridge/pci_expander_bridge.h from virt.c Michael S. Tsirkin
2026-10-28 17:16 ` [PULL 56/78] hw/pci-bridge: split CXL PXB into separate file Michael S. Tsirkin
2026-10-28 17:16 ` [PULL 57/78] vhost-user: add missing stub for vhost_user_has_protocol_feature() Michael S. Tsirkin
2026-10-28 17:16 ` [PULL 58/78] vhost: move skip-drain feature check to vhost-user Michael S. Tsirkin
2026-10-28 17:16 ` [PULL 59/78] virtio-snd: don't use virtio_snd_pcm_get_stream() in virtio_snd_post_load() Michael S. Tsirkin
2026-10-28 17:16 ` [PULL 60/78] vhost-user: add dedicated mailing list Michael S. Tsirkin
2026-10-28 17:16 ` [PULL 61/78] amd_iommu: Assign ioapic_as only when interrupt remapping is supported Michael S. Tsirkin
2026-10-28 17:16 ` [PULL 62/78] x86-iommu: Introduce x86_iommu_device_get() for x86-iommu retrieval Michael S. Tsirkin
2026-10-28 17:16 ` [PULL 63/78] amd_iommu: Add support for get_x86_iommu callback Michael S. Tsirkin
2026-10-28 17:16 ` [PULL 64/78] intel_iommu: " Michael S. Tsirkin
2026-10-28 17:16 ` [PULL 65/78] i386/kvm: Use x86_iommu_device_get() for per-device IOMMU lookup Michael S. Tsirkin
2026-10-28 17:16 ` [PULL 66/78] amd_iommu: Introduce root bus Michael S. Tsirkin
2026-10-28 17:16 ` [PULL 67/78] acpi-build: Create PCI topology using amd iommu " Michael S. Tsirkin
2026-10-28 17:16 ` [PULL 68/78] amd_iommu: Allow IOMMU to attachment on non-system (pcie.0) bus Michael S. Tsirkin
2026-10-28 17:17 ` [PULL 69/78] intel_iommu_accel: Guard VTDAccelPASIDCacheEntry definition Michael S. Tsirkin
2026-10-28 17:17 ` [PULL 70/78] backends/iommufd: Introduce iommufd_backend_alloc_faultq Michael S. Tsirkin
2026-10-28 17:17 ` [PULL 71/78] backends/iommufd: Extend iommufd_backend_alloc_hwpt() with fault_id Michael S. Tsirkin
2026-10-28 17:17 ` [PULL 72/78] intel_iommu_accel: Add PRQ injection for passthrough device Michael S. Tsirkin
2026-10-28 17:17 ` [PULL 73/78] intel_iommu_accel: Accept PRQ response " Michael S. Tsirkin
2026-10-28 17:17 ` [PULL 74/78] intel_iommu_accel: Teardown FAULTQ resources in bottom half Michael S. Tsirkin
2026-10-28 17:17 ` [PULL 75/78] hw/virtio/virtio-mem: Fix error message on invalid NUMA node Michael S. Tsirkin
2026-10-28 17:17 ` [PULL 76/78] net/tap: add tap_fd_get_mtu() to query host tap MTU Michael S. Tsirkin
2026-10-28 17:17 ` [PULL 77/78] net: add generic get_mtu NetClientInfo callback Michael S. Tsirkin
2026-10-28 17:17 ` [PULL 78/78] virtio-net: add mtu-from-tap property for automatic MTU detection Michael S. Tsirkin
2026-10-29 14:45 ` [PULL 00/78] pci, vhost, virtio, iommu, acpi: features, fixes Richard Henderson
2026-10-29 18:31 ` Michael S. Tsirkin
2026-10-29 22:07 ` Richard Henderson
2026-10-29 22:44 ` Daniel Paziyski
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.