Merge tag 'for_upstream' of https://git.kernel.org/pub/scm/virt/kvm/mst/qemu into staging pci, vhost, virtio, iommu, apci: features, fixes new vhost-user-media device virtio-net now supports live-TAP local migration cxl now supports CFMW restrictions and per-window options intel_iommu now supports concurrent page fault handling with PRI pci now batches memory transactions around mapping updates vhost-user-blk now allows toggling inflight-migration at runtime virtio-rtc now supports smeared UTC clock type intel_iommu now exposes SMPWC when SVM is enabled new reviewer for CXL patches Fixes, cleanups all over the place Signed-off-by: Michael S. Tsirkin <mst@redhat.com> # -----BEGIN PGP SIGNATURE----- # # iQFDBAABCgAtFiEEXQn9CHHI+FuUyooNKB8NuNKNVGkFAmqkXRUPHG1zdEByZWRo # YXQuY29tAAoJECgfDbjSjVRpoWsH/jeMRFjgYu5Es05ha87JW6NJW6j/Ggw3F8e5 # kup/SsoeqX+pK1VqXkKimWRf0iXsnweTqviVR88KMmMBP9UNdwgRBM7XflplYiOv # UOsjZGZAKjOKnyjbQyNihYY4CfaJn2VYkxJBRxweS3/OEK4JV2wmFcWEjFuZEdJT # SXpYXAPu0e8XTPVk/+bhi6kIYHhsuu1sWhcsoRDGdw5ZrGvwTwF8cS4SWaYHrWc1 # wjvC39P3Hg+Zvw6vDkPP2Amos2s6iheEgsPI0628YYvER+YV641YD48xEXqTG44e # FGwCihJ97rYYGQEwL+1FL4ZLKcp7HwacqxkJYg+GHbKj1Iog9OQ= # =9H6A # -----END PGP SIGNATURE----- # gpg: Signature made Fri 11 Sep 2026 09:57:09 AM HST # gpg: using RSA key 5D09FD0871C8F85B94CA8A0D281F0DB8D28D5469 # gpg: issuer "mst@redhat.com" # gpg: Good signature from "Michael S. Tsirkin <mst@kernel.org>" [unknown] # gpg: aka "Michael S. Tsirkin <mst@redhat.com>" [unknown] # gpg: WARNING: The key's User ID is not certified with a trusted signature! # gpg: There is no indication that the signature belongs to the owner. # Primary key fingerprint: 0270 606B 6F3C DF3D 0B17 0970 C350 3912 AFBE 8E67 # Subkey fingerprint: 5D09 FD08 71C8 F85B 94CA 8A0D 281F 0DB8 D28D 5469 * tag 'for_upstream' of https://git.kernel.org/pub/scm/virt/kvm/mst/qemu: (74 commits) MAINTAINERS: add Junjie Cao as CXL reviewer vhost-user-blk: use GET_VRING_BASE_SKIP_DRAIN when inflight-migration is on vhost-user-blk: move inflight_needed higher vhost-user-blk: make inflight-migration prop mutable on running vm vhost-user: use skip_drain with GET_VRING_BASE_SKIP_DRAIN message vhost-user: add GET_VRING_BASE_SKIP_DRAIN message vhost-user: add skip_drain param to do_vhost_virtqueue_stop intel_iommu: Expose SMPWC when SVM is enabled virtio-net: validate IHL in virtio_net_rsc_extract_unit4 before use net/tap-solaris: Fix resource leaks on error paths hw/hotplug: Constify HotplugHandler hw/hotplug: Reduce some HotplugHandler variables scope hw/hotplug: Mark various HotplugHandlerClass variables as const hw/acpi: Make AcpiGedState const in build_ged_aml() pci: load a device's config inside one memory transaction pci: batch BAR remapping into one memory transaction intel_iommu: Always write all the flags passed to vtd_set_flag_in_pte intel_iommu: Only set dirty bit when PTE exposes write permission intel_iommu: Support concurrent page fault handling with PRI virtio-rtc: Report smeared UTC clock type ... Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
diff --git a/MAINTAINERS b/MAINTAINERS index e9b68c5..5bf8957 100644 --- a/MAINTAINERS +++ b/MAINTAINERS
@@ -2689,6 +2689,12 @@ F: include/hw/virtio/vhost-user-rtc.h F: hw/virtio/vhost-user-rtc* +vhost-user-media +M: Albert Esteve <aesteve@redhat.com> +S: Supported +F: hw/display/vhost-user-media* +F: include/hw/virtio/vhost-user-media.h + virtio-crypto M: Gonglei <arei.gonglei@huawei.com> S: Supported @@ -3327,6 +3333,7 @@ Compute Express Link M: Jonathan Cameron <jic23@kernel.org> +R: Junjie Cao <junjie.cao@intel.com> L: linux-cxl@vger.kernel.org S: Supported F: hw/cxl/
diff --git a/backends/cryptodev-vhost.c b/backends/cryptodev-vhost.c index c6069f4..f1ca6bc 100644 --- a/backends/cryptodev-vhost.c +++ b/backends/cryptodev-vhost.c
@@ -109,7 +109,7 @@ cryptodev_vhost_stop_one(CryptoDevBackendVhost *crypto, VirtIODevice *dev) { - vhost_dev_stop(&crypto->dev, dev, false); + vhost_dev_stop(&crypto->dev, dev, false, false); vhost_dev_disable_notifiers(&crypto->dev, dev); }
diff --git a/backends/vhost-user.c b/backends/vhost-user.c index 46dadb7..de6b1ba 100644 --- a/backends/vhost-user.c +++ b/backends/vhost-user.c
@@ -108,7 +108,7 @@ return 0; } - ret = vhost_dev_stop(&b->dev, b->vdev, true); + ret = vhost_dev_stop(&b->dev, b->vdev, true, false); if (k->set_guest_notifiers && (err = k->set_guest_notifiers(qbus->parent, b->dev.nvqs, false)) < 0) {
diff --git a/docs/about/deprecated.rst b/docs/about/deprecated.rst index f20f097..ccbc421 100644 --- a/docs/about/deprecated.rst +++ b/docs/about/deprecated.rst
@@ -71,6 +71,15 @@ ``-mon mode=control`` is replaced by ``-object monitor-qmp``. The short convenience options are not deprecated, only ``-mon``. +``script=no`` and ``downscript=no`` for ``-netdev tap`` (since 11.2) +''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' + +The special value ``"no"`` for the ``script`` and ``downscript`` +parameters of ``-netdev tap`` disables script execution. This special +treatment of ``"no"`` is deprecated. Use an empty string (``script=`` +or ``downscript=``) to disable script execution instead. In a future +version, ``"no"`` will be treated as a plain file name. + QEMU Machine Protocol (QMP) commands ------------------------------------ @@ -164,6 +173,15 @@ Use ``query-accelerators`` instead. +``"no"`` as value of ``script``/``downscript`` for tap in ``netdev_add`` (since 11.2) +''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' + +The special value ``"no"`` for the ``script`` and ``downscript`` +parameters of ``netdev_add`` with ``type=tap`` disables script +execution. This special treatment of ``"no"`` is deprecated. Use an +empty string instead. In a future version, ``"no"`` will be treated as +a plain file name. + Human Machine Protocol (HMP) commands -------------------------------------
diff --git a/docs/interop/vhost-user.rst b/docs/interop/vhost-user.rst index ae8c7ed..c1e02ff 100644 --- a/docs/interop/vhost-user.rst +++ b/docs/interop/vhost-user.rst
@@ -457,6 +457,7 @@ * ``VHOST_USER_GET_FEATURES`` * ``VHOST_USER_GET_PROTOCOL_FEATURES`` * ``VHOST_USER_GET_VRING_BASE`` +* ``VHOST_USER_GET_VRING_BASE_SKIP_DRAIN`` * ``VHOST_USER_SET_LOG_BASE`` (if ``VHOST_USER_PROTOCOL_F_LOG_SHMFD``) * ``VHOST_USER_GET_INFLIGHT_FD`` (if ``VHOST_USER_PROTOCOL_F_INFLIGHT_SHMFD``) @@ -533,7 +534,7 @@ Each ring is initialized in a stopped and disabled state. Rings are started with ``VHOST_USER_SET_VRING_KICK`` (or ``VHOST_USER_VRING_KICK`` if ``VHOST_USER_PROTOCOL_F_INBAND_NOTIFICATIONS`` is negotiated) and stopped with -``VHOST_USER_GET_VRING_BASE``. A stopped ring enters the started state again +``VHOST_USER_GET_VRING_BASE`` or ``VHOST_USER_GET_VRING_BASE_SKIP_DRAIN``. A stopped ring enters the started state again with ``VHOST_USER_SET_VRING_KICK`` (or ``VHOST_USER_VRING_KICK`` if ``VHOST_USER_PROTOCOL_F_INBAND_NOTIFICATIONS`` is negotiated) and the back-end resumes processing requests. @@ -1158,7 +1159,8 @@ #define VHOST_USER_PROTOCOL_F_DEVICE_STATE 19 #define VHOST_USER_PROTOCOL_F_GET_VRING_BASE_INFLIGHT 20 #define VHOST_USER_PROTOCOL_F_GPA_ADDRESSES 21 - #define VHOST_USER_PROTOCOL_F_SHMEM_MAP 22 + #define VHOST_USER_PROTOCOL_F_SHMEM 22 + #define VHOST_USER_PROTOCOL_F_GET_VRING_BASE_SKIP_DRAIN 23 Front-end message types ----------------------- @@ -1355,17 +1357,11 @@ set to 0. By default, the back-end must complete all inflight I/O requests for the - specified vring before stopping it. - - If the ``VHOST_USER_PROTOCOL_F_GET_VRING_BASE_INFLIGHT`` protocol - feature has been negotiated, the back-end may suspend in-flight I/O - requests and record them as described in :ref:`Inflight I/O tracking - <inflight_io_tracking>` instead of completing them before stopping the vring. - How to suspend an in-flight request depends on the implementation of the back-end - but it typically can be done by aborting or cancelling the underlying I/O - request. The ``VHOST_USER_PROTOCOL_F_GET_VRING_BASE_INFLIGHT`` - protocol feature must only be negotiated if - ``VHOST_USER_PROTOCOL_F_INFLIGHT_SHMFD`` is also negotiated. + specified vring before stopping it. If the + ``VHOST_USER_PROTOCOL_F_GET_VRING_BASE_SKIP_DRAIN`` protocol feature has + been negotiated, the front-end may instead use + ``VHOST_USER_GET_VRING_BASE_SKIP_DRAIN`` to request the back-end to + suspend in-flight I/O immediately. ``VHOST_USER_SET_VRING_KICK`` :id: 12 @@ -1868,6 +1864,28 @@ * The size may be 0 if the region is unused. +``VHOST_USER_GET_VRING_BASE_SKIP_DRAIN`` + :id: 45 + :equivalent ioctl: N/A + :request payload: vring state description + :reply payload: vring descriptor index/indices + + This message requires the ``VHOST_USER_PROTOCOL_F_GET_VRING_BASE_SKIP_DRAIN`` + protocol feature to be negotiated. + + Identical to ``VHOST_USER_GET_VRING_BASE`` except that the back-end + must not wait for inflight I/O requests to complete before stopping + the vring. Instead, the back-end must immediately suspend all + in-flight I/O requests and record them as described in + :ref:`Inflight I/O tracking <inflight_io_tracking>`. How to suspend + an in-flight request depends on the implementation of the back-end, + but it typically can be done by aborting or cancelling the underlying + I/O request. + + The ``VHOST_USER_PROTOCOL_F_GET_VRING_BASE_SKIP_DRAIN`` protocol feature + must only be negotiated if both ``VHOST_USER_PROTOCOL_F_GET_VRING_BASE_INFLIGHT`` + and ``VHOST_USER_PROTOCOL_F_INFLIGHT_SHMFD`` are also negotiated. + Back-end message types ----------------------
diff --git a/docs/system/devices/cxl.rst b/docs/system/devices/cxl.rst index 9d0771c..6bee339 100644 --- a/docs/system/devices/cxl.rst +++ b/docs/system/devices/cxl.rst
@@ -384,8 +384,13 @@ -device cxl-type3,bus=swport3,persistent-memdev=cxl-mem3,lsa=cxl-lsa3,id=cxl-pmem3,sn=0x4 \ -M cxl-fmw.0.targets.0=cxl.1,cxl-fmw.0.size=4G,cxl-fmw.0.interleave-granularity=4k -An example of 4 type3 devices with volatile memory below a switch. Two of the devices -use HDM-DB for coherence, which requires operating in Flit mode:: +An example of a mixed configuration across two host bridges, each with its +own CXL window. Below cxl.1, two type3 volatile devices behind a switch use +HDM-DB for coherence, which requires operating in Flit mode and a window +that permits the device-coherent Back-Invalidate model. Below cxl.2, two +plain host-only devices are directly attached and use the default window. +Because a window targets a host bridge, giving each coherency model its own +bridge keeps the window-to-device mapping explicit:: qemu-system-x86_64 -M q35,cxl=on -m 4G,maxmem=8G,slots=8 -smp 4 \ ... @@ -395,17 +400,17 @@ -object memory-backend-ram,id=cxl-mem3,share=on,size=256M \ -device pxb-cxl,bus_nr=12,bus=pcie.0,id=cxl.1 \ -device cxl-rp,port=0,bus=cxl.1,id=root_port0,chassis=0,slot=0 \ - -device cxl-rp,port=1,bus=cxl.1,id=root_port1,chassis=0,slot=1 \ -device cxl-upstream,bus=root_port0,id=us0,x-256b-flit=on \ -device cxl-downstream,port=0,bus=us0,id=swport0,chassis=0,slot=4 \ - -device cxl-type3,bus=swport0,volatile-memdev=cxl-mem0,id=cxl-mem0,sn=0x1,x-256b-flit=on,hdm-db=on \ + -device cxl-type3,bus=swport0,volatile-memdev=cxl-mem0,id=cxl-vmem0,sn=0x1,x-256b-flit=on,hdm-db=on \ -device cxl-downstream,port=1,bus=us0,id=swport1,chassis=0,slot=5 \ - -device cxl-type3,bus=swport1,volatile-memdev=cxl-mem1,id=cxl-mem1,sn=0x2,x-256b-flit=on,hdm-db=on \ - -device cxl-downstream,port=2,bus=us0,id=swport2,chassis=0,slot=6 \ - -device cxl-type3,bus=swport2,volatile-memdev=cxl-mem2,id=cxl-mem2,sn=0x3 \ - -device cxl-downstream,port=3,bus=us0,id=swport3,chassis=0,slot=7 \ - -device cxl-type3,bus=swport3,volatile-memdev=cxl-mem3,id=cxl-mem3,sn=0x4 \ - -M cxl-fmw.0.targets.0=cxl.1,cxl-fmw.0.size=4G,cxl-fmw.0.interleave-granularity=4k + -device cxl-type3,bus=swport1,volatile-memdev=cxl-mem1,id=cxl-vmem1,sn=0x2,x-256b-flit=on,hdm-db=on \ + -device pxb-cxl,bus_nr=222,bus=pcie.0,id=cxl.2 \ + -device cxl-rp,port=0,bus=cxl.2,id=root_port1,chassis=0,slot=6 \ + -device cxl-type3,bus=root_port1,volatile-memdev=cxl-mem2,id=cxl-vmem2,sn=0x3 \ + -device cxl-rp,port=1,bus=cxl.2,id=root_port2,chassis=0,slot=7 \ + -device cxl-type3,bus=root_port2,volatile-memdev=cxl-mem3,id=cxl-vmem3,sn=0x4 \ + -M cxl-fmw.0.targets.0=cxl.1,cxl-fmw.0.size=4G,cxl-fmw.0.interleave-granularity=4k,cxl-fmw.0.back-invalidate=on,cxl-fmw.1.targets.0=cxl.2,cxl-fmw.1.size=4G,cxl-fmw.1.interleave-granularity=4k A simple arm/virt example featuring a single direct connected CXL Type 3 Volatile Memory device::
diff --git a/docs/system/i386/microvm.rst b/docs/system/i386/microvm.rst index 1675e37..077ea15 100644 --- a/docs/system/i386/microvm.rst +++ b/docs/system/i386/microvm.rst
@@ -79,7 +79,7 @@ -serial stdio \ -drive id=test,file=test.img,format=raw,if=none \ -device virtio-blk-device,drive=test \ - -netdev tap,id=tap0,script=no,downscript=no \ + -netdev tap,id=tap0,script=,downscript= \ -device virtio-net-device,netdev=tap0 While the example above works, you might be interested in reducing the @@ -103,7 +103,7 @@ -device virtconsole,chardev=virtiocon0 \ -drive id=test,file=test.img,format=raw,if=none \ -device virtio-blk-device,drive=test \ - -netdev tap,id=tap0,script=no,downscript=no \ + -netdev tap,id=tap0,script=,downscript= \ -device virtio-net-device,netdev=tap0
diff --git a/docs/system/i386/xenpvh.rst b/docs/system/i386/xenpvh.rst index 904778e..862f388 100644 --- a/docs/system/i386/xenpvh.rst +++ b/docs/system/i386/xenpvh.rst
@@ -42,7 +42,7 @@ -vnc none \ -display none \ -device virtio-net-pci,id=nic0,netdev=net0,mac=00:16:3e:5c:81:78 \ - -netdev type=tap,id=net0,ifname=vif3.0-emu,br=xenbr0,script=no,downscript=no \ + -netdev type=tap,id=net0,ifname=vif3.0-emu,br=xenbr0,script=,downscript= \ -smp 4,maxcpus=4 \ -nographic \ -machine xenpvh,ram-low-base=0,ram-low-size=2147483648,ram-high-base=4294967296,ram-high-size=2147483648,pci-ecam-base=824633720832,pci-ecam-size=268435456,pci-mmio-base=4026531840,pci-mmio-size=33554432,pci-mmio-high-base=824902156288,pci-mmio-high-size=68719476736 \
diff --git a/docs/system/ppc/ppce500.rst b/docs/system/ppc/ppce500.rst index c9fe091..ec5aaf1 100644 --- a/docs/system/ppc/ppce500.rst +++ b/docs/system/ppc/ppce500.rst
@@ -158,14 +158,14 @@ $ qemu-system-ppc64 -M ppce500 -smp 4 -m 2G \ -display none -serial stdio \ -bios u-boot \ - -nic tap,ifname=tap0,script=no,downscript=no,model=e1000 + -nic tap,ifname=tap0,script=,downscript=,model=e1000 The QEMU ``ppce500`` machine can also dynamically instantiate an eTSEC device if “-device eTSEC” is given to QEMU: .. code-block:: bash - -netdev tap,ifname=tap0,script=no,downscript=no,id=net0 -device eTSEC,netdev=net0 + -netdev tap,ifname=tap0,script=,downscript=,id=net0 -device eTSEC,netdev=net0 Root file system on flash drive -------------------------------
diff --git a/docs/system/riscv/microchip-icicle-kit.rst b/docs/system/riscv/microchip-icicle-kit.rst index 9809e94..7fdb966 100644 --- a/docs/system/riscv/microchip-icicle-kit.rst +++ b/docs/system/riscv/microchip-icicle-kit.rst
@@ -84,7 +84,7 @@ $ qemu-system-riscv64 -M microchip-icicle-kit -smp 5 -m 2G \ -sd path/to/sdcard.img \ -nic user,model=cadence_gem \ - -nic tap,ifname=tap,model=cadence_gem,script=no \ + -nic tap,ifname=tap,model=cadence_gem,script= \ -display none -serial stdio \ -kernel path/to/u-boot/build/dir/u-boot.bin \ -dtb path/to/u-boot/build/dir/u-boot.dtb
diff --git a/docs/system/riscv/sifive_u.rst b/docs/system/riscv/sifive_u.rst index 8f55ae8..0e4dcf3 100644 --- a/docs/system/riscv/sifive_u.rst +++ b/docs/system/riscv/sifive_u.rst
@@ -199,7 +199,7 @@ $ qemu-system-riscv64 -M sifive_u -smp 5 -m 2G \ -display none -serial stdio \ - -nic tap,ifname=tap0,script=no,downscript=no \ + -nic tap,ifname=tap0,script=,downscript= \ -kernel /path/to/vxWorks \ -append "gem(0,0)host:vxWorks h=192.168.200.1 e=192.168.200.2:ffffff00 u=target pw=vxTarget f=0x01"
diff --git a/hw/acpi/acpi-cpu-hotplug-stub.c b/hw/acpi/acpi-cpu-hotplug-stub.c index 72c5f05..2e05d71 100644 --- a/hw/acpi/acpi-cpu-hotplug-stub.c +++ b/hw/acpi/acpi-cpu-hotplug-stub.c
@@ -14,7 +14,7 @@ { } -void acpi_cpu_plug_cb(HotplugHandler *hotplug_dev, +void acpi_cpu_plug_cb(const HotplugHandler *hotplug_dev, CPUHotplugState *cpu_st, DeviceState *dev, Error **errp) { } @@ -24,7 +24,7 @@ { } -void acpi_cpu_unplug_request_cb(HotplugHandler *hotplug_dev, +void acpi_cpu_unplug_request_cb(const HotplugHandler *hotplug_dev, CPUHotplugState *cpu_st, DeviceState *dev, Error **errp) {
diff --git a/hw/acpi/acpi-mem-hotplug-stub.c b/hw/acpi/acpi-mem-hotplug-stub.c index 7ad0fdc..c218813 100644 --- a/hw/acpi/acpi-mem-hotplug-stub.c +++ b/hw/acpi/acpi-mem-hotplug-stub.c
@@ -13,7 +13,7 @@ { } -void acpi_memory_plug_cb(HotplugHandler *hotplug_dev, MemHotplugState *mem_st, +void acpi_memory_plug_cb(const HotplugHandler *hotplug_dev, MemHotplugState *mem_st, DeviceState *dev, Error **errp) { } @@ -23,7 +23,7 @@ { } -void acpi_memory_unplug_request_cb(HotplugHandler *hotplug_dev, +void acpi_memory_unplug_request_cb(const HotplugHandler *hotplug_dev, MemHotplugState *mem_st, DeviceState *dev, Error **errp) {
diff --git a/hw/acpi/acpi-nvdimm-stub.c b/hw/acpi/acpi-nvdimm-stub.c index 22ba17f..0120bac 100644 --- a/hw/acpi/acpi-nvdimm-stub.c +++ b/hw/acpi/acpi-nvdimm-stub.c
@@ -2,6 +2,6 @@ #include "hw/mem/nvdimm.h" #include "hw/core/hotplug.h" -void nvdimm_acpi_plug_cb(HotplugHandler *hotplug_dev, DeviceState *dev) +void nvdimm_acpi_plug_cb(const HotplugHandler *hotplug_dev, DeviceState *dev) { }
diff --git a/hw/acpi/acpi-pci-hotplug-stub.c b/hw/acpi/acpi-pci-hotplug-stub.c index d58ea72..20625c7 100644 --- a/hw/acpi/acpi-pci-hotplug-stub.c +++ b/hw/acpi/acpi-pci-hotplug-stub.c
@@ -9,22 +9,22 @@ { } -void acpi_pcihp_device_plug_cb(HotplugHandler *hotplug_dev, AcpiPciHpState *s, +void acpi_pcihp_device_plug_cb(const HotplugHandler *hotplug_dev, AcpiPciHpState *s, DeviceState *dev, Error **errp) { } -void acpi_pcihp_device_pre_plug_cb(HotplugHandler *hotplug_dev, +void acpi_pcihp_device_pre_plug_cb(const HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { } -void acpi_pcihp_device_unplug_cb(HotplugHandler *hotplug_dev, AcpiPciHpState *s, +void acpi_pcihp_device_unplug_cb(const HotplugHandler *hotplug_dev, AcpiPciHpState *s, DeviceState *dev, Error **errp) { } -void acpi_pcihp_device_unplug_request_cb(HotplugHandler *hotplug_dev, +void acpi_pcihp_device_unplug_request_cb(const HotplugHandler *hotplug_dev, AcpiPciHpState *s, DeviceState *dev, Error **errp) {
diff --git a/hw/acpi/cpu.c b/hw/acpi/cpu.c index d63ca83..ef97ed7 100644 --- a/hw/acpi/cpu.c +++ b/hw/acpi/cpu.c
@@ -132,7 +132,7 @@ trace_cpuhp_acpi_clear_remove_evt(cpu_st->selector); } else if (data & 8) { DeviceState *dev = NULL; - HotplugHandler *hotplug_ctrl = NULL; + const HotplugHandler *hotplug_ctrl; if (!cdev->cpu || cdev->cpu == first_cpu) { trace_cpuhp_acpi_ejecting_invalid_cpu(cpu_st->selector); @@ -247,7 +247,7 @@ return NULL; } -void acpi_cpu_plug_cb(HotplugHandler *hotplug_dev, +void acpi_cpu_plug_cb(const HotplugHandler *hotplug_dev, CPUHotplugState *cpu_st, DeviceState *dev, Error **errp) { AcpiCpuStatus *cdev; @@ -264,7 +264,7 @@ } } -void acpi_cpu_unplug_request_cb(HotplugHandler *hotplug_dev, +void acpi_cpu_unplug_request_cb(const HotplugHandler *hotplug_dev, CPUHotplugState *cpu_st, DeviceState *dev, Error **errp) {
diff --git a/hw/acpi/cxl.c b/hw/acpi/cxl.c index f92f7fa..77c1db6 100644 --- a/hw/acpi/cxl.c +++ b/hw/acpi/cxl.c
@@ -172,7 +172,7 @@ build_append_int_noprefix(table_data, fw->enc_int_gran, 4); /* Window Restrictions */ - build_append_int_noprefix(table_data, 0x2f, 2); + build_append_int_noprefix(table_data, fw->restrictions, 2); /* QTG ID */ build_append_int_noprefix(table_data, 0, 2);
diff --git a/hw/acpi/generic_event_device.c b/hw/acpi/generic_event_device.c index 67c9e9b..65a5d16 100644 --- a/hw/acpi/generic_event_device.c +++ b/hw/acpi/generic_event_device.c
@@ -45,10 +45,11 @@ * affected by the interrupt. This way, we can support up to 32 events * with a unique interrupt. */ -void build_ged_aml(Aml *table, const char *name, HotplugHandler *hotplug_dev, +void build_ged_aml(Aml *table, const char *name, + const HotplugHandler *hotplug_dev, uint32_t ged_irq, AmlRegionSpace rs, hwaddr ged_base) { - AcpiGedState *s = ACPI_GED(hotplug_dev); + const AcpiGedState *s = ACPI_GED(hotplug_dev); Aml *crs = aml_resource_template(); Aml *evt, *field; Aml *dev = aml_device("%s", name); @@ -250,7 +251,7 @@ }, }; -static void acpi_ged_device_pre_plug_cb(HotplugHandler *hotplug_dev, +static void acpi_ged_device_pre_plug_cb(const HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { if (object_dynamic_cast(OBJECT(dev), TYPE_PCI_DEVICE)) { @@ -258,7 +259,7 @@ } } -static void acpi_ged_device_plug_cb(HotplugHandler *hotplug_dev, +static void acpi_ged_device_plug_cb(const HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { AcpiGedState *s = ACPI_GED(hotplug_dev); @@ -279,7 +280,7 @@ } } -static void acpi_ged_unplug_request_cb(HotplugHandler *hotplug_dev, +static void acpi_ged_unplug_request_cb(const HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { AcpiGedState *s = ACPI_GED(hotplug_dev); @@ -298,7 +299,7 @@ } } -static void acpi_ged_unplug_cb(HotplugHandler *hotplug_dev, +static void acpi_ged_unplug_cb(const HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { AcpiGedState *s = ACPI_GED(hotplug_dev);
diff --git a/hw/acpi/ich9.c b/hw/acpi/ich9.c index 8082eae..6d421b9 100644 --- a/hw/acpi/ich9.c +++ b/hw/acpi/ich9.c
@@ -410,7 +410,7 @@ #undef PM_REG_FIELD -void ich9_pm_device_pre_plug_cb(HotplugHandler *hotplug_dev, DeviceState *dev, +void ich9_pm_device_pre_plug_cb(const HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { ICH9LPCState *lpc = ICH9_LPC_DEVICE(hotplug_dev); @@ -432,7 +432,7 @@ } } -void ich9_pm_device_plug_cb(HotplugHandler *hotplug_dev, DeviceState *dev, +void ich9_pm_device_plug_cb(const HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { ICH9LPCState *lpc = ICH9_LPC_DEVICE(hotplug_dev); @@ -455,7 +455,7 @@ } } -void ich9_pm_device_unplug_request_cb(HotplugHandler *hotplug_dev, +void ich9_pm_device_unplug_request_cb(const HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { ICH9LPCState *lpc = ICH9_LPC_DEVICE(hotplug_dev); @@ -489,7 +489,7 @@ } } -void ich9_pm_device_unplug_cb(HotplugHandler *hotplug_dev, DeviceState *dev, +void ich9_pm_device_unplug_cb(const HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { ICH9LPCState *lpc = ICH9_LPC_DEVICE(hotplug_dev); @@ -507,7 +507,7 @@ } } -bool ich9_pm_is_hotpluggable_bus(HotplugHandler *hotplug_dev, BusState *bus) +bool ich9_pm_is_hotpluggable_bus(const HotplugHandler *hotplug_dev, BusState *bus) { ICH9LPCState *lpc = ICH9_LPC_DEVICE(hotplug_dev); return acpi_pcihp_is_hotpluggable_bus(&lpc->pm.acpi_pci_hotplug, bus);
diff --git a/hw/acpi/memory_hotplug.c b/hw/acpi/memory_hotplug.c index 1ddfdd1..b5b6d6a 100644 --- a/hw/acpi/memory_hotplug.c +++ b/hw/acpi/memory_hotplug.c
@@ -119,7 +119,6 @@ MemStatus *mdev; ACPIOSTInfo *info; DeviceState *dev = NULL; - HotplugHandler *hotplug_ctrl = NULL; Error *local_err = NULL; if (!mem_st->dev_count) { @@ -167,6 +166,8 @@ mdev->is_removing = false; trace_mhp_acpi_clear_remove_evt(mem_st->selector); } else if (data & 8) { + const HotplugHandler *hotplug_ctrl; + if (!mdev->is_enabled) { trace_mhp_acpi_ejecting_invalid_slot(mem_st->selector); break; @@ -253,7 +254,7 @@ return &mem_st->devs[slot]; } -void acpi_memory_plug_cb(HotplugHandler *hotplug_dev, MemHotplugState *mem_st, +void acpi_memory_plug_cb(const HotplugHandler *hotplug_dev, MemHotplugState *mem_st, DeviceState *dev, Error **errp) { MemStatus *mdev; @@ -276,7 +277,7 @@ } } -void acpi_memory_unplug_request_cb(HotplugHandler *hotplug_dev, +void acpi_memory_unplug_request_cb(const HotplugHandler *hotplug_dev, MemHotplugState *mem_st, DeviceState *dev, Error **errp) {
diff --git a/hw/acpi/nvdimm.c b/hw/acpi/nvdimm.c index 703e854..5a7ff1e 100644 --- a/hw/acpi/nvdimm.c +++ b/hw/acpi/nvdimm.c
@@ -887,7 +887,7 @@ }, }; -void nvdimm_acpi_plug_cb(HotplugHandler *hotplug_dev, DeviceState *dev) +void nvdimm_acpi_plug_cb(const HotplugHandler *hotplug_dev, DeviceState *dev) { if (dev->hotplugged) { acpi_send_event(DEVICE(hotplug_dev), ACPI_NVDIMM_HOTPLUG_STATUS);
diff --git a/hw/acpi/pcihp.c b/hw/acpi/pcihp.c index b26ddf0..b69379a 100644 --- a/hw/acpi/pcihp.c +++ b/hw/acpi/pcihp.c
@@ -173,7 +173,6 @@ static void acpi_pcihp_eject_slot(AcpiPciHpState *s, unsigned bsel, unsigned slots) { - HotplugHandler *hotplug_ctrl; BusChild *kid, *next; int slot = ctz32(slots); PCIBus *bus = acpi_pcihp_find_hotplug_bus(s, bsel); @@ -210,6 +209,8 @@ */ qdev->pending_deleted_event = false; } else { + const HotplugHandler *hotplug_ctrl; + hotplug_ctrl = qdev_get_hotplug_handler(qdev); hotplug_handler_unplug(hotplug_ctrl, qdev, &error_abort); object_unparent(OBJECT(qdev)); @@ -260,7 +261,7 @@ acpi_pcihp_update(s); } -void acpi_pcihp_device_pre_plug_cb(HotplugHandler *hotplug_dev, +void acpi_pcihp_device_pre_plug_cb(const HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { PCIDevice *pdev = PCI_DEVICE(dev); @@ -274,7 +275,7 @@ } } -void acpi_pcihp_device_plug_cb(HotplugHandler *hotplug_dev, AcpiPciHpState *s, +void acpi_pcihp_device_plug_cb(const HotplugHandler *hotplug_dev, AcpiPciHpState *s, DeviceState *dev, Error **errp) { PCIDevice *pdev = PCI_DEVICE(dev); @@ -316,7 +317,7 @@ acpi_send_event(DEVICE(hotplug_dev), ACPI_PCI_HOTPLUG_STATUS); } -void acpi_pcihp_device_unplug_cb(HotplugHandler *hotplug_dev, AcpiPciHpState *s, +void acpi_pcihp_device_unplug_cb(const HotplugHandler *hotplug_dev, AcpiPciHpState *s, DeviceState *dev, Error **errp) { PCIDevice *pdev = PCI_DEVICE(dev); @@ -327,7 +328,7 @@ qdev_unrealize(dev); } -void acpi_pcihp_device_unplug_request_cb(HotplugHandler *hotplug_dev, +void acpi_pcihp_device_unplug_request_cb(const HotplugHandler *hotplug_dev, AcpiPciHpState *s, DeviceState *dev, Error **errp) {
diff --git a/hw/acpi/piix4.c b/hw/acpi/piix4.c index 52bd616..a7d5356 100644 --- a/hw/acpi/piix4.c +++ b/hw/acpi/piix4.c
@@ -303,8 +303,8 @@ acpi_pm1_evt_power_down(&s->ar); } -static void piix4_device_pre_plug_cb(HotplugHandler *hotplug_dev, - DeviceState *dev, Error **errp) +static void piix4_device_pre_plug_cb(const HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) { PIIX4PMState *s = PIIX4_PM(hotplug_dev); @@ -323,7 +323,7 @@ } } -static void piix4_device_plug_cb(HotplugHandler *hotplug_dev, +static void piix4_device_plug_cb(const HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { PIIX4PMState *s = PIIX4_PM(hotplug_dev); @@ -344,7 +344,7 @@ } } -static void piix4_device_unplug_request_cb(HotplugHandler *hotplug_dev, +static void piix4_device_unplug_request_cb(const HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { PIIX4PMState *s = PIIX4_PM(hotplug_dev); @@ -364,7 +364,7 @@ } } -static void piix4_device_unplug_cb(HotplugHandler *hotplug_dev, +static void piix4_device_unplug_cb(const HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { PIIX4PMState *s = PIIX4_PM(hotplug_dev); @@ -383,7 +383,7 @@ } } -static bool piix4_is_hotpluggable_bus(HotplugHandler *hotplug_dev, +static bool piix4_is_hotpluggable_bus(const HotplugHandler *hotplug_dev, BusState *bus) { PIIX4PMState *s = PIIX4_PM(hotplug_dev);
diff --git a/hw/arm/virt.c b/hw/arm/virt.c index 0871a35..3eecf09 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c
@@ -3753,7 +3753,7 @@ return ms->possible_cpus; } -static void virt_memory_pre_plug(HotplugHandler *hotplug_dev, DeviceState *dev, +static void virt_memory_pre_plug(const HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { VirtMachineState *vms = VIRT_MACHINE(hotplug_dev); @@ -3779,7 +3779,7 @@ pc_dimm_pre_plug(PC_DIMM(dev), MACHINE(hotplug_dev), errp); } -static void virt_memory_plug(HotplugHandler *hotplug_dev, +static void virt_memory_plug(const HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { VirtMachineState *vms = VIRT_MACHINE(hotplug_dev); @@ -3798,7 +3798,7 @@ } } -static void virt_machine_device_pre_plug_cb(HotplugHandler *hotplug_dev, +static void virt_machine_device_pre_plug_cb(const HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { VirtMachineState *vms = VIRT_MACHINE(hotplug_dev); @@ -3896,7 +3896,7 @@ } } -static void virt_machine_device_plug_cb(HotplugHandler *hotplug_dev, +static void virt_machine_device_plug_cb(const HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { VirtMachineState *vms = VIRT_MACHINE(hotplug_dev); @@ -3957,7 +3957,7 @@ } } -static void virt_dimm_unplug_request(HotplugHandler *hotplug_dev, +static void virt_dimm_unplug_request(const HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { VirtMachineState *vms = VIRT_MACHINE(hotplug_dev); @@ -3977,7 +3977,7 @@ errp); } -static void virt_dimm_unplug(HotplugHandler *hotplug_dev, +static void virt_dimm_unplug(const HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { VirtMachineState *vms = VIRT_MACHINE(hotplug_dev); @@ -3995,8 +3995,9 @@ error_propagate(errp, local_err); } -static void virt_machine_device_unplug_request_cb(HotplugHandler *hotplug_dev, - DeviceState *dev, Error **errp) +static void +virt_machine_device_unplug_request_cb(const HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) { if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) { virt_dimm_unplug_request(hotplug_dev, dev, errp); @@ -4009,7 +4010,7 @@ } } -static void virt_machine_device_unplug_cb(HotplugHandler *hotplug_dev, +static void virt_machine_device_unplug_cb(const HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) { @@ -4022,8 +4023,8 @@ } } -static HotplugHandler *virt_machine_get_hotplug_handler(MachineState *machine, - DeviceState *dev) +static const HotplugHandler * +virt_machine_get_hotplug_handler(MachineState *machine, DeviceState *dev) { MachineClass *mc = MACHINE_GET_CLASS(machine);
diff --git a/hw/block/vhost-user-blk.c b/hw/block/vhost-user-blk.c index 2e5b3ae..3dade14 100644 --- a/hw/block/vhost-user-blk.c +++ b/hw/block/vhost-user-blk.c
@@ -137,6 +137,14 @@ .vhost_dev_config_notifier = vhost_user_blk_handle_config_change, }; +static bool vhost_user_blk_inflight_needed(void *opaque) +{ + struct VHostUserBlk *s = opaque; + + return s->inflight_migration; +} + + static int vhost_user_blk_start(VirtIODevice *vdev, Error **errp) { VHostUserBlk *s = VHOST_USER_BLK(vdev); @@ -234,11 +242,13 @@ return 0; } + bool skip_drain = vhost_user_blk_inflight_needed(s); + force_stop = s->skip_get_vring_base_on_force_shutdown && qemu_force_shutdown_requested(); ret = force_stop ? vhost_dev_force_stop(&s->dev, vdev, true) : - vhost_dev_stop(&s->dev, vdev, true); + vhost_dev_stop(&s->dev, vdev, true, skip_drain); err = k->set_guest_notifiers(qbus->parent, s->dev.nvqs, false); if (err < 0) { @@ -370,7 +380,6 @@ vhost_dev_set_config_notifier(&s->dev, &blk_ops); s->vhost_user.supports_config = true; - s->vhost_user.supports_inflight_migration = s->inflight_migration; ret = vhost_dev_init(&s->dev, &s->vhost_user, VHOST_BACKEND_TYPE_USER, 0, errp); if (ret < 0) { @@ -597,18 +606,29 @@ return &s->dev; } -static bool vhost_user_blk_inflight_needed(void *opaque) +static bool vhost_user_blk_pre_save(void *opaque, Error **errp) { - struct VHostUserBlk *s = opaque; + VHostUserBlk *s = VHOST_USER_BLK(opaque); - return vhost_user_has_protocol_feature( - &s->dev, VHOST_USER_PROTOCOL_F_GET_VRING_BASE_INFLIGHT); + bool inflight_migration_enabled = vhost_user_has_protocol_feature(&s->dev, + VHOST_USER_PROTOCOL_F_GET_VRING_BASE_SKIP_DRAIN); + + if (vhost_user_blk_inflight_needed(s) && !inflight_migration_enabled) { + error_setg(errp, "can't migrate vhost-user-blk device: " + "backend doesn't support " + "VHOST_USER_PROTOCOL_F_GET_VRING_BASE_SKIP_DRAIN " + "protocol feature"); + return false; + } + + return true; } static const VMStateDescription vmstate_vhost_user_blk_inflight = { .name = "vhost-user-blk/inflight", .version_id = 1, .needed = vhost_user_blk_inflight_needed, + .pre_save_errp = vhost_user_blk_pre_save, .fields = (const VMStateField[]) { VMSTATE_VHOST_INFLIGHT_REGION(inflight, VHostUserBlk), VMSTATE_END_OF_LIST() @@ -629,6 +649,8 @@ } }; +static PropertyInfo vhost_user_blk_inflight_migration_prop; + static const Property vhost_user_blk_properties[] = { DEFINE_PROP_CHR("chardev", VHostUserBlk, chardev), DEFINE_PROP_UINT16("num-queues", VHostUserBlk, num_queues, @@ -644,8 +666,9 @@ VIRTIO_BLK_F_WRITE_ZEROES, true), DEFINE_PROP_BOOL("skip-get-vring-base-on-force-shutdown", VHostUserBlk, skip_get_vring_base_on_force_shutdown, false), - DEFINE_PROP_BOOL("inflight-migration", VHostUserBlk, - inflight_migration, false), + DEFINE_PROP("inflight-migration", VHostUserBlk, inflight_migration, + vhost_user_blk_inflight_migration_prop, bool, + .set_default = true, .defval.u = false), }; static void vhost_user_blk_class_init(ObjectClass *klass, const void *data) @@ -675,8 +698,29 @@ .class_init = vhost_user_blk_class_init, }; +static void vhost_user_blk_set_inflight_migration(Object *obj, Visitor *v, + const char *name, + void *opaque, Error **errp) +{ + DeviceState *dev = DEVICE(obj); + + if (dev->realized && !runstate_is_running()) { + error_setg(errp, "Property '%s' cannot be changed " + "while VM is not running", name); + return; + } + + qdev_prop_bool.set(obj, v, name, opaque, errp); +} + + static void virtio_register_types(void) { + vhost_user_blk_inflight_migration_prop = qdev_prop_bool; + vhost_user_blk_inflight_migration_prop.realized_set_allowed = true; + vhost_user_blk_inflight_migration_prop.set = + vhost_user_blk_set_inflight_migration; + type_register_static(&vhost_user_blk_info); }
diff --git a/hw/block/virtio-blk.c b/hw/block/virtio-blk.c index 6b92066..61c341c 100644 --- a/hw/block/virtio-blk.c +++ b/hw/block/virtio-blk.c
@@ -69,7 +69,7 @@ } static int virtio_blk_handle_rw_error(VirtIOBlockReq *req, int error, - bool is_read, bool acct_failed) + bool is_read) { VirtIOBlock *s = req->dev; BlockErrorAction action = blk_get_error_action(s->blk, is_read, error); @@ -85,9 +85,7 @@ } } else if (action == BLOCK_ERROR_ACTION_REPORT) { virtio_blk_req_complete(req, VIRTIO_BLK_S_IOERR); - if (acct_failed) { - block_acct_failed(blk_get_stats(s->blk), &req->acct); - } + block_acct_failed(blk_get_stats(s->blk), &req->acct); g_free(req); } @@ -124,7 +122,7 @@ * the memory until the request is completed (which will * happen on the other side of the migration). */ - if (virtio_blk_handle_rw_error(req, -ret, is_read, true)) { + if (virtio_blk_handle_rw_error(req, -ret, is_read)) { continue; } } @@ -140,7 +138,7 @@ VirtIOBlockReq *req = opaque; VirtIOBlock *s = req->dev; - if (ret && virtio_blk_handle_rw_error(req, -ret, 0, true)) { + if (ret && virtio_blk_handle_rw_error(req, -ret, 0)) { return; } @@ -153,17 +151,13 @@ { VirtIOBlockReq *req = opaque; VirtIOBlock *s = req->dev; - bool is_write_zeroes = (virtio_ldl_p(VIRTIO_DEVICE(s), &req->out.type) & - ~VIRTIO_BLK_T_BARRIER) == VIRTIO_BLK_T_WRITE_ZEROES; - if (ret && virtio_blk_handle_rw_error(req, -ret, false, is_write_zeroes)) { + if (ret && virtio_blk_handle_rw_error(req, -ret, false)) { return; } virtio_blk_req_complete(req, VIRTIO_BLK_S_OK); - if (is_write_zeroes) { - block_acct_done(blk_get_stats(s->blk), &req->acct); - } + block_acct_done(blk_get_stats(s->blk), &req->acct); g_free(req); } @@ -443,6 +437,9 @@ goto err; } + block_acct_start(blk_get_stats(s->blk), &req->acct, bytes, + BLOCK_ACCT_UNMAP); + blk_aio_pdiscard(s->blk, sector << BDRV_SECTOR_BITS, bytes, virtio_blk_discard_write_zeroes_complete, req); } @@ -450,9 +447,8 @@ return VIRTIO_BLK_S_OK; err: - if (is_write_zeroes) { - block_acct_invalid(blk_get_stats(s->blk), BLOCK_ACCT_WRITE); - } + block_acct_invalid(blk_get_stats(s->blk), + is_write_zeroes ? BLOCK_ACCT_WRITE : BLOCK_ACCT_UNMAP); return err_status; } @@ -759,7 +755,8 @@ { ZoneCmdData *data = opaque; VirtIOBlockReq *req = data->req; - VirtIODevice *vdev = VIRTIO_DEVICE(req->dev); + VirtIOBlock *s = req->dev; + VirtIODevice *vdev = VIRTIO_DEVICE(s); int64_t append_sector, n; uint8_t err_status = VIRTIO_BLK_S_OK; @@ -782,6 +779,11 @@ out: virtio_blk_req_complete(req, err_status); + if (err_status == VIRTIO_BLK_S_OK) { + block_acct_done(blk_get_stats(s->blk), &req->acct); + } else { + block_acct_failed(blk_get_stats(s->blk), &req->acct); + } g_free(req); g_free(data); } @@ -1384,6 +1386,10 @@ } req = qemu_get_virtqueue_element(vdev, f, sizeof(VirtIOBlockReq)); + if (!req) { + error_report("Failed to restore virtio-blk request"); + return -EINVAL; + } virtio_blk_init_request(s, virtio_get_queue(vdev, vq_idx), req); WITH_QEMU_LOCK_GUARD(&s->rq_lock) {
diff --git a/hw/char/virtio-serial-bus.c b/hw/char/virtio-serial-bus.c index 83a033c..c36e18a 100644 --- a/hw/char/virtio-serial-bus.c +++ b/hw/char/virtio-serial-bus.c
@@ -764,6 +764,10 @@ port->elem = qemu_get_virtqueue_element(vdev, f, sizeof(VirtQueueElement)); + if (!port->elem) { + error_report("Failed to restore virtio-serial element"); + return -EINVAL; + } /* * Port was throttled on source machine. Let's @@ -975,7 +979,7 @@ port->elem = NULL; } -static void virtser_port_device_plug(HotplugHandler *hotplug_dev, +static void virtser_port_device_plug(const HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { VirtIOSerialPort *port = VIRTIO_SERIAL_PORT(dev);
diff --git a/hw/core/hotplug.c b/hw/core/hotplug.c index 68aabad..3aca068 100644 --- a/hw/core/hotplug.c +++ b/hw/core/hotplug.c
@@ -13,44 +13,44 @@ #include "hw/core/hotplug.h" #include "qemu/module.h" -void hotplug_handler_pre_plug(HotplugHandler *plug_handler, +void hotplug_handler_pre_plug(const HotplugHandler *plug_handler, DeviceState *plugged_dev, Error **errp) { - HotplugHandlerClass *hdc = HOTPLUG_HANDLER_GET_CLASS(plug_handler); + const HotplugHandlerClass *hdc = HOTPLUG_HANDLER_GET_CLASS(plug_handler); if (hdc->pre_plug) { hdc->pre_plug(plug_handler, plugged_dev, errp); } } -void hotplug_handler_plug(HotplugHandler *plug_handler, +void hotplug_handler_plug(const HotplugHandler *plug_handler, DeviceState *plugged_dev, Error **errp) { - HotplugHandlerClass *hdc = HOTPLUG_HANDLER_GET_CLASS(plug_handler); + const HotplugHandlerClass *hdc = HOTPLUG_HANDLER_GET_CLASS(plug_handler); if (hdc->plug) { hdc->plug(plug_handler, plugged_dev, errp); } } -void hotplug_handler_unplug_request(HotplugHandler *plug_handler, +void hotplug_handler_unplug_request(const HotplugHandler *plug_handler, DeviceState *plugged_dev, Error **errp) { - HotplugHandlerClass *hdc = HOTPLUG_HANDLER_GET_CLASS(plug_handler); + const HotplugHandlerClass *hdc = HOTPLUG_HANDLER_GET_CLASS(plug_handler); if (hdc->unplug_request) { hdc->unplug_request(plug_handler, plugged_dev, errp); } } -void hotplug_handler_unplug(HotplugHandler *plug_handler, +void hotplug_handler_unplug(const HotplugHandler *plug_handler, DeviceState *plugged_dev, Error **errp) { - HotplugHandlerClass *hdc = HOTPLUG_HANDLER_GET_CLASS(plug_handler); + const HotplugHandlerClass *hdc = HOTPLUG_HANDLER_GET_CLASS(plug_handler); if (hdc->unplug) { hdc->unplug(plug_handler, plugged_dev, errp);
diff --git a/hw/core/qdev-hotplug.c b/hw/core/qdev-hotplug.c index 1d547e0..d7fa8a5 100644 --- a/hw/core/qdev-hotplug.c +++ b/hw/core/qdev-hotplug.c
@@ -14,7 +14,7 @@ #include "hw/core/boards.h" #include "qapi/error.h" -HotplugHandler *qdev_get_machine_hotplug_handler(DeviceState *dev) +const HotplugHandler *qdev_get_machine_hotplug_handler(DeviceState *dev) { MachineState *machine; MachineClass *mc; @@ -90,7 +90,7 @@ qdev_hotplug_unplug_allowed_common(dev, dev->parent_bus, errp); } -HotplugHandler *qdev_get_bus_hotplug_handler(DeviceState *dev) +const HotplugHandler *qdev_get_bus_hotplug_handler(DeviceState *dev) { if (dev->parent_bus) { return dev->parent_bus->hotplug_handler; @@ -98,9 +98,9 @@ return NULL; } -HotplugHandler *qdev_get_hotplug_handler(DeviceState *dev) +const HotplugHandler *qdev_get_hotplug_handler(DeviceState *dev) { - HotplugHandler *hotplug_ctrl = qdev_get_machine_hotplug_handler(dev); + const HotplugHandler *hotplug_ctrl = qdev_get_machine_hotplug_handler(dev); if (hotplug_ctrl == NULL && dev->parent_bus) { hotplug_ctrl = qdev_get_bus_hotplug_handler(dev); @@ -109,7 +109,7 @@ } /* can be used as ->unplug() callback for the simple cases */ -void qdev_simple_device_unplug_cb(HotplugHandler *hotplug_dev, +void qdev_simple_device_unplug_cb(const HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { qdev_unrealize(dev);
diff --git a/hw/core/qdev.c b/hw/core/qdev.c index 0b0f2f4..c6540be 100644 --- a/hw/core/qdev.c +++ b/hw/core/qdev.c
@@ -501,7 +501,7 @@ { DeviceState *dev = DEVICE(obj); DeviceClass *dc = DEVICE_GET_CLASS(dev); - HotplugHandler *hotplug_ctrl; + const HotplugHandler *hotplug_ctrl; BusState *bus; NamedClockList *ncl; Error *local_err = NULL;
diff --git a/hw/cxl/cxl-host.c b/hw/cxl/cxl-host.c index eba13c9..e279ce5 100644 --- a/hw/cxl/cxl-host.c +++ b/hw/cxl/cxl-host.c
@@ -61,6 +61,52 @@ fw->enc_int_gran = 0; } + /* + * HDM-D and HDM-H are advertised by default; disabling one + * (host-only=off or device-coherent=off) narrows the window to the + * other. + */ + if (!object->has_device_coherent || object->device_coherent) { + fw->restrictions |= CXL_FMW_DEVICE_COHERENT; + } + if (object->has_host_only ? object->host_only : !object->back_invalidate) { + fw->restrictions |= CXL_FMW_HOST_ONLY; + } + if (object->back_invalidate) { + fw->restrictions |= CXL_FMW_DEVICE_COHERENT | CXL_FMW_BI; + } + + if (!(fw->restrictions & (CXL_FMW_DEVICE_COHERENT | CXL_FMW_HOST_ONLY))) { + error_setg(errp, "CFMW coherency model required"); + return; + } + + if (object->fixed_config) { + fw->restrictions |= CXL_FMW_FIXED_CONFIG; /* no-op */ + } + + /* Volatile and persistent are permitted unless explicitly disabled. */ + if (!object->has_q_volatile || object->q_volatile) { + fw->restrictions |= CXL_FMW_VOLATILE; + } + if (!object->has_persistent || object->persistent) { + fw->restrictions |= CXL_FMW_PERSISTENT; + } + if (!(fw->restrictions & (CXL_FMW_VOLATILE | CXL_FMW_PERSISTENT))) { + error_setg(errp, "CFMW volatile and/or persistent memory required"); + return; + } + + /* + * Reject the undefined and conflicting coherency combinations, + * per CXL r4.0 9.18.1.3. + */ + if ((fw->restrictions & CXL_FMW_HOST_ONLY) && + (fw->restrictions & CXL_FMW_BI)) { + error_setg(errp, "CFMW host-only coherency + BI is undefined behavior"); + return; + } + fw->targets = g_malloc0_n(fw->num_targets, sizeof(*fw->targets)); for (i = 0, target = object->targets; target; i++, target = target->next) { /* This link cannot be resolved yet, so stash the name for now */ @@ -442,7 +488,7 @@ } } - interleave_ways_dec = cxl_interleave_ways_dec(iw, &error_fatal); + interleave_ways_dec = cxl_interleave_ways_dec(iw, NULL); if (interleave_ways_dec == 0) { return 0; }
diff --git a/hw/display/Kconfig b/hw/display/Kconfig index b3593fe..060274b 100644 --- a/hw/display/Kconfig +++ b/hw/display/Kconfig
@@ -120,6 +120,11 @@ default y depends on VIRTIO_VGA && VHOST_USER_GPU +config VHOST_USER_MEDIA + bool + default y + depends on VIRTIO && VHOST_USER + config DPCD bool select AUX
diff --git a/hw/display/meson.build b/hw/display/meson.build index ffecedb..d8b4e12 100644 --- a/hw/display/meson.build +++ b/hw/display/meson.build
@@ -37,6 +37,9 @@ system_ss.add(when: 'CONFIG_VGA', if_true: files('vga.c')) system_ss.add(when: 'CONFIG_VIRTIO', if_true: files('virtio-dmabuf.c')) system_ss.add(when: 'CONFIG_DM163', if_true: files('dm163.c')) +system_ss.add(when: 'CONFIG_VHOST_USER_MEDIA', if_true: files('vhost-user-media.c')) +system_ss.add(when: ['CONFIG_VHOST_USER_MEDIA', 'CONFIG_VIRTIO_PCI'], + if_true: files('vhost-user-media-pci.c')) stub_ss.add([files('acpi-vga-stub.c'), pixman]) if (config_all_devices.has_key('CONFIG_VGA_CIRRUS') or
diff --git a/hw/display/vhost-user-media-pci.c b/hw/display/vhost-user-media-pci.c new file mode 100644 index 0000000..7681f75 --- /dev/null +++ b/hw/display/vhost-user-media-pci.c
@@ -0,0 +1,115 @@ +/* + * Vhost-user MEDIA virtio device PCI glue + * + * Copyright Red Hat, Inc. 2026 + * Authors: Albert Esteve <aesteve@redhat.com> + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "hw/core/qdev-properties.h" +#include "hw/virtio/vhost-user-media.h" +#include "hw/virtio/virtio-pci.h" + +/* BAR 2 is used for the shared memory region exposed to the guest */ +#define VIRTIO_MEDIA_PCI_SHM_BAR 2 + +#define VIRTIO_MEDIA_PCI_SHMCAP_ID_SHM 0 + +#define TYPE_VHOST_USER_MEDIA_PCI "vhost-user-media-pci-base" +OBJECT_DECLARE_SIMPLE_TYPE(VHostUserMEDIAPCI, VHOST_USER_MEDIA_PCI) + +struct VHostUserMEDIAPCI { + VirtIOPCIProxy parent_obj; + VHostUserMEDIA vdev; + MemoryRegion shmbar; +}; + +static const Property vumedia_pci_properties[] = { + DEFINE_PROP_BIT("ioeventfd", VirtIOPCIProxy, flags, + VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT, true), + DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, + DEV_NVECTORS_UNSPECIFIED), +}; + +static void vumedia_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp) +{ + VHostUserMEDIAPCI *dev = VHOST_USER_MEDIA_PCI(vpci_dev); + DeviceState *dev_state = DEVICE(&dev->vdev); + VirtIODevice *vdev = VIRTIO_DEVICE(dev_state); + VirtioSharedMemory *shmem; + uint64_t shm_size; + + if (vpci_dev->nvectors == DEV_NVECTORS_UNSPECIFIED) { + vpci_dev->nvectors = 1; + } + + if (vpci_dev->flags & VIRTIO_PCI_FLAG_MODERN_PIO_NOTIFY) { + error_setg(errp, "modern-pio-notify is not supported due to PCI BAR " + "layout limitations"); + return; + } + + if (!qdev_realize(dev_state, BUS(&vpci_dev->bus), errp)) { + return; + } + + shmem = virtio_find_shmem_region(vdev, VIRTIO_MEDIA_PCI_SHMCAP_ID_SHM); + if (!shmem) { + error_setg(errp, "vhost-user-media: missing shared memory region"); + return; + } + shm_size = memory_region_size(&shmem->mr); + + memory_region_init(&dev->shmbar, OBJECT(vpci_dev), + "vhost-media-pci-shmbar", shm_size); + memory_region_add_subregion(&dev->shmbar, 0, &shmem->mr); + virtio_pci_add_shm_cap(vpci_dev, VIRTIO_MEDIA_PCI_SHM_BAR, 0, + shm_size, VIRTIO_MEDIA_PCI_SHMCAP_ID_SHM); + + /* After 'realized' so the memory region exists */ + pci_register_bar(&vpci_dev->pci_dev, VIRTIO_MEDIA_PCI_SHM_BAR, + PCI_BASE_ADDRESS_SPACE_MEMORY | + PCI_BASE_ADDRESS_MEM_PREFETCH | + PCI_BASE_ADDRESS_MEM_TYPE_64, + &dev->shmbar); +} + +static void vumedia_pci_class_init(ObjectClass *klass, const void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass); + PCIDeviceClass *pcidev_k = PCI_DEVICE_CLASS(klass); + k->realize = vumedia_pci_realize; + set_bit(DEVICE_CATEGORY_MISC, dc->categories); + device_class_set_props(dc, vumedia_pci_properties); + pcidev_k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET; + pcidev_k->device_id = 0; /* Set by virtio-pci based on virtio id */ + pcidev_k->revision = 0x00; + pcidev_k->class_id = PCI_CLASS_MULTIMEDIA_VIDEO; +} + +static void vumedia_pci_instance_init(Object *obj) +{ + VHostUserMEDIAPCI *dev = VHOST_USER_MEDIA_PCI(obj); + + virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev), + TYPE_VHOST_USER_MEDIA); +} + +static const VirtioPCIDeviceTypeInfo vumedia_pci_info = { + .base_name = TYPE_VHOST_USER_MEDIA_PCI, + .non_transitional_name = "vhost-user-media-pci", + .instance_size = sizeof(VHostUserMEDIAPCI), + .instance_init = vumedia_pci_instance_init, + .class_init = vumedia_pci_class_init, +}; + +static void vumedia_pci_register(void) +{ + virtio_pci_types_register(&vumedia_pci_info); +} + +type_init(vumedia_pci_register);
diff --git a/hw/display/vhost-user-media.c b/hw/display/vhost-user-media.c new file mode 100644 index 0000000..851af8d --- /dev/null +++ b/hw/display/vhost-user-media.c
@@ -0,0 +1,388 @@ +/* + * Vhost-user Media device + * + * Copyright Red Hat, Inc. 2026 + * + * This is the boilerplate for instantiating a vhost-user device + * implementing a virtio-media device. + * + * Authors: + * Albert Esteve <aesteve@redhat.com> + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qemu/error-report.h" +#include "hw/core/qdev-properties-system.h" +#include "standard-headers/linux/virtio_ids.h" +#include "hw/virtio/virtio-bus.h" +#include "hw/virtio/vhost-user-media.h" +#include "migration/blocker.h" + +static const int feature_bits[] = { + VIRTIO_F_VERSION_1, + VIRTIO_F_NOTIFY_ON_EMPTY, + VIRTIO_RING_F_INDIRECT_DESC, + VIRTIO_RING_F_EVENT_IDX, + VIRTIO_F_RING_RESET, + VHOST_INVALID_FEATURE_BIT +}; + +static void +vu_media_get_config(VirtIODevice *vdev, uint8_t *config_data) +{ + VHostUserMEDIA *media = VHOST_USER_MEDIA(vdev); + Error *local_err = NULL; + int ret; + + memset(config_data, 0, sizeof(struct virtio_media_config)); + + ret = vhost_dev_get_config(&media->vhost_dev, + config_data, sizeof(struct virtio_media_config), + &local_err); + if (ret) { + error_report_err(local_err); + return; + } +} + +static void vu_media_start(VirtIODevice *vdev) +{ + VHostUserMEDIA *media = VHOST_USER_MEDIA(vdev); + BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(vdev))); + VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus); + int ret; + int i; + + if (!k->set_guest_notifiers) { + error_report("binding does not support guest notifiers"); + return; + } + + ret = vhost_dev_enable_notifiers(&media->vhost_dev, vdev); + if (ret < 0) { + error_report("Error enabling host notifiers: %d", -ret); + return; + } + + ret = k->set_guest_notifiers(qbus->parent, media->vhost_dev.nvqs, true); + if (ret < 0) { + error_report("Error binding guest notifier: %d", -ret); + goto err_host_notifiers; + } + + media->vhost_dev.acked_features = vdev->guest_features; + + media->vhost_dev.vq_index_end = media->vhost_dev.nvqs; + ret = vhost_dev_start(&media->vhost_dev, vdev, true); + if (ret < 0) { + error_report("Error starting vhost-user-media: %d", -ret); + goto err_guest_notifiers; + } + + /* + * guest_notifier_mask/pending not used yet, so just unmask + * everything here. virtio-pci will do the right thing by + * enabling/disabling irqfd. + */ + for (i = 0; i < media->vhost_dev.nvqs; i++) { + vhost_virtqueue_mask(&media->vhost_dev, vdev, i, false); + } + + return; + +err_guest_notifiers: + k->set_guest_notifiers(qbus->parent, media->vhost_dev.nvqs, false); +err_host_notifiers: + vhost_dev_disable_notifiers(&media->vhost_dev, vdev); +} + +static void vu_media_stop(VirtIODevice *vdev) +{ + VHostUserMEDIA *media = VHOST_USER_MEDIA(vdev); + BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(vdev))); + VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus); + int ret; + + if (!k->set_guest_notifiers) { + return; + } + + vhost_dev_stop(&media->vhost_dev, vdev, true, false); + + ret = k->set_guest_notifiers(qbus->parent, media->vhost_dev.nvqs, false); + if (ret < 0) { + error_report("vhost guest notifier cleanup failed: %d", ret); + return; + } + + vhost_dev_disable_notifiers(&media->vhost_dev, vdev); +} + +static int vu_media_set_status(VirtIODevice *vdev, uint8_t status) +{ + VHostUserMEDIA *media = VHOST_USER_MEDIA(vdev); + bool should_start = virtio_device_should_start(vdev, status); + + if (vhost_dev_is_started(&media->vhost_dev) == should_start) { + return 0; + } + + if (should_start) { + vu_media_start(vdev); + } else { + vu_media_stop(vdev); + } + return 0; +} + +static uint64_t vu_media_get_features(VirtIODevice *vdev, + uint64_t requested_features, + Error **errp) +{ + VHostUserMEDIA *media = VHOST_USER_MEDIA(vdev); + + return vhost_get_features(&media->vhost_dev, feature_bits, + requested_features); +} + +static void vu_media_handle_output(VirtIODevice *vdev, VirtQueue *vq) +{ + /* + * Not normally called; it's the daemon that handles the queue; + * however virtio's cleanup path can call this. + */ +} + +static void vu_media_guest_notifier_mask(VirtIODevice *vdev, int idx, + bool mask) +{ + VHostUserMEDIA *media = VHOST_USER_MEDIA(vdev); + + if (idx == VIRTIO_CONFIG_IRQ_IDX) { + return; + } + + vhost_virtqueue_mask(&media->vhost_dev, vdev, idx, mask); +} + +static bool vu_media_guest_notifier_pending(VirtIODevice *vdev, int idx) +{ + VHostUserMEDIA *media = VHOST_USER_MEDIA(vdev); + + if (idx == VIRTIO_CONFIG_IRQ_IDX) { + return false; + } + + return vhost_virtqueue_pending(&media->vhost_dev, idx); +} + +static int vu_media_handle_config_change(struct vhost_dev *dev) +{ + virtio_notify_config(dev->vdev); + return 0; +} + +static const VhostDevConfigOps media_ops = { + .vhost_dev_config_notifier = vu_media_handle_config_change, +}; + +static int vu_media_connect(DeviceState *dev) +{ + VirtIODevice *vdev = VIRTIO_DEVICE(dev); + VHostUserMEDIA *media = VHOST_USER_MEDIA(vdev); + + if (media->connected) { + return 0; + } + media->connected = true; + + /* restore vhost state */ + if (virtio_device_started(vdev, vdev->status)) { + vu_media_start(vdev); + } + + return 0; +} + +static void vu_media_disconnect(DeviceState *dev) +{ + VirtIODevice *vdev = VIRTIO_DEVICE(dev); + VHostUserMEDIA *media = VHOST_USER_MEDIA(vdev); + + if (!media->connected) { + return; + } + media->connected = false; + + if (vhost_dev_is_started(&media->vhost_dev)) { + vu_media_stop(vdev); + } +} + +static void vu_media_event(void *opaque, QEMUChrEvent event) +{ + DeviceState *dev = opaque; + VirtIODevice *vdev = VIRTIO_DEVICE(dev); + VHostUserMEDIA *media = VHOST_USER_MEDIA(vdev); + + switch (event) { + case CHR_EVENT_OPENED: + if (vu_media_connect(dev) < 0) { + qemu_chr_fe_disconnect(&media->conf.chardev); + return; + } + break; + case CHR_EVENT_CLOSED: + vu_media_disconnect(dev); + break; + case CHR_EVENT_BREAK: + case CHR_EVENT_MUX_IN: + case CHR_EVENT_MUX_OUT: + /* Ignore */ + break; + } +} + +static void do_vhost_user_cleanup(VirtIODevice *vdev, VHostUserMEDIA *media, + struct vhost_virtqueue *vhost_vqs) +{ + virtio_delete_queue(media->command_vq); + virtio_delete_queue(media->event_vq); + g_free(vhost_vqs); + virtio_cleanup(vdev); + vhost_user_cleanup(&media->vhost_user); +} + +static void vu_media_device_realize(DeviceState *dev, Error **errp) +{ + VirtIODevice *vdev = VIRTIO_DEVICE(dev); + VHostUserMEDIA *media = VHOST_USER_MEDIA(dev); + uint64_t memory_sizes[VIRTIO_MAX_SHMEM_REGIONS]; + struct vhost_virtqueue *vhost_vqs; + int ret, nregions; + + if (!media->conf.chardev.chr) { + error_setg(errp, "vhost-user-media: chardev is mandatory"); + return; + } + + if (!vhost_user_init(&media->vhost_user, &media->conf.chardev, errp)) { + return; + } + + virtio_init(vdev, VIRTIO_ID_MEDIA, sizeof(struct virtio_media_config)); + + media->command_vq = virtio_add_queue(vdev, 128, vu_media_handle_output); + media->event_vq = virtio_add_queue(vdev, 128, vu_media_handle_output); + media->vhost_dev.nvqs = 2; + media->vhost_dev.vqs = g_new0(struct vhost_virtqueue, + media->vhost_dev.nvqs); + vhost_vqs = media->vhost_dev.vqs; + + vhost_dev_set_config_notifier(&media->vhost_dev, &media_ops); + media->vhost_user.supports_config = true; + + ret = vhost_dev_init(&media->vhost_dev, &media->vhost_user, + VHOST_BACKEND_TYPE_USER, 0, errp); + if (ret < 0) { + do_vhost_user_cleanup(vdev, media, vhost_vqs); + return; + } + + ret = media->vhost_dev.vhost_ops->vhost_get_shmem_config(&media->vhost_dev, + &nregions, + memory_sizes, + errp); + if (ret < 0) { + goto q_fail; + } + + if (!nregions || !memory_sizes[0]) { + error_setg(errp, "vhost-user-media: backend did not provide " + "shared memory region 0"); + goto q_fail; + } + + if (memory_sizes[0] % qemu_real_host_page_size() != 0) { + error_setg(errp, "shared memory region 0 size must be a multiple " + "of the host page size"); + goto q_fail; + } + + if (media->vhost_dev.migration_blocker == NULL) { + error_setg(&media->vhost_dev.migration_blocker, + "Migration disabled: devices with VIRTIO Shared Memory " + "Regions do not support migration yet."); + ret = migrate_add_blocker_normal(&media->vhost_dev.migration_blocker, + errp); + if (ret < 0) { + goto q_fail; + } + } + + virtio_new_shmem_region(vdev, 0, memory_sizes[0]); + + qemu_chr_fe_set_handlers(&media->conf.chardev, NULL, + NULL, vu_media_event, + NULL, (void *)dev, NULL, true); + return; + +q_fail: + vhost_dev_cleanup(&media->vhost_dev); + do_vhost_user_cleanup(vdev, media, vhost_vqs); +} + +static void vu_media_device_unrealize(DeviceState *dev) +{ + VirtIODevice *vdev = VIRTIO_DEVICE(dev); + VHostUserMEDIA *media = VHOST_USER_MEDIA(dev); + struct vhost_virtqueue *vhost_vqs = media->vhost_dev.vqs; + + /* This will stop vhost backend if appropriate. */ + vu_media_set_status(vdev, 0); + vhost_dev_cleanup(&media->vhost_dev); + do_vhost_user_cleanup(vdev, media, vhost_vqs); +} + +static const VMStateDescription vu_media_vmstate = { + .name = "vhost-user-media", + .unmigratable = 1, +}; + +static const Property vu_media_properties[] = { + DEFINE_PROP_CHR("chardev", VHostUserMEDIA, conf.chardev), +}; + +static void vu_media_class_init(ObjectClass *klass, const void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); + + device_class_set_props(dc, vu_media_properties); + dc->vmsd = &vu_media_vmstate; + set_bit(DEVICE_CATEGORY_MISC, dc->categories); + vdc->realize = vu_media_device_realize; + vdc->unrealize = vu_media_device_unrealize; + vdc->get_features = vu_media_get_features; + vdc->get_config = vu_media_get_config; + vdc->set_status = vu_media_set_status; + vdc->guest_notifier_mask = vu_media_guest_notifier_mask; + vdc->guest_notifier_pending = vu_media_guest_notifier_pending; +} + +static const TypeInfo vu_media_info = { + .name = TYPE_VHOST_USER_MEDIA, + .parent = TYPE_VIRTIO_DEVICE, + .instance_size = sizeof(VHostUserMEDIA), + .class_init = vu_media_class_init, +}; + +static void vu_media_register_types(void) +{ + type_register_static(&vu_media_info); +} + +type_init(vu_media_register_types)
diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c index 1696821..6d7308b 100644 --- a/hw/i386/acpi-build.c +++ b/hw/i386/acpi-build.c
@@ -1842,6 +1842,37 @@ return 0; } +/* + * IVHD type 0x10 reports features using Feature Reporting field, which has + * different format than extended feature register (EFR) in the IOMMU MMIO + * space. + * + * Convert the EFR format to feature reporting format. + */ +static uint32_t +get_amd_ivhd_feature_report(uint64_t extended_feature) +{ + uint32_t feature_report; + uint64_t hats_mode = (extended_feature & AMDVI_HATS_MODE_MASK) >> + AMDVI_HATS_MODE_SHIFT; + uint64_t gats_mode = (extended_feature & AMDVI_GATS_MODE_MASK) >> + AMDVI_GATS_MODE_SHIFT; + uint32_t is_ia = !!(extended_feature & AMDVI_FEATURE_IA); + uint32_t is_ga = !!(extended_feature & AMDVI_FEATURE_GA); + uint32_t is_gt = !!(extended_feature & AMDVI_FEATURE_GT); + uint32_t is_xt = !!(extended_feature & AMDVI_FEATURE_XT); + + feature_report = + hats_mode << AMDVI_IVHD_FEATURE_REPORT_HATS_SHIFT | /* HATS[31:30] */ + gats_mode << AMDVI_IVHD_FEATURE_REPORT_GATS_SHIFT | /* GATS[29:28] */ + is_ia << AMDVI_IVHD_FEATURE_REPORT_IA_SUP_SHIFT | /* IASup[5] */ + is_ga << AMDVI_IVHD_FEATURE_REPORT_GA_SUP_SHIFT | /* GASup[6] */ + is_gt << AMDVI_IVHD_FEATURE_REPORT_GT_SUP_SHIFT | /* GTSup[2] */ + is_xt << AMDVI_IVHD_FEATURE_REPORT_XT_SUP_SHIFT; /* XTSup[0] */ + + return feature_report; +} + static void build_amd_iommu(GArray *table_data, BIOSLinker *linker, const char *oem_id, const char *oem_table_id) @@ -1850,7 +1881,8 @@ GArray *ivhd_blob = g_array_new(false, true, 1); AcpiTable table = { .sig = "IVRS", .rev = 1, .oem_id = oem_id, .oem_table_id = oem_table_id }; - uint64_t feature_report; + uint16_t iommu_devid = pci_get_bdf(&s->pci->dev); + uint64_t extended_feature = amdvi_extended_feature_register(s); acpi_table_begin(&table, table_data); /* IVinfo - IO virtualization information common to all @@ -1858,7 +1890,9 @@ */ build_append_int_noprefix(table_data, (1UL << 0) | /* EFRSup */ - (40UL << 8), /* PASize */ + AMDVI_GVA_SIZE_48 | /* GVASize: 010b = 48 bits */ + AMDVI_PA_SIZE_52 | /* PASize: 011_0100b = 52 bits */ + AMDVI_VA_SIZE_64, /* VASize: 100_0000b = 64 bits */ 4); /* reserved */ build_append_int_noprefix(table_data, 0, 8); @@ -1904,16 +1938,13 @@ build_append_int_noprefix(table_data, (1UL << 0) | /* HtTunEn */ (1UL << 4) | /* iotblSup */ - (1UL << 6) | /* PrefSup */ - (1UL << 7), /* PPRSup */ + (1UL << 6), /* PrefSup */ 1); /* IVHD length */ build_append_int_noprefix(table_data, ivhd_blob->len + 24, 2); /* DeviceID */ - build_append_int_noprefix(table_data, - object_property_get_int(OBJECT(s->pci), "addr", - &error_abort), 2); + build_append_int_noprefix(table_data, iommu_devid, 2); /* Capability offset */ build_append_int_noprefix(table_data, s->pci->capab_offset, 2); /* IOMMU base address */ @@ -1923,14 +1954,9 @@ /* IOMMU info */ build_append_int_noprefix(table_data, 0, 2); /* IOMMU Feature Reporting */ - feature_report = (48UL << 30) | /* HATS */ - (48UL << 28) | /* GATS */ - (1UL << 2) | /* GTSup */ - (1UL << 6); /* GASup */ - if (s->xtsup) { - feature_report |= (1UL << 0); /* XTSup */ - } - build_append_int_noprefix(table_data, feature_report, 4); + build_append_int_noprefix(table_data, + get_amd_ivhd_feature_report(extended_feature), + 4); /* IVHD entries as found above */ g_array_append_vals(table_data, ivhd_blob->data, ivhd_blob->len); @@ -1945,10 +1971,9 @@ /* IVHD length */ build_append_int_noprefix(table_data, ivhd_blob->len + 40, 2); + /* DeviceID */ - build_append_int_noprefix(table_data, - object_property_get_int(OBJECT(s->pci), "addr", - &error_abort), 2); + build_append_int_noprefix(table_data, iommu_devid, 2); /* Capability offset */ build_append_int_noprefix(table_data, s->pci->capab_offset, 2); /* IOMMU base address */ @@ -1964,9 +1989,7 @@ build_append_int_noprefix(table_data, 0, 4); } /* EFR Register Image */ - build_append_int_noprefix(table_data, - amdvi_extended_feature_register(s), - 8); + build_append_int_noprefix(table_data, extended_feature, 8); /* EFR Register Image 2 */ build_append_int_noprefix(table_data, 0, 8);
diff --git a/hw/i386/amd_iommu-stub.c b/hw/i386/amd_iommu-stub.c index d62a373..fb36dee 100644 --- a/hw/i386/amd_iommu-stub.c +++ b/hw/i386/amd_iommu-stub.c
@@ -22,5 +22,5 @@ uint64_t amdvi_extended_feature_register(AMDVIState *s) { - return AMDVI_DEFAULT_EXT_FEATURES; + return 0; }
diff --git a/hw/i386/amd_iommu.c b/hw/i386/amd_iommu.c index 578c27c..56e7815 100644 --- a/hw/i386/amd_iommu.c +++ b/hw/i386/amd_iommu.c
@@ -2595,7 +2595,7 @@ 0xff000000); pci_set_long(pdev->config + s->capab_offset + AMDVI_CAPAB_MISC, 0); pci_set_long(pdev->config + s->capab_offset + AMDVI_CAPAB_MISC, - AMDVI_MAX_PH_ADDR | AMDVI_MAX_GVA_ADDR | AMDVI_MAX_VA_ADDR); + AMDVI_PA_SIZE_52 | AMDVI_GVA_SIZE_48 | AMDVI_VA_SIZE_64); } static void amdvi_sysbus_reset(DeviceState *dev)
diff --git a/hw/i386/amd_iommu.h b/hw/i386/amd_iommu.h index 687691e..c5b346c 100644 --- a/hw/i386/amd_iommu.h +++ b/hw/i386/amd_iommu.h
@@ -174,7 +174,11 @@ #define AMDVI_DTE_QUAD3_RESERVED (GENMASK64(14, 0) | GENMASK64(53, 48)) /* AMDVI paging mode */ +#define AMDVI_GATS_MODE_SHIFT (12) +#define AMDVI_GATS_MODE_MASK (3ULL << 12) #define AMDVI_GATS_MODE (2ULL << 12) +#define AMDVI_HATS_MODE_SHIFT (10) +#define AMDVI_HATS_MODE_MASK (3ULL << 10) #define AMDVI_HATS_MODE (2ULL << 10) #define AMDVI_HATS_MODE_RESERVED (3ULL << 10) @@ -223,9 +227,8 @@ /* default extended feature */ #define AMDVI_DEFAULT_EXT_FEATURES \ - (AMDVI_FEATURE_PREFETCH | AMDVI_FEATURE_PPR | \ - AMDVI_FEATURE_IA | AMDVI_FEATURE_GT | AMDVI_FEATURE_HE | \ - AMDVI_GATS_MODE | AMDVI_HATS_MODE | AMDVI_FEATURE_GA) + (AMDVI_FEATURE_PREFETCH | AMDVI_FEATURE_IA | AMDVI_FEATURE_GT | \ + AMDVI_GATS_MODE | AMDVI_HATS_MODE | AMDVI_FEATURE_GA) /* capabilities header */ #define AMDVI_CAPAB_FEATURES (AMDVI_CAPAB_FLAT_EXT | \ @@ -243,9 +246,14 @@ #define AMDVI_PAGE_SHIFT_4K 12 #define AMDVI_PAGE_MASK_4K GENMASK64(63, 12) -#define AMDVI_MAX_GVA_ADDR (2UL << 5) -#define AMDVI_MAX_PH_ADDR (40UL << 8) -#define AMDVI_MAX_VA_ADDR (48UL << 15) +/* + * IOMMU Capability Offset 10h: Miscellaneous Information Register 0. + * VAsize[21:15], PAsize[14:8], and GVAsize[7:5]. + * These fields use the same layout in the IVRS IVinfo Field. + */ +#define AMDVI_VA_SIZE_64 (64UL << 15) +#define AMDVI_PA_SIZE_52 (52UL << 8) +#define AMDVI_GVA_SIZE_48 (2UL << 5) /* Completion Wait data size */ #define AMDVI_COMPLETION_DATA_SIZE 8 @@ -291,6 +299,13 @@ #define AMDVI_DEV_LINT0_PASS_MASK (1ULL << 62) #define AMDVI_DEV_LINT1_PASS_MASK (1ULL << 63) +#define AMDVI_IVHD_FEATURE_REPORT_HATS_SHIFT (30) +#define AMDVI_IVHD_FEATURE_REPORT_GATS_SHIFT (28) +#define AMDVI_IVHD_FEATURE_REPORT_GA_SUP_SHIFT (6) +#define AMDVI_IVHD_FEATURE_REPORT_IA_SUP_SHIFT (5) +#define AMDVI_IVHD_FEATURE_REPORT_GT_SUP_SHIFT (2) +#define AMDVI_IVHD_FEATURE_REPORT_XT_SUP_SHIFT (0) + #define TYPE_AMD_IOMMU_DEVICE "amd-iommu" OBJECT_DECLARE_SIMPLE_TYPE(AMDVIState, AMD_IOMMU_DEVICE)
diff --git a/hw/i386/intel_iommu.c b/hw/i386/intel_iommu.c index 82c3c3b..350d2b7 100644 --- a/hw/i386/intel_iommu.c +++ b/hw/i386/intel_iommu.c
@@ -1964,13 +1964,13 @@ } } -static MemTxResult vtd_set_flag_in_pte(dma_addr_t base_addr, uint32_t index, - uint64_t pte, uint64_t flag) +static MemTxResult vtd_set_flags_in_pte(dma_addr_t base_addr, uint32_t index, + uint64_t pte, uint64_t flags) { - if (pte & flag) { + if ((pte & flags) == flags) { return MEMTX_OK; } - pte |= flag; + pte |= flags; pte = cpu_to_le64(pte); return dma_memory_write(&address_space_memory, base_addr + index * sizeof(pte), @@ -2039,11 +2039,11 @@ return -VTD_FR_FS_PAGING_ENTRY_RSVD; } - if (vtd_is_last_pte(fspte, *fspte_level) && is_write) { + if (vtd_is_last_pte(fspte, *fspte_level) && *writes && is_write) { flag_ad |= VTD_FS_D; } - if (vtd_set_flag_in_pte(addr, offset, fspte, flag_ad) != MEMTX_OK) { + if (vtd_set_flags_in_pte(addr, offset, fspte, flag_ad) != MEMTX_OK) { return -VTD_FR_FS_BIT_UPDATE_FAILED; } @@ -3627,15 +3627,26 @@ static void vtd_handle_prs_write(IntelIOMMUState *s) { - uint32_t prs = vtd_get_long_raw(s, DMAR_PRS_REG); + uint32_t prs; + + vtd_iommu_lock(s); + + prs = vtd_get_long_raw(s, DMAR_PRS_REG); if (!(prs & VTD_PR_STATUS_PPR) && !(prs & VTD_PR_STATUS_PRO)) { vtd_set_clear_mask_long(s, DMAR_PECTL_REG, VTD_PR_PECTL_IP, 0); } + + vtd_iommu_unlock(s); } static void vtd_handle_pectl_write(IntelIOMMUState *s) { - uint32_t pectl = vtd_get_long_raw(s, DMAR_PECTL_REG); + uint32_t pectl; + + vtd_iommu_lock(s); + + pectl = vtd_get_long_raw(s, DMAR_PECTL_REG); + if ((pectl & VTD_PR_PECTL_IP) && !(pectl & VTD_PR_PECTL_IM)) { /* * If IP field was 1 when software clears the IM field, @@ -3644,6 +3655,8 @@ vtd_set_clear_mask_long(s, DMAR_PECTL_REG, VTD_PR_PECTL_IP, 0); vtd_generate_interrupt(s, DMAR_PEADDR_REG, DMAR_PEDATA_REG); } + + vtd_iommu_unlock(s); } static uint64_t vtd_mem_read(void *opaque, hwaddr addr, unsigned size) @@ -5108,7 +5121,8 @@ } if (s->svm) { - s->ecap |= VTD_ECAP_PRS | VTD_ECAP_PDS | VTD_ECAP_NWFS; + s->ecap |= VTD_ECAP_PRS | VTD_ECAP_PDS | VTD_ECAP_NWFS | + VTD_ECAP_SMPWCS; } vtd_reset_caches(s); @@ -5377,19 +5391,18 @@ { IntelIOMMUState *s = opaque; VTDAddressSpace *vtd_as; + uint64_t queue_addr_reg; + uint64_t queue_tail_offset_reg; + uint64_t new_queue_tail_offset; + uint64_t queue_head_offset_reg; + hwaddr queue_tail; + uint32_t old_pr_status; + uint16_t sid; + VTDPRDesc desc; + int ret = 0; vtd_as = vtd_find_add_as(s, bus, devfn, pasid); - - uint64_t queue_addr_reg = vtd_get_quad(s, DMAR_PQA_REG); - uint64_t queue_tail_offset_reg = vtd_get_quad(s, DMAR_PQT_REG); - uint64_t new_queue_tail_offset = ( - (queue_tail_offset_reg + VTD_PQA_ENTRY_SIZE) % - (vtd_prq_size(s) * VTD_PQA_ENTRY_SIZE)); - uint64_t queue_head_offset_reg = vtd_get_quad(s, DMAR_PQH_REG); - hwaddr queue_tail = (queue_addr_reg & VTD_PQA_ADDR) + queue_tail_offset_reg; - uint32_t old_pr_status = vtd_get_long(s, DMAR_PRS_REG); - uint16_t sid = PCI_BUILD_BDF(pci_bus_num(vtd_as->bus), vtd_as->devfn); - VTDPRDesc desc; + sid = PCI_BUILD_BDF(pci_bus_num(vtd_as->bus), vtd_as->devfn); if (!(s->ecap & VTD_ECAP_PRS)) { return -EPERM; @@ -5413,25 +5426,7 @@ return -EPERM; } - if (old_pr_status & VTD_PR_STATUS_PRO) { - /* - * No action is taken by hardware to report a fault - * or generate an event - */ - return -ENOSPC; - } - - /* Check for overflow */ - if (new_queue_tail_offset == queue_head_offset_reg) { - vtd_set_clear_mask_long(s, DMAR_PRS_REG, 0, VTD_PR_STATUS_PRO); - vtd_generate_page_request_event(s, old_pr_status); - return -ENOSPC; - } - - if (vtd_pri_perform_implicit_invalidation(vtd_as, addr)) { - return -EINVAL; - } - + /* Prepare the descriptor */ desc.lo = VTD_PRD_TYPE | VTD_PRD_PP(true) | VTD_PRD_RID(sid) | VTD_PRD_PASID(vtd_as->pasid) | VTD_PRD_PMR(priv_req); desc.hi = VTD_PRD_RDR(is_read) | VTD_PRD_WRR(is_write) | @@ -5439,26 +5434,55 @@ desc.lo = cpu_to_le64(desc.lo); desc.hi = cpu_to_le64(desc.hi); + + if (vtd_pri_perform_implicit_invalidation(vtd_as, addr)) { + return -EINVAL; + } + + vtd_iommu_lock(s); + + queue_addr_reg = vtd_get_quad(s, DMAR_PQA_REG); + queue_tail_offset_reg = vtd_get_quad(s, DMAR_PQT_REG); + new_queue_tail_offset = ((queue_tail_offset_reg + VTD_PQA_ENTRY_SIZE) % + (vtd_prq_size(s) * VTD_PQA_ENTRY_SIZE)); + queue_head_offset_reg = vtd_get_quad(s, DMAR_PQH_REG); + queue_tail = (queue_addr_reg & VTD_PQA_ADDR) + queue_tail_offset_reg; + old_pr_status = vtd_get_long(s, DMAR_PRS_REG); + + if (old_pr_status & VTD_PR_STATUS_PRO) { + /* + * No action is taken by hardware to report a fault + * or generate an event + */ + ret = -ENOSPC; + goto out; + } + + /* Check for overflow */ + if (new_queue_tail_offset == queue_head_offset_reg) { + vtd_set_clear_mask_long(s, DMAR_PRS_REG, 0, VTD_PR_STATUS_PRO); + vtd_generate_page_request_event(s, old_pr_status); + ret = -ENOSPC; + goto out; + } + if (dma_memory_write(&address_space_memory, queue_tail, &desc, sizeof(desc), MEMTXATTRS_UNSPECIFIED)) { error_report_once("IO error, the PQ tail cannot be updated"); - return -EIO; + ret = -EIO; + goto out; } /* increment the tail register and set the pending request bit */ vtd_set_quad(s, DMAR_PQT_REG, new_queue_tail_offset); - /* - * read status again so that the kernel does not miss a request. - * in some cases, we can trigger an unecessary interrupt but this strategy - * drastically improves performance as we don't need to take a lock. - */ - old_pr_status = vtd_get_long(s, DMAR_PRS_REG); if (!(old_pr_status & VTD_PR_STATUS_PPR)) { vtd_set_clear_mask_long(s, DMAR_PRS_REG, 0, VTD_PR_STATUS_PPR); vtd_generate_page_request_event(s, old_pr_status); } - return 0; +out: + vtd_iommu_unlock(s); + return ret; } static void vtd_init_iotlb_notifier(PCIBus *bus, void *opaque, int devfn,
diff --git a/hw/i386/intel_iommu_accel.c b/hw/i386/intel_iommu_accel.c index c03435f..c367777 100644 --- a/hw/i386/intel_iommu_accel.c +++ b/hw/i386/intel_iommu_accel.c
@@ -37,6 +37,12 @@ return false; } + if ((s->ecap & VTD_ECAP_SMPWCS) && !(vtd->ecap_reg & VTD_ECAP_SMPWCS)) { + error_setg(errp, + "Scalable-mode coherent walk is unsupported by host IOMMU"); + return false; + } + if (s->fs1gp && !(vtd->cap_reg & VTD_CAP_FS1GP)) { error_setg(errp, "First stage 1GB large page is unsupported by host IOMMU");
diff --git a/hw/i386/intel_iommu_internal.h b/hw/i386/intel_iommu_internal.h index 924e91c..df7a0ef 100644 --- a/hw/i386/intel_iommu_internal.h +++ b/hw/i386/intel_iommu_internal.h
@@ -202,6 +202,7 @@ #define VTD_ECAP_SMTS (1ULL << 43) #define VTD_ECAP_SSTS (1ULL << 46) #define VTD_ECAP_FSTS (1ULL << 47) +#define VTD_ECAP_SMPWCS (1ULL << 48) /* CAP_REG */ /* (offset >> 4) << 24 */
diff --git a/hw/i386/microvm.c b/hw/i386/microvm.c index e7adab7..a49de33 100644 --- a/hw/i386/microvm.c +++ b/hw/i386/microvm.c
@@ -416,7 +416,7 @@ g_free(cmdline); } -static void microvm_device_pre_plug_cb(HotplugHandler *hotplug_dev, +static void microvm_device_pre_plug_cb(const HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { X86CPU *cpu = X86_CPU(dev); @@ -425,26 +425,26 @@ x86_cpu_pre_plug(hotplug_dev, dev, errp); } -static void microvm_device_plug_cb(HotplugHandler *hotplug_dev, +static void microvm_device_plug_cb(const HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { x86_cpu_plug(hotplug_dev, dev, errp); } -static void microvm_device_unplug_request_cb(HotplugHandler *hotplug_dev, +static void microvm_device_unplug_request_cb(const HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { error_setg(errp, "unplug not supported by microvm"); } -static void microvm_device_unplug_cb(HotplugHandler *hotplug_dev, +static void microvm_device_unplug_cb(const HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { error_setg(errp, "unplug not supported by microvm"); } -static HotplugHandler *microvm_get_hotplug_handler(MachineState *machine, - DeviceState *dev) +static const HotplugHandler *microvm_get_hotplug_handler(MachineState *machine, + DeviceState *dev) { if (object_dynamic_cast(OBJECT(dev), TYPE_CPU)) { return HOTPLUG_HANDLER(machine);
diff --git a/hw/i386/pc.c b/hw/i386/pc.c index e9e4fc2..9006e7c 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c
@@ -1181,7 +1181,7 @@ g_free(i8259); } -static void pc_memory_pre_plug(HotplugHandler *hotplug_dev, DeviceState *dev, +static void pc_memory_pre_plug(const HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { const X86MachineState *x86ms = X86_MACHINE(hotplug_dev); @@ -1214,7 +1214,7 @@ pc_dimm_pre_plug(PC_DIMM(dev), MACHINE(hotplug_dev), errp); } -static void pc_memory_plug(HotplugHandler *hotplug_dev, +static void pc_memory_plug(const HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { PCMachineState *pcms = PC_MACHINE(hotplug_dev); @@ -1231,7 +1231,7 @@ hotplug_handler_plug(x86ms->acpi_dev, dev, &error_abort); } -static void pc_memory_unplug_request(HotplugHandler *hotplug_dev, +static void pc_memory_unplug_request(const HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { X86MachineState *x86ms = X86_MACHINE(hotplug_dev); @@ -1256,7 +1256,7 @@ errp); } -static void pc_memory_unplug(HotplugHandler *hotplug_dev, +static void pc_memory_unplug(const HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { PCMachineState *pcms = PC_MACHINE(hotplug_dev); @@ -1274,7 +1274,7 @@ error_propagate(errp, local_err); } -static void pc_hv_balloon_pre_plug(HotplugHandler *hotplug_dev, +static void pc_hv_balloon_pre_plug(const HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { /* The vmbus handler has no hotplug handler; we should never end up here. */ @@ -1282,13 +1282,13 @@ memory_device_pre_plug(MEMORY_DEVICE(dev), MACHINE(hotplug_dev), errp); } -static void pc_hv_balloon_plug(HotplugHandler *hotplug_dev, +static void pc_hv_balloon_plug(const HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { memory_device_plug(MEMORY_DEVICE(dev), MACHINE(hotplug_dev)); } -static void pc_sp_mem_pre_plug(HotplugHandler *hotplug_dev, +static void pc_sp_mem_pre_plug(const HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { MachineState *ms = MACHINE(hotplug_dev); @@ -1304,7 +1304,7 @@ memory_device_pre_plug(MEMORY_DEVICE(dev), ms, errp); } -static void pc_sp_mem_plug(HotplugHandler *hotplug_dev, +static void pc_sp_mem_plug(const HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { SpMemDevice *spm = SP_MEM(dev); @@ -1318,7 +1318,7 @@ e820_add_entry(addr, size, E820_SOFT_RESERVED); } -static void pc_machine_device_pre_plug_cb(HotplugHandler *hotplug_dev, +static void pc_machine_device_pre_plug_cb(const HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) { @@ -1356,7 +1356,7 @@ } } -static void pc_machine_device_plug_cb(HotplugHandler *hotplug_dev, +static void pc_machine_device_plug_cb(const HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) { @@ -1372,7 +1372,7 @@ } } -static void pc_machine_device_unplug_request_cb(HotplugHandler *hotplug_dev, +static void pc_machine_device_unplug_request_cb(const HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) { @@ -1388,7 +1388,7 @@ } } -static void pc_machine_device_unplug_cb(HotplugHandler *hotplug_dev, +static void pc_machine_device_unplug_cb(const HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) { @@ -1403,8 +1403,8 @@ } } -static HotplugHandler *pc_get_hotplug_handler(MachineState *machine, - DeviceState *dev) +static const HotplugHandler *pc_get_hotplug_handler(MachineState *machine, + DeviceState *dev) { if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM) || object_dynamic_cast(OBJECT(dev), TYPE_SP_MEM) ||
diff --git a/hw/i386/x86-common.c b/hw/i386/x86-common.c index 8f9419e..ae58352 100644 --- a/hw/i386/x86-common.c +++ b/hw/i386/x86-common.c
@@ -158,7 +158,7 @@ return found_cpu; } -void x86_cpu_plug(HotplugHandler *hotplug_dev, +void x86_cpu_plug(const HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { CPUArchId *found_cpu; @@ -199,7 +199,7 @@ error_propagate(errp, local_err); } -void x86_cpu_unplug_request_cb(HotplugHandler *hotplug_dev, +void x86_cpu_unplug_request_cb(const HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { int idx = -1; @@ -222,7 +222,7 @@ errp); } -void x86_cpu_unplug_cb(HotplugHandler *hotplug_dev, +void x86_cpu_unplug_cb(const HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { CPUArchId *found_cpu; @@ -248,7 +248,7 @@ error_propagate(errp, local_err); } -void x86_cpu_pre_plug(HotplugHandler *hotplug_dev, +void x86_cpu_pre_plug(const HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { int idx;
diff --git a/hw/ide/core.c b/hw/ide/core.c index ef57379..06e6bb6 100644 --- a/hw/ide/core.c +++ b/hw/ide/core.c
@@ -898,6 +898,7 @@ if (ret == -EINVAL) { ide_dma_error(s); + block_acct_failed(blk_get_stats(s->blk), &s->acct); return; } @@ -968,7 +969,7 @@ if ((s->dma_cmd == IDE_DMA_READ || s->dma_cmd == IDE_DMA_WRITE) && !ide_sect_range_ok(s, sector_num, n)) { ide_dma_error(s); - block_acct_invalid(blk_get_stats(s->blk), s->acct.type); + block_acct_failed(blk_get_stats(s->blk), &s->acct); return; }
diff --git a/hw/intc/loongarch_dintc.c b/hw/intc/loongarch_dintc.c index c01f1fe..1bedfbc 100644 --- a/hw/intc/loongarch_dintc.c +++ b/hw/intc/loongarch_dintc.c
@@ -168,8 +168,8 @@ return loongarch_dintc_cpu_by_arch_id(s, arch_id); } -static void loongarch_dintc_cpu_plug(HotplugHandler *hotplug_dev, - DeviceState *dev, Error **errp) +static void loongarch_dintc_cpu_plug(const HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) { LoongArchDINTCState *s = LOONGARCH_DINTC(hotplug_dev); Object *obj = OBJECT(dev); @@ -194,8 +194,8 @@ return; } -static void loongarch_dintc_cpu_unplug(HotplugHandler *hotplug_dev, - DeviceState *dev, Error **errp) +static void loongarch_dintc_cpu_unplug(const HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) { LoongArchDINTCState *s = LOONGARCH_DINTC(hotplug_dev); Object *obj = OBJECT(dev);
diff --git a/hw/intc/loongarch_extioi_common.c b/hw/intc/loongarch_extioi_common.c index 5cb0d39..22cbb7a 100644 --- a/hw/intc/loongarch_extioi_common.c +++ b/hw/intc/loongarch_extioi_common.c
@@ -28,7 +28,7 @@ return NULL; } -static void loongarch_extioi_cpu_plug(HotplugHandler *hotplug_dev, +static void loongarch_extioi_cpu_plug(const HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { LoongArchExtIOICommonState *s = LOONGARCH_EXTIOI_COMMON(hotplug_dev); @@ -60,7 +60,7 @@ } } -static void loongarch_extioi_cpu_unplug(HotplugHandler *hotplug_dev, +static void loongarch_extioi_cpu_unplug(const HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { LoongArchExtIOICommonState *s = LOONGARCH_EXTIOI_COMMON(hotplug_dev);
diff --git a/hw/intc/loongarch_ipi.c b/hw/intc/loongarch_ipi.c index 28d816e..c8f775a 100644 --- a/hw/intc/loongarch_ipi.c +++ b/hw/intc/loongarch_ipi.c
@@ -129,7 +129,7 @@ } } -static void loongarch_ipi_cpu_plug(HotplugHandler *hotplug_dev, +static void loongarch_ipi_cpu_plug(const HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { LoongsonIPICommonState *lics = LOONGSON_IPI_COMMON(hotplug_dev); @@ -155,7 +155,7 @@ qdev_connect_gpio_out(DEVICE(lics), index, qdev_get_gpio_in(dev, IRQ_IPI)); } -static void loongarch_ipi_cpu_unplug(HotplugHandler *hotplug_dev, +static void loongarch_ipi_cpu_unplug(const HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { LoongsonIPICommonState *lics = LOONGSON_IPI_COMMON(hotplug_dev);
diff --git a/hw/loongarch/virt.c b/hw/loongarch/virt.c index 9cc7992..9c5371a 100644 --- a/hw/loongarch/virt.c +++ b/hw/loongarch/virt.c
@@ -1167,7 +1167,7 @@ return NULL; } -static void virt_cpu_pre_plug(HotplugHandler *hotplug_dev, +static void virt_cpu_pre_plug(const HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(hotplug_dev); @@ -1229,7 +1229,7 @@ numa_cpu_pre_plug(cpu_slot, dev, errp); } -static void virt_cpu_unplug_request(HotplugHandler *hotplug_dev, +static void virt_cpu_unplug_request(const HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(hotplug_dev); @@ -1246,7 +1246,7 @@ hotplug_handler_unplug_request(HOTPLUG_HANDLER(lvms->acpi_ged), dev, errp); } -static void virt_cpu_unplug(HotplugHandler *hotplug_dev, +static void virt_cpu_unplug(const HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { CPUArchId *cpu_slot; @@ -1268,7 +1268,7 @@ cpu_slot->cpu = NULL; } -static void virt_cpu_plug(HotplugHandler *hotplug_dev, +static void virt_cpu_plug(const HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { CPUArchId *cpu_slot; @@ -1304,14 +1304,14 @@ !object_dynamic_cast(OBJECT(dev), TYPE_NVDIMM); } -static void virt_mem_pre_plug(HotplugHandler *hotplug_dev, DeviceState *dev, - Error **errp) +static void virt_mem_pre_plug(const HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) { pc_dimm_pre_plug(PC_DIMM(dev), MACHINE(hotplug_dev), errp); } -static void virt_device_pre_plug(HotplugHandler *hotplug_dev, - DeviceState *dev, Error **errp) +static void virt_device_pre_plug(const HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) { if (memhp_type_supported(dev)) { virt_mem_pre_plug(hotplug_dev, dev, errp); @@ -1320,8 +1320,8 @@ } } -static void virt_mem_unplug_request(HotplugHandler *hotplug_dev, - DeviceState *dev, Error **errp) +static void virt_mem_unplug_request(const HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) { LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(hotplug_dev); @@ -1330,8 +1330,8 @@ errp); } -static void virt_device_unplug_request(HotplugHandler *hotplug_dev, - DeviceState *dev, Error **errp) +static void virt_device_unplug_request(const HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) { if (memhp_type_supported(dev)) { virt_mem_unplug_request(hotplug_dev, dev, errp); @@ -1340,8 +1340,8 @@ } } -static void virt_mem_unplug(HotplugHandler *hotplug_dev, - DeviceState *dev, Error **errp) +static void virt_mem_unplug(const HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) { LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(hotplug_dev); @@ -1350,8 +1350,8 @@ qdev_unrealize(dev); } -static void virt_device_unplug(HotplugHandler *hotplug_dev, - DeviceState *dev, Error **errp) +static void virt_device_unplug(const HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) { if (memhp_type_supported(dev)) { virt_mem_unplug(hotplug_dev, dev, errp); @@ -1360,8 +1360,8 @@ } } -static void virt_mem_plug(HotplugHandler *hotplug_dev, - DeviceState *dev, Error **errp) +static void virt_mem_plug(const HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) { LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(hotplug_dev); @@ -1370,8 +1370,8 @@ dev, &error_abort); } -static void virt_device_plug_cb(HotplugHandler *hotplug_dev, - DeviceState *dev, Error **errp) +static void virt_device_plug_cb(const HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) { LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(hotplug_dev); MachineClass *mc = MACHINE_GET_CLASS(lvms); @@ -1389,8 +1389,8 @@ } } -static HotplugHandler *virt_get_hotplug_handler(MachineState *machine, - DeviceState *dev) +static const HotplugHandler *virt_get_hotplug_handler(MachineState *machine, + DeviceState *dev) { MachineClass *mc = MACHINE_GET_CLASS(machine);
diff --git a/hw/mem/cxl_type3.c b/hw/mem/cxl_type3.c index 28f41fa..a04dc74 100644 --- a/hw/mem/cxl_type3.c +++ b/hw/mem/cxl_type3.c
@@ -936,8 +936,8 @@ } /* DOE Initialization */ - pcie_doe_init(pci_dev, &ct3d->doe_cdat, 0x190, doe_cdat_prot, true, - CXL_T3_MSIX_PCIE_DOE_TABLE_ACCESS); + pcie_doe_init(pci_dev, &ct3d->doe_cdat, cxl_cstate->dvsec_offset, + doe_cdat_prot, true, CXL_T3_MSIX_PCIE_DOE_TABLE_ACCESS); cxl_cstate->cdat.build_cdat_table = ct3_build_cdat_table; cxl_cstate->cdat.free_cdat_table = ct3_free_cdat_table; @@ -1200,7 +1200,7 @@ } if (((uint64_t)host_addr < decoder_base) || (hpa_offset >= decoder_size)) { - int decoded_iw = cxl_interleave_ways_dec(iw, &error_fatal); + int decoded_iw = cxl_interleave_ways_dec(iw, NULL); if (decoded_iw == 0) { return false;
diff --git a/hw/net/vhost_net.c b/hw/net/vhost_net.c index 323d117..6e05c99 100644 --- a/hw/net/vhost_net.c +++ b/hw/net/vhost_net.c
@@ -384,7 +384,7 @@ if (net->nc->info->poll) { net->nc->info->poll(net->nc, true); } - vhost_dev_stop(&net->dev, dev, false); + vhost_dev_stop(&net->dev, dev, false, false); fail_start: return r; } @@ -403,7 +403,7 @@ if (net->nc->info->poll) { net->nc->info->poll(net->nc, true); } - vhost_dev_stop(&net->dev, dev, false); + vhost_dev_stop(&net->dev, dev, false, false); if (net->nc->info->stop) { net->nc->info->stop(net->nc); } @@ -636,7 +636,8 @@ vhost_virtqueue_stop(&net->dev, vdev, net->dev.vqs + idx, - net->dev.vq_index + idx); + net->dev.vq_index + idx, + false); } int vhost_net_virtqueue_restart(VirtIODevice *vdev, NetClientState *nc, @@ -686,7 +687,7 @@ assert(ret >= 0); } - vhost_dev_stop(&net->dev, vdev, false); + vhost_dev_stop(&net->dev, vdev, false, false); return r; }
diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c index 814b99a..19f453f 100644 --- a/hw/net/virtio-net.c +++ b/hw/net/virtio-net.c
@@ -40,6 +40,7 @@ #include "migration/misc.h" #include "standard-headers/linux/ethtool.h" #include "system/system.h" +#include "system/runstate.h" #include "system/replay.h" #include "trace.h" #include "monitor/qdev.h" @@ -1750,12 +1751,16 @@ } ptr += n->host_hdr_len; + size -= n->host_hdr_len; + + if (size < sizeof(struct eth_header)) { + return 0; + } if (!memcmp(&ptr[12], vlan, sizeof(vlan))) { int vid; - /* Truncated vlan packet */ - if (size < n->host_hdr_len + 16) { + if (size < 16) { return 0; } vid = lduw_be_p(ptr + 14) & 0xfff; @@ -2099,6 +2104,14 @@ unit->ip = (void *)ip; ip_hdrlen = (ip->ip_ver_len & 0xF) << 2; unit->ip_plen = &ip->ip_len; + + if (ip_hdrlen != sizeof(struct ip_header)) { + unit->tcp = NULL; + unit->tcp_hdrlen = 0; + unit->payload = 0; + return; + } + unit->tcp = (struct tcp_header *)(((uint8_t *)unit->ip) + ip_hdrlen); unit->tcp_hdrlen = (htons(unit->tcp->th_offset_flags) & 0xF000) >> 10; unit->payload = read_unit_ip_len(unit) - ip_hdrlen - unit->tcp_hdrlen; @@ -2195,13 +2208,31 @@ { uint16_t hdr_len; VirtioNetRscSeg *seg; + size_t ip_size; hdr_len = chain->n->guest_hdr_len; + + /* + * Strip any trailing padding beyond the IP payload so that seg->size + * stays in sync with the IP length field used by the bounds check in + * virtio_net_rsc_coalesce_data(). virtio_net_rsc_sanity_check4/6() + * guarantees that ip_size <= size. + */ + ip_size = hdr_len + sizeof(struct eth_header); + if (chain->proto == ETH_P_IP) { + struct ip_header *ip = (struct ip_header *)(buf + ip_size); + ip_size += htons(ip->ip_len); + } else { + struct ip6_header *ip6 = (struct ip6_header *)(buf + ip_size); + ip_size += sizeof(struct ip6_header) + + htons(ip6->ip6_ctlun.ip6_un1.ip6_un1_plen); + } + seg = g_new(VirtioNetRscSeg, 1); seg->buf = g_malloc(hdr_len + sizeof(struct eth_header) + sizeof(struct ip6_header) + VIRTIO_NET_MAX_TCP_PAYLOAD); - memcpy(seg->buf, buf, size); - seg->size = size; + memcpy(seg->buf, buf, ip_size); + seg->size = ip_size; seg->packets = 1; seg->dup_ack = 0; seg->is_coalesced = 0; @@ -3079,7 +3110,17 @@ n->multiqueue = multiqueue; virtio_net_change_num_queues(n, max * 2 + 1); - virtio_net_set_queue_pairs(n); + /* + * virtio_net_set_multiqueue() called from set_features(0) on early + * reset, when peer may wait for incoming (and is not initialized + * yet). + * Don't worry about it: virtio_net_set_queue_pairs() will be called + * later from virtio_net_post_load_device(), and anyway will be + * no-op for local incoming migration with live backend passing. + */ + if (!n->peers_wait_incoming) { + virtio_net_set_queue_pairs(n); + } } static int virtio_net_pre_load_queues(VirtIODevice *vdev, uint32_t n) @@ -3108,6 +3149,17 @@ virtio_add_feature_ex(features, VIRTIO_NET_F_MAC); + if (n->peers_wait_incoming) { + /* + * Excessive feature set is OK for early initialization when + * we wait for local incoming migration: actual guest-negotiated + * features will come with migration stream anyway. And we are sure + * that we support same host-features as source, because the backend + * is the same (the same TAP device, for example). + */ + return; + } + if (!peer_has_vnet_hdr(n)) { virtio_clear_feature_ex(features, VIRTIO_NET_F_CSUM); virtio_clear_feature_ex(features, VIRTIO_NET_F_HOST_TSO4); @@ -3204,6 +3256,7 @@ 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, @@ -3261,6 +3314,20 @@ } 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; } @@ -3411,6 +3478,14 @@ { 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; @@ -3749,7 +3824,7 @@ static bool failover_unplug_primary(VirtIONet *n, DeviceState *dev) { - HotplugHandler *hotplug_ctrl; + const HotplugHandler *hotplug_ctrl; PCIDevice *pci_dev; Error *err = NULL; @@ -3772,7 +3847,7 @@ Error **errp) { Error *err = NULL; - HotplugHandler *hotplug_ctrl; + const HotplugHandler *hotplug_ctrl; PCIDevice *pdev = PCI_DEVICE(dev); BusState *primary_bus; @@ -3890,6 +3965,42 @@ return qatomic_read(&n->failover_primary_hidden); } +static bool virtio_net_check_peers_wait_incoming(VirtIONet *n, bool *waiting, + Error **errp) +{ + bool has_waiting = false; + bool has_not_waiting = false; + + for (int i = 0; i < n->max_queue_pairs; i++) { + NetClientState *peer = n->nic->ncs[i].peer; + if (!peer) { + continue; + } + + if (peer->info->is_wait_incoming && + peer->info->is_wait_incoming(peer)) { + has_waiting = true; + } else { + has_not_waiting = true; + } + + if (has_waiting && has_not_waiting) { + error_setg(errp, "Mixed peer states: some peers wait for incoming " + "migration while others don't"); + return false; + } + } + + if (has_waiting && !runstate_check(RUN_STATE_INMIGRATE)) { + error_setg(errp, "Peers wait for incoming, but it's not an incoming " + "migration."); + return false; + } + + *waiting = has_waiting; + return true; +} + static void virtio_net_device_realize(DeviceState *dev, Error **errp) { VirtIODevice *vdev = VIRTIO_DEVICE(dev); @@ -4027,6 +4138,12 @@ n->nic->ncs[i].do_not_pad = true; } + if (!virtio_net_check_peers_wait_incoming(n, &n->peers_wait_incoming, + errp)) { + virtio_cleanup(vdev); + return; + } + peer_test_vnet_hdr(n); if (peer_has_vnet_hdr(n)) { n->host_hdr_len = sizeof(struct virtio_net_hdr);
diff --git a/hw/nvme/ctrl.c b/hw/nvme/ctrl.c index c662d96..c641226 100644 --- a/hw/nvme/ctrl.c +++ b/hw/nvme/ctrl.c
@@ -2389,7 +2389,6 @@ trace_pci_nvme_compare_mdata_cb(nvme_cid(req)); if (ret) { - block_acct_failed(stats, acct); req->status = NVME_UNRECOVERED_READ; trace_pci_nvme_err_aio(nvme_cid(req), strerror(-ret), req->status); @@ -2444,9 +2443,14 @@ goto out; } - block_acct_done(stats, acct); out: + if (req->status == NVME_SUCCESS) { + block_acct_done(stats, acct); + } else { + block_acct_failed(stats, acct); + } + qemu_iovec_destroy(&ctx->data.iov); g_free(ctx->data.bounce); @@ -2474,7 +2478,6 @@ trace_pci_nvme_compare_data_cb(nvme_cid(req)); if (ret) { - block_acct_failed(stats, acct); req->status = NVME_UNRECOVERED_READ; trace_pci_nvme_err_aio(nvme_cid(req), strerror(-ret), req->status); @@ -2513,9 +2516,13 @@ return; } - block_acct_done(stats, acct); - out: + if (req->status == NVME_SUCCESS) { + block_acct_done(stats, acct); + } else { + block_acct_failed(stats, acct); + } + qemu_iovec_destroy(&ctx->data.iov); g_free(ctx->data.bounce); g_free(ctx); @@ -2802,8 +2809,6 @@ static void nvme_copy_done(NvmeCopyAIOCB *iocb) { NvmeRequest *req = iocb->req; - NvmeNamespace *ns = req->ns; - BlockAcctStats *stats = blk_get_stats(ns->blkconf.blk); if (iocb->idx != iocb->nr) { req->cqe.result = cpu_to_le32(iocb->idx); @@ -2813,14 +2818,6 @@ g_free(iocb->bounce); g_free(iocb->ranges); - if (iocb->ret < 0) { - block_acct_failed(stats, &iocb->acct.read); - block_acct_failed(stats, &iocb->acct.write); - } else { - block_acct_done(stats, &iocb->acct.read); - block_acct_done(stats, &iocb->acct.write); - } - iocb->common.cb(iocb->common.opaque, iocb->ret); qemu_aio_unref(iocb); } @@ -2951,6 +2948,7 @@ NvmeCopyAIOCB *iocb = opaque; NvmeRequest *req = iocb->req; NvmeNamespace *dns = req->ns; + BlockAcctStats *stats = blk_get_stats(dns->blkconf.blk); uint32_t nlb; nvme_copy_source_range_parse(iocb->ranges, iocb->idx, iocb->format, NULL, @@ -2959,10 +2957,12 @@ if (ret < 0) { iocb->ret = ret; req->status = NVME_WRITE_FAULT; - goto out; - } else if (iocb->ret < 0) { + } + if (iocb->ret < 0) { + block_acct_failed(stats, &iocb->acct.write); goto out; } + block_acct_done(stats, &iocb->acct.write); if (dns->params.zoned) { nvme_advance_zone_wp(dns, iocb->zone, nlb); @@ -2979,11 +2979,18 @@ NvmeCopyAIOCB *iocb = opaque; NvmeRequest *req = iocb->req; NvmeNamespace *dns = req->ns; + BlockAcctStats *stats = blk_get_stats(dns->blkconf.blk); uint32_t nlb; size_t mlen; uint8_t *mbounce; - if (ret < 0 || iocb->ret < 0 || !dns->lbaf.ms) { + if (ret < 0 || iocb->ret < 0) { + block_acct_failed(stats, &iocb->acct.write); + goto out; + } + block_acct_done(stats, &iocb->acct.write); + + if (!dns->lbaf.ms) { goto out; } @@ -2996,6 +3003,7 @@ qemu_iovec_reset(&iocb->iov); qemu_iovec_add(&iocb->iov, mbounce, mlen); + block_acct_start(stats, &iocb->acct.write, mlen, BLOCK_ACCT_WRITE); iocb->aiocb = blk_aio_pwritev(dns->blkconf.blk, nvme_moff(dns, iocb->slba), &iocb->iov, 0, nvme_copy_out_completed_cb, iocb); @@ -3012,6 +3020,7 @@ NvmeRequest *req = iocb->req; NvmeNamespace *sns = iocb->sns; NvmeNamespace *dns = req->ns; + BlockAcctStats *sstats = blk_get_stats(sns->blkconf.blk); NvmeCopyCmd *copy = NULL; uint8_t *mbounce = NULL; uint32_t nlb; @@ -3024,10 +3033,12 @@ if (ret < 0) { iocb->ret = ret; req->status = NVME_UNRECOVERED_READ; - goto out; - } else if (iocb->ret < 0) { + } + if (iocb->ret < 0) { + block_acct_failed(sstats, &iocb->acct.read); goto out; } + block_acct_done(sstats, &iocb->acct.read); nvme_copy_source_range_parse(iocb->ranges, iocb->idx, iocb->format, &slba, &nlb, NULL, &apptag, &appmask, &reftag); @@ -3102,7 +3113,7 @@ qemu_iovec_reset(&iocb->iov); qemu_iovec_add(&iocb->iov, iocb->bounce, len); - block_acct_start(blk_get_stats(dns->blkconf.blk), &iocb->acct.write, 0, + block_acct_start(blk_get_stats(dns->blkconf.blk), &iocb->acct.write, len, BLOCK_ACCT_WRITE); iocb->aiocb = blk_aio_pwritev(dns->blkconf.blk, nvme_l2b(dns, iocb->slba), @@ -3121,20 +3132,29 @@ { NvmeCopyAIOCB *iocb = opaque; NvmeNamespace *sns = iocb->sns; + BlockAcctStats *stats = blk_get_stats(sns->blkconf.blk); uint64_t slba; uint32_t nlb; + size_t mlen; - if (ret < 0 || iocb->ret < 0 || !sns->lbaf.ms) { + if (ret < 0 || iocb->ret < 0) { + block_acct_failed(stats, &iocb->acct.read); + goto out; + } + block_acct_done(stats, &iocb->acct.read); + + if (!sns->lbaf.ms) { goto out; } nvme_copy_source_range_parse(iocb->ranges, iocb->idx, iocb->format, &slba, &nlb, NULL, NULL, NULL, NULL); + mlen = nvme_m2b(sns, nlb); qemu_iovec_reset(&iocb->iov); - qemu_iovec_add(&iocb->iov, iocb->bounce + nvme_l2b(sns, nlb), - nvme_m2b(sns, nlb)); + qemu_iovec_add(&iocb->iov, iocb->bounce + nvme_l2b(sns, nlb), mlen); + block_acct_start(stats, &iocb->acct.read, mlen, BLOCK_ACCT_READ); iocb->aiocb = blk_aio_preadv(sns->blkconf.blk, nvme_moff(sns, slba), &iocb->iov, 0, nvme_copy_in_completed_cb, iocb); @@ -3342,7 +3362,7 @@ assert(len <= blen); qemu_iovec_add(&iocb->iov, iocb->bounce, len); - block_acct_start(blk_get_stats(sns->blkconf.blk), &iocb->acct.read, 0, + block_acct_start(blk_get_stats(sns->blkconf.blk), &iocb->acct.read, len, BLOCK_ACCT_READ); iocb->aiocb = blk_aio_preadv(sns->blkconf.blk, nvme_l2b(sns, slba), @@ -10595,8 +10615,8 @@ }, }; -static void nvme_ns_hot_plug(HotplugHandler *hotplug_dev, DeviceState *dev, - Error **errp) +static void nvme_ns_hot_plug(const HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) { NvmeNamespace *ns = NVME_NS(dev); NvmeSubsystem *subsys = ns->subsys; @@ -10627,8 +10647,8 @@ } } -static void nvme_ns_hot_unplug(HotplugHandler *hotplug_dev, DeviceState *dev, - Error **errp) +static void nvme_ns_hot_unplug(const HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) { NvmeNamespace *ns = NVME_NS(dev); NvmeSubsystem *subsys = ns->subsys;
diff --git a/hw/pci-bridge/pci_bridge_dev.c b/hw/pci-bridge/pci_bridge_dev.c index 0c13835..bc53bcb 100644 --- a/hw/pci-bridge/pci_bridge_dev.c +++ b/hw/pci-bridge/pci_bridge_dev.c
@@ -205,7 +205,7 @@ } }; -void pci_bridge_dev_plug_cb(HotplugHandler *hotplug_dev, DeviceState *dev, +void pci_bridge_dev_plug_cb(const HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { PCIDevice *pci_hotplug_dev = PCI_DEVICE(hotplug_dev); @@ -218,7 +218,7 @@ shpc_device_plug_cb(hotplug_dev, dev, errp); } -void pci_bridge_dev_unplug_cb(HotplugHandler *hotplug_dev, DeviceState *dev, +void pci_bridge_dev_unplug_cb(const HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { PCIDevice *pci_hotplug_dev = PCI_DEVICE(hotplug_dev); @@ -227,7 +227,7 @@ shpc_device_unplug_cb(hotplug_dev, dev, errp); } -void pci_bridge_dev_unplug_request_cb(HotplugHandler *hotplug_dev, +void pci_bridge_dev_unplug_request_cb(const HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { PCIDevice *pci_hotplug_dev = PCI_DEVICE(hotplug_dev);
diff --git a/hw/pci/pci.c b/hw/pci/pci.c index c15f2b9..0efb4eb 100644 --- a/hw/pci/pci.c +++ b/hw/pci/pci.c
@@ -840,6 +840,8 @@ } memcpy(s->config, config, size); + memory_region_transaction_begin(); + pci_update_mappings(s); if (IS_PCI_BRIDGE(s)) { pci_bridge_update_mappings(PCI_BRIDGE(s)); @@ -848,6 +850,8 @@ pci_set_master(s, pci_get_word(s->config + PCI_COMMAND) & PCI_COMMAND_MASTER); + memory_region_transaction_commit(); + g_free(config); return 0; } @@ -1742,6 +1746,8 @@ int i; pcibus_t new_addr; + memory_region_transaction_begin(); + for(i = 0; i < PCI_NUM_REGIONS; i++) { r = &d->io_regions[i]; @@ -1778,6 +1784,8 @@ } pci_update_vga(d); + + memory_region_transaction_commit(); } int pci_irq_disabled(PCIDevice *d)
diff --git a/hw/pci/pcie.c b/hw/pci/pcie.c index 4622c75..42bcb92 100644 --- a/hw/pci/pcie.c +++ b/hw/pci/pcie.c
@@ -508,7 +508,7 @@ } } -void pcie_cap_slot_pre_plug_cb(HotplugHandler *hotplug_dev, DeviceState *dev, +void pcie_cap_slot_pre_plug_cb(const HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { PCIDevice *hotplug_pdev = PCI_DEVICE(hotplug_dev); @@ -525,7 +525,7 @@ pcie_cap_slot_plug_common(PCI_DEVICE(hotplug_dev), dev, errp); } -void pcie_cap_slot_plug_cb(HotplugHandler *hotplug_dev, DeviceState *dev, +void pcie_cap_slot_plug_cb(const HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { PCIDevice *hotplug_pdev = PCI_DEVICE(hotplug_dev); @@ -571,7 +571,7 @@ } } -void pcie_cap_slot_unplug_cb(HotplugHandler *hotplug_dev, DeviceState *dev, +void pcie_cap_slot_unplug_cb(const HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { qdev_unrealize(dev); @@ -579,7 +579,7 @@ static void pcie_unplug_device(PCIBus *bus, PCIDevice *dev, void *opaque) { - HotplugHandler *hotplug_ctrl = qdev_get_hotplug_handler(DEVICE(dev)); + const HotplugHandler *hotplug_ctrl = qdev_get_hotplug_handler(DEVICE(dev)); if (dev->partially_hotplugged) { dev->qdev.pending_deleted_event = false; @@ -608,7 +608,7 @@ PCI_EXP_SLTSTA_PDC); } -void pcie_cap_slot_unplug_request_cb(HotplugHandler *hotplug_dev, +void pcie_cap_slot_unplug_request_cb(const HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { Error *local_err = NULL;
diff --git a/hw/pci/pcie_port.c b/hw/pci/pcie_port.c index dbb6032..57fcc90 100644 --- a/hw/pci/pcie_port.c +++ b/hw/pci/pcie_port.c
@@ -188,7 +188,7 @@ return dsp_count; } -static bool pcie_slot_is_hotpluggable_bus(HotplugHandler *plug_handler, +static bool pcie_slot_is_hotpluggable_bus(const HotplugHandler *plug_handler, BusState *bus) { PCIESlot *s = PCIE_SLOT(bus->parent);
diff --git a/hw/pci/shpc.c b/hw/pci/shpc.c index 9386028..3f24ca8 100644 --- a/hw/pci/shpc.c +++ b/hw/pci/shpc.c
@@ -271,7 +271,6 @@ static void shpc_free_devices_in_slot(SHPCDevice *shpc, int slot) { - HotplugHandler *hotplug_ctrl; int devfn; int pci_slot = SHPC_IDX_TO_PCI(slot); for (devfn = PCI_DEVFN(pci_slot, 0); @@ -279,6 +278,8 @@ ++devfn) { PCIDevice *affected_dev = shpc->sec_bus->devices[devfn]; if (affected_dev) { + const HotplugHandler *hotplug_ctrl; + hotplug_ctrl = qdev_get_hotplug_handler(DEVICE(affected_dev)); hotplug_handler_unplug(hotplug_ctrl, DEVICE(affected_dev), &error_abort); @@ -560,8 +561,8 @@ return true; } -void shpc_device_plug_cb(HotplugHandler *hotplug_dev, DeviceState *dev, - Error **errp) +void shpc_device_plug_cb(const HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) { PCIDevice *pci_hotplug_dev = PCI_DEVICE(hotplug_dev); SHPCDevice *shpc = pci_hotplug_dev->shpc; @@ -600,13 +601,13 @@ shpc_interrupt_update(pci_hotplug_dev); } -void shpc_device_unplug_cb(HotplugHandler *hotplug_dev, DeviceState *dev, +void shpc_device_unplug_cb(const HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { qdev_unrealize(dev); } -void shpc_device_unplug_request_cb(HotplugHandler *hotplug_dev, +void shpc_device_unplug_request_cb(const HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { PCIDevice *pci_hotplug_dev = PCI_DEVICE(hotplug_dev);
diff --git a/hw/ppc/e500plat.c b/hw/ppc/e500plat.c index 85cec81..4bc1426 100644 --- a/hw/ppc/e500plat.c +++ b/hw/ppc/e500plat.c
@@ -42,7 +42,7 @@ ppce500_init(machine); } -static void e500plat_machine_device_plug_cb(HotplugHandler *hotplug_dev, +static void e500plat_machine_device_plug_cb(const HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { PPCE500MachineState *pms = PPCE500_MACHINE(hotplug_dev); @@ -53,7 +53,7 @@ } } -static +static const HotplugHandler *e500plat_machine_get_hotpug_handler(MachineState *machine, DeviceState *dev) {
diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index 20e0249..6d99464 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c
@@ -3616,7 +3616,7 @@ } } -static void spapr_memory_plug(HotplugHandler *hotplug_dev, DeviceState *dev) +static void spapr_memory_plug(const HotplugHandler *hotplug_dev, DeviceState *dev) { SpaprMachineState *ms = SPAPR_MACHINE(hotplug_dev); PCDIMMDevice *dimm = PC_DIMM(dev); @@ -3642,7 +3642,7 @@ } } -static void spapr_memory_pre_plug(HotplugHandler *hotplug_dev, DeviceState *dev, +static void spapr_memory_pre_plug(const HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { SpaprMachineState *spapr = SPAPR_MACHINE(hotplug_dev); @@ -3807,7 +3807,7 @@ /* Callback to be called during DRC release. */ void spapr_lmb_release(DeviceState *dev) { - HotplugHandler *hotplug_ctrl = qdev_get_hotplug_handler(dev); + const HotplugHandler *hotplug_ctrl = qdev_get_hotplug_handler(dev); SpaprMachineState *spapr = SPAPR_MACHINE(hotplug_ctrl); SpaprDimmState *ds = spapr_pending_dimm_unplugs_find(spapr, PC_DIMM(dev)); @@ -3832,7 +3832,7 @@ object_unparent(OBJECT(dev)); } -static void spapr_memory_unplug(HotplugHandler *hotplug_dev, DeviceState *dev) +static void spapr_memory_unplug(const HotplugHandler *hotplug_dev, DeviceState *dev) { SpaprMachineState *spapr = SPAPR_MACHINE(hotplug_dev); SpaprDimmState *ds = spapr_pending_dimm_unplugs_find(spapr, PC_DIMM(dev)); @@ -3845,7 +3845,7 @@ spapr_pending_dimm_unplugs_remove(spapr, ds); } -static void spapr_memory_unplug_request(HotplugHandler *hotplug_dev, +static void spapr_memory_unplug_request(const HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { SpaprMachineState *spapr = SPAPR_MACHINE(hotplug_dev); @@ -3899,14 +3899,14 @@ /* Callback to be called during DRC release. */ void spapr_core_release(DeviceState *dev) { - HotplugHandler *hotplug_ctrl = qdev_get_hotplug_handler(dev); + const HotplugHandler *hotplug_ctrl = qdev_get_hotplug_handler(dev); /* Call the unplug handler chain. This can never fail. */ hotplug_handler_unplug(hotplug_ctrl, dev, &error_abort); object_unparent(OBJECT(dev)); } -static void spapr_core_unplug(HotplugHandler *hotplug_dev, DeviceState *dev) +static void spapr_core_unplug(const HotplugHandler *hotplug_dev, DeviceState *dev) { MachineState *ms = MACHINE(hotplug_dev); CPUCore *cc = CPU_CORE(dev); @@ -3918,7 +3918,7 @@ } static -void spapr_core_unplug_request(HotplugHandler *hotplug_dev, DeviceState *dev, +void spapr_core_unplug_request(const HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { SpaprMachineState *spapr = SPAPR_MACHINE(OBJECT(hotplug_dev)); @@ -3988,7 +3988,7 @@ return 0; } -static void spapr_core_plug(HotplugHandler *hotplug_dev, DeviceState *dev) +static void spapr_core_plug(const HotplugHandler *hotplug_dev, DeviceState *dev) { SpaprMachineState *spapr = SPAPR_MACHINE(OBJECT(hotplug_dev)); MachineClass *mc = MACHINE_GET_CLASS(spapr); @@ -4043,7 +4043,7 @@ } -static void spapr_core_pre_plug(HotplugHandler *hotplug_dev, DeviceState *dev, +static void spapr_core_pre_plug(const HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { MachineState *machine = MACHINE(OBJECT(hotplug_dev)); @@ -4169,7 +4169,7 @@ return true; } -static bool spapr_phb_pre_plug(HotplugHandler *hotplug_dev, DeviceState *dev, +static bool spapr_phb_pre_plug(const HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { SpaprMachineState *spapr = SPAPR_MACHINE(OBJECT(hotplug_dev)); @@ -4198,7 +4198,7 @@ windows_supported, sphb->dma_liobn, errp); } -static void spapr_phb_plug(HotplugHandler *hotplug_dev, DeviceState *dev) +static void spapr_phb_plug(const HotplugHandler *hotplug_dev, DeviceState *dev) { SpaprPhbState *sphb = SPAPR_PCI_HOST_BRIDGE(dev); SpaprDrc *drc; @@ -4220,18 +4220,18 @@ void spapr_phb_release(DeviceState *dev) { - HotplugHandler *hotplug_ctrl = qdev_get_hotplug_handler(dev); + const HotplugHandler *hotplug_ctrl = qdev_get_hotplug_handler(dev); hotplug_handler_unplug(hotplug_ctrl, dev, &error_abort); object_unparent(OBJECT(dev)); } -static void spapr_phb_unplug(HotplugHandler *hotplug_dev, DeviceState *dev) +static void spapr_phb_unplug(const HotplugHandler *hotplug_dev, DeviceState *dev) { qdev_unrealize(dev); } -static void spapr_phb_unplug_request(HotplugHandler *hotplug_dev, +static void spapr_phb_unplug_request(const HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { SpaprPhbState *sphb = SPAPR_PCI_HOST_BRIDGE(dev); @@ -4251,7 +4251,7 @@ } static -bool spapr_tpm_proxy_pre_plug(HotplugHandler *hotplug_dev, DeviceState *dev, +bool spapr_tpm_proxy_pre_plug(const HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { SpaprMachineState *spapr = SPAPR_MACHINE(OBJECT(hotplug_dev)); @@ -4264,7 +4264,7 @@ return true; } -static void spapr_tpm_proxy_plug(HotplugHandler *hotplug_dev, DeviceState *dev) +static void spapr_tpm_proxy_plug(const HotplugHandler *hotplug_dev, DeviceState *dev) { SpaprMachineState *spapr = SPAPR_MACHINE(OBJECT(hotplug_dev)); SpaprTpmProxy *tpm_proxy = SPAPR_TPM_PROXY(dev); @@ -4275,7 +4275,7 @@ spapr->tpm_proxy = tpm_proxy; } -static void spapr_tpm_proxy_unplug(HotplugHandler *hotplug_dev, DeviceState *dev) +static void spapr_tpm_proxy_unplug(const HotplugHandler *hotplug_dev, DeviceState *dev) { SpaprMachineState *spapr = SPAPR_MACHINE(OBJECT(hotplug_dev)); @@ -4284,7 +4284,7 @@ spapr->tpm_proxy = NULL; } -static void spapr_machine_device_plug(HotplugHandler *hotplug_dev, +static void spapr_machine_device_plug(const HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) { @@ -4298,7 +4298,7 @@ } } -static void spapr_machine_device_unplug(HotplugHandler *hotplug_dev, +static void spapr_machine_device_unplug(const HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) { @@ -4324,7 +4324,7 @@ spapr_ovec_empty(spapr->ov5_cas); } -static void spapr_machine_device_unplug_request(HotplugHandler *hotplug_dev, +static void spapr_machine_device_unplug_request(const HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { SpaprMachineState *sms = SPAPR_MACHINE(OBJECT(hotplug_dev)); @@ -4349,7 +4349,7 @@ } } -static void spapr_machine_device_pre_plug(HotplugHandler *hotplug_dev, +static void spapr_machine_device_pre_plug(const HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) { @@ -4363,8 +4363,8 @@ } } -static HotplugHandler *spapr_get_hotplug_handler(MachineState *machine, - DeviceState *dev) +static const HotplugHandler *spapr_get_hotplug_handler(MachineState *machine, + DeviceState *dev) { if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM) || object_dynamic_cast(OBJECT(dev), TYPE_SPAPR_CPU_CORE) ||
diff --git a/hw/ppc/spapr_nvdimm.c b/hw/ppc/spapr_nvdimm.c index 6647428..542ed79 100644 --- a/hw/ppc/spapr_nvdimm.c +++ b/hw/ppc/spapr_nvdimm.c
@@ -64,7 +64,7 @@ void (*unrealize)(NVDIMMDevice *dimm, Error **errp); }; -bool spapr_nvdimm_validate(HotplugHandler *hotplug_dev, NVDIMMDevice *nvdimm, +bool spapr_nvdimm_validate(const HotplugHandler *hotplug_dev, NVDIMMDevice *nvdimm, uint64_t size, Error **errp) { const MachineClass *mc = MACHINE_GET_CLASS(hotplug_dev);
diff --git a/hw/ppc/spapr_pci.c b/hw/ppc/spapr_pci.c index c1d4b78..f7fc544 100644 --- a/hw/ppc/spapr_pci.c +++ b/hw/ppc/spapr_pci.c
@@ -1445,7 +1445,7 @@ /* Callback to be called during DRC release. */ void spapr_phb_remove_pci_device_cb(DeviceState *dev) { - HotplugHandler *hotplug_ctrl = qdev_get_hotplug_handler(dev); + const HotplugHandler *hotplug_ctrl = qdev_get_hotplug_handler(dev); hotplug_handler_unplug(hotplug_ctrl, dev, &error_abort); object_unparent(OBJECT(dev)); @@ -1454,7 +1454,7 @@ int spapr_pci_dt_populate(SpaprDrc *drc, SpaprMachineState *spapr, void *fdt, int *fdt_start_offset, Error **errp) { - HotplugHandler *plug_handler = qdev_get_hotplug_handler(drc->dev); + const HotplugHandler *plug_handler = qdev_get_hotplug_handler(drc->dev); SpaprPhbState *sphb = SPAPR_PCI_HOST_BRIDGE(plug_handler); PCIDevice *pdev = PCI_DEVICE(drc->dev); @@ -1521,7 +1521,7 @@ return true; } -static void spapr_pci_pre_plug(HotplugHandler *plug_handler, +static void spapr_pci_pre_plug(const HotplugHandler *plug_handler, DeviceState *plugged_dev, Error **errp) { SpaprPhbState *phb = SPAPR_PCI_HOST_BRIDGE(DEVICE(plug_handler)); @@ -1556,7 +1556,7 @@ } } -static void spapr_pci_plug(HotplugHandler *plug_handler, +static void spapr_pci_plug(const HotplugHandler *plug_handler, DeviceState *plugged_dev, Error **errp) { SpaprPhbState *phb = SPAPR_PCI_HOST_BRIDGE(DEVICE(plug_handler)); @@ -1614,7 +1614,7 @@ remove_drcs(phb, bus); } -static void spapr_pci_unplug(HotplugHandler *plug_handler, +static void spapr_pci_unplug(const HotplugHandler *plug_handler, DeviceState *plugged_dev, Error **errp) { SpaprPhbState *phb = SPAPR_PCI_HOST_BRIDGE(DEVICE(plug_handler)); @@ -1639,7 +1639,7 @@ qdev_unrealize(plugged_dev); } -static void spapr_pci_unplug_request(HotplugHandler *plug_handler, +static void spapr_pci_unplug_request(const HotplugHandler *plug_handler, DeviceState *plugged_dev, Error **errp) { SpaprPhbState *phb = SPAPR_PCI_HOST_BRIDGE(DEVICE(plug_handler));
diff --git a/hw/remote/machine.c b/hw/remote/machine.c index ced782f..f79f5a0 100644 --- a/hw/remote/machine.c +++ b/hw/remote/machine.c
@@ -111,7 +111,7 @@ s->auto_shutdown = true; } -static void remote_machine_dev_unplug_cb(HotplugHandler *hotplug_dev, +static void remote_machine_dev_unplug_cb(const HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { qdev_unrealize(dev);
diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c index f3a1cc5..d4b5018 100644 --- a/hw/riscv/virt.c +++ b/hw/riscv/virt.c
@@ -1088,8 +1088,8 @@ visit_type_OnOffAuto(v, name, &s->acpi, errp); } -static HotplugHandler *virt_machine_get_hotplug_handler(MachineState *machine, - DeviceState *dev) +static const HotplugHandler * +virt_machine_get_hotplug_handler(MachineState *machine, DeviceState *dev) { MachineClass *mc = MACHINE_GET_CLASS(machine); RISCVVirtState *s = RISCV_VIRT_MACHINE(machine); @@ -1104,7 +1104,7 @@ return NULL; } -static void virt_machine_device_plug_cb(HotplugHandler *hotplug_dev, +static void virt_machine_device_plug_cb(const HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { RISCVVirtState *s = RISCV_VIRT_MACHINE(hotplug_dev);
diff --git a/hw/s390x/ccw-device.h b/hw/s390x/ccw-device.h index 15f64cf..c303f1d 100644 --- a/hw/s390x/ccw-device.h +++ b/hw/s390x/ccw-device.h
@@ -37,7 +37,7 @@ struct CCWDeviceClass { DeviceClass parent_class; - void (*unplug)(HotplugHandler *, DeviceState *, Error **); + void (*unplug)(const HotplugHandler *, DeviceState *, Error **); bool (*realize)(CcwDevice *, Error **); void (*refill_ids)(CcwDevice *); };
diff --git a/hw/s390x/css-bridge.c b/hw/s390x/css-bridge.c index 440fefb..e7cd9ac 100644 --- a/hw/s390x/css-bridge.c +++ b/hw/s390x/css-bridge.c
@@ -26,7 +26,7 @@ * (including sending a channel report to the guest) and remove the * device from the virtual css bus. */ -static void ccw_device_unplug(HotplugHandler *hotplug_dev, +static void ccw_device_unplug(const HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { CcwDevice *ccw_dev = CCW_DEVICE(dev);
diff --git a/hw/s390x/s390-pci-bus.c b/hw/s390x/s390-pci-bus.c index eff980f..2eb4e8c 100644 --- a/hw/s390x/s390-pci-bus.c +++ b/hw/s390x/s390-pci-bus.c
@@ -158,7 +158,7 @@ static void s390_pci_perform_unplug(S390PCIBusDevice *pbdev) { - HotplugHandler *hotplug_ctrl; + const HotplugHandler *hotplug_ctrl; if (pbdev->pft == ZPCI_PFT_ISM) { notifier_remove(&pbdev->shutdown_notifier); @@ -1005,8 +1005,8 @@ return true; } -static void s390_pcihost_pre_plug(HotplugHandler *hotplug_dev, DeviceState *dev, - Error **errp) +static void s390_pcihost_pre_plug(const HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) { S390pciState *s = S390_PCI_HOST_BRIDGE(hotplug_dev); @@ -1079,7 +1079,7 @@ return 0; } -static void s390_pcihost_plug(HotplugHandler *hotplug_dev, DeviceState *dev, +static void s390_pcihost_plug(const HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { S390pciState *s = S390_PCI_HOST_BRIDGE(hotplug_dev); @@ -1216,7 +1216,7 @@ } } -static void s390_pcihost_unplug(HotplugHandler *hotplug_dev, DeviceState *dev, +static void s390_pcihost_unplug(const HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { S390pciState *s = S390_PCI_HOST_BRIDGE(hotplug_dev); @@ -1255,7 +1255,7 @@ } } -static void s390_pcihost_unplug_request(HotplugHandler *hotplug_dev, +static void s390_pcihost_unplug_request(const HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) {
diff --git a/hw/s390x/s390-virtio-ccw.c b/hw/s390x/s390-virtio-ccw.c index 1726677..7fb78d8 100644 --- a/hw/s390x/s390-virtio-ccw.c +++ b/hw/s390x/s390-virtio-ccw.c
@@ -343,8 +343,8 @@ } -static void s390_cpu_plug(HotplugHandler *hotplug_dev, - DeviceState *dev, Error **errp) +static void s390_cpu_plug(const HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) { ERRP_GUARD(); MachineState *ms = MACHINE(hotplug_dev); @@ -608,7 +608,7 @@ bql_lock(); } -static void s390_machine_device_pre_plug(HotplugHandler *hotplug_dev, +static void s390_machine_device_pre_plug(const HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { if (object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_MD_CCW)) { @@ -618,7 +618,7 @@ } } -static void s390_machine_device_plug(HotplugHandler *hotplug_dev, +static void s390_machine_device_plug(const HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { S390CcwMachineState *s390ms = S390_CCW_MACHINE(hotplug_dev); @@ -648,7 +648,7 @@ } } -static void s390_machine_device_unplug_request(HotplugHandler *hotplug_dev, +static void s390_machine_device_unplug_request(const HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { if (object_dynamic_cast(OBJECT(dev), TYPE_CPU)) { @@ -662,7 +662,7 @@ } } -static void s390_machine_device_unplug(HotplugHandler *hotplug_dev, +static void s390_machine_device_unplug(const HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { if (object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_MD_CCW)) { @@ -715,8 +715,8 @@ return ms->possible_cpus; } -static HotplugHandler *s390_get_hotplug_handler(MachineState *machine, - DeviceState *dev) +static const HotplugHandler *s390_get_hotplug_handler(MachineState *machine, + DeviceState *dev) { if (object_dynamic_cast(OBJECT(dev), TYPE_CPU) || object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_MD_CCW) ||
diff --git a/hw/s390x/virtio-ccw-md.c b/hw/s390x/virtio-ccw-md.c index 0370f58..70dbea8 100644 --- a/hw/s390x/virtio-ccw-md.c +++ b/hw/s390x/virtio-ccw-md.c
@@ -19,7 +19,7 @@ void virtio_ccw_md_pre_plug(VirtIOMDCcw *vmd, MachineState *ms, Error **errp) { DeviceState *dev = DEVICE(vmd); - HotplugHandler *bus_handler = qdev_get_bus_hotplug_handler(dev); + const HotplugHandler *bus_handler = qdev_get_bus_hotplug_handler(dev); MemoryDeviceState *md = MEMORY_DEVICE(vmd); Error *local_err = NULL; @@ -48,7 +48,7 @@ void virtio_ccw_md_plug(VirtIOMDCcw *vmd, MachineState *ms, Error **errp) { DeviceState *dev = DEVICE(vmd); - HotplugHandler *bus_handler = qdev_get_bus_hotplug_handler(dev); + const HotplugHandler *bus_handler = qdev_get_bus_hotplug_handler(dev); MemoryDeviceState *md = MEMORY_DEVICE(vmd); Error *local_err = NULL; @@ -72,8 +72,8 @@ { VirtIOMDCcwClass *vmdc = VIRTIO_MD_CCW_GET_CLASS(vmd); DeviceState *dev = DEVICE(vmd); - HotplugHandler *bus_handler = qdev_get_bus_hotplug_handler(dev); - HotplugHandlerClass *hdc; + const HotplugHandler *bus_handler = qdev_get_bus_hotplug_handler(dev); + const HotplugHandlerClass *hdc; Error *local_err = NULL; if (!vmdc->unplug_request_check) { @@ -112,7 +112,7 @@ void virtio_ccw_md_unplug(VirtIOMDCcw *vmd, MachineState *ms, Error **errp) { DeviceState *dev = DEVICE(vmd); - HotplugHandler *bus_handler = qdev_get_bus_hotplug_handler(dev); + const HotplugHandler *bus_handler = qdev_get_bus_hotplug_handler(dev); MemoryDeviceState *md = MEMORY_DEVICE(vmd); Error *local_err = NULL;
diff --git a/hw/s390x/virtio-ccw.c b/hw/s390x/virtio-ccw.c index d82874e..30fb2c0 100644 --- a/hw/s390x/virtio-ccw.c +++ b/hw/s390x/virtio-ccw.c
@@ -1235,7 +1235,7 @@ virtio_ccw_device_unrealize(_dev); } -static void virtio_ccw_busdev_unplug(HotplugHandler *hotplug_dev, +static void virtio_ccw_busdev_unplug(const HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { VirtioCcwDevice *_dev = to_virtio_ccw_dev_fast(dev);
diff --git a/hw/scsi/scsi-disk.c b/hw/scsi/scsi-disk.c index a42f7d8..deb33ac 100644 --- a/hw/scsi/scsi-disk.c +++ b/hw/scsi/scsi-disk.c
@@ -318,6 +318,13 @@ return true; case BLOCK_ERROR_ACTION_IGNORE: + /* + * The caller may have already finalized the accounting cookie + * (e.g. scsi_dma_complete() for ret < 0), but calling + * block_acct_done() afterwards is fine (just a no-op). Just + * make sure all cookies are indeed accounted at some point. + */ + block_acct_done(blk_get_stats(s->qdev.conf.blk), &r->acct); return false; case BLOCK_ERROR_ACTION_STOP:
diff --git a/hw/scsi/vhost-scsi-common.c b/hw/scsi/vhost-scsi-common.c index e19800a..e546a6d 100644 --- a/hw/scsi/vhost-scsi-common.c +++ b/hw/scsi/vhost-scsi-common.c
@@ -108,7 +108,7 @@ VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus); int ret = 0; - ret = vhost_dev_stop(&vsc->dev, vdev, true); + ret = vhost_dev_stop(&vsc->dev, vdev, true, false); if (k->set_guest_notifiers) { int r = k->set_guest_notifiers(qbus->parent, vsc->dev.nvqs, false);
diff --git a/hw/scsi/virtio-scsi-dataplane.c b/hw/scsi/virtio-scsi-dataplane.c index 95f13fb..bcb6e44 100644 --- a/hw/scsi/virtio-scsi-dataplane.c +++ b/hw/scsi/virtio-scsi-dataplane.c
@@ -230,7 +230,7 @@ fail_guest_notifiers: s->dataplane_fenced = true; s->dataplane_starting = false; - s->dataplane_started = true; + s->dataplane_started = false; return -ENOSYS; }
diff --git a/hw/scsi/virtio-scsi.c b/hw/scsi/virtio-scsi.c index bf64d12..d7e2662 100644 --- a/hw/scsi/virtio-scsi.c +++ b/hw/scsi/virtio-scsi.c
@@ -274,6 +274,10 @@ assert(n < vs->conf.num_queues); req = qemu_get_virtqueue_element(vdev, f, sizeof(VirtIOSCSIReq) + vs->cdb_size); + if (!req) { + error_report("Failed to restore virtio-scsi request"); + return NULL; + } virtio_scsi_init_req(s, vs->cmd_vqs[n], req); if (virtio_scsi_parse_req(req, sizeof(VirtIOSCSICmdReq) + vs->cdb_size, @@ -1140,14 +1144,14 @@ } } -static void virtio_scsi_pre_hotplug(HotplugHandler *hotplug_dev, +static void virtio_scsi_pre_hotplug(const HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { SCSIDevice *sd = SCSI_DEVICE(dev); sd->hba_supports_iothread = true; } -static void virtio_scsi_hotplug(HotplugHandler *hotplug_dev, DeviceState *dev, +static void virtio_scsi_hotplug(const HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { VirtIODevice *vdev = VIRTIO_DEVICE(hotplug_dev); @@ -1179,7 +1183,7 @@ } } -static void virtio_scsi_hotunplug(HotplugHandler *hotplug_dev, DeviceState *dev, +static void virtio_scsi_hotunplug(const HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { VirtIODevice *vdev = VIRTIO_DEVICE(hotplug_dev);
diff --git a/hw/scsi/vmw_pvscsi.c b/hw/scsi/vmw_pvscsi.c index 05f9317..77c9b84 100644 --- a/hw/scsi/vmw_pvscsi.c +++ b/hw/scsi/vmw_pvscsi.c
@@ -612,7 +612,7 @@ } static void -pvscsi_hotplug(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) +pvscsi_hotplug(const HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { PVSCSIState *s = PVSCSI(hotplug_dev); @@ -620,7 +620,7 @@ } static void -pvscsi_hot_unplug(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) +pvscsi_hot_unplug(const HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { PVSCSIState *s = PVSCSI(hotplug_dev);
diff --git a/hw/virtio/vdpa-dev.c b/hw/virtio/vdpa-dev.c index 6dc684a..7e5cc04 100644 --- a/hw/virtio/vdpa-dev.c +++ b/hw/virtio/vdpa-dev.c
@@ -301,7 +301,7 @@ return; } - vhost_dev_stop(&s->dev, vdev, false); + vhost_dev_stop(&s->dev, vdev, false, false); ret = k->set_guest_notifiers(qbus->parent, s->dev.nvqs, false); if (ret < 0) {
diff --git a/hw/virtio/vhost-user-base.c b/hw/virtio/vhost-user-base.c index 478ec68..1a37701 100644 --- a/hw/virtio/vhost-user-base.c +++ b/hw/virtio/vhost-user-base.c
@@ -78,7 +78,7 @@ return 0; } - ret = vhost_dev_stop(&vub->vhost_dev, vdev, true); + ret = vhost_dev_stop(&vub->vhost_dev, vdev, true, false); err = k->set_guest_notifiers(qbus->parent, vub->vhost_dev.nvqs, false); if (err < 0) {
diff --git a/hw/virtio/vhost-user-fs.c b/hw/virtio/vhost-user-fs.c index 2099939..0d88428 100644 --- a/hw/virtio/vhost-user-fs.c +++ b/hw/virtio/vhost-user-fs.c
@@ -111,7 +111,7 @@ return 0; } - ret = vhost_dev_stop(&fs->vhost_dev, vdev, true); + ret = vhost_dev_stop(&fs->vhost_dev, vdev, true, false); err = k->set_guest_notifiers(qbus->parent, fs->vhost_dev.nvqs, false); if (err < 0) {
diff --git a/hw/virtio/vhost-user-scmi.c b/hw/virtio/vhost-user-scmi.c index 02dc088..c4692bd 100644 --- a/hw/virtio/vhost-user-scmi.c +++ b/hw/virtio/vhost-user-scmi.c
@@ -101,7 +101,7 @@ return 0; } - ret = vhost_dev_stop(vhost_dev, vdev, true); + ret = vhost_dev_stop(vhost_dev, vdev, true, false); err = k->set_guest_notifiers(qbus->parent, vhost_dev->nvqs, false); if (err < 0) {
diff --git a/hw/virtio/vhost-user.c b/hw/virtio/vhost-user.c index 2881cec..65889b0 100644 --- a/hw/virtio/vhost-user.c +++ b/hw/virtio/vhost-user.c
@@ -101,6 +101,7 @@ VHOST_USER_SET_DEVICE_STATE_FD = 42, VHOST_USER_CHECK_DEVICE_STATE = 43, VHOST_USER_GET_SHMEM_CONFIG = 44, + VHOST_USER_GET_VRING_BASE_SKIP_DRAIN = 45, VHOST_USER_MAX } VhostUserRequest; @@ -167,6 +168,7 @@ VHOST_USER_CASE(GET_SHARED_OBJECT) VHOST_USER_CASE(SET_DEVICE_STATE_FD) VHOST_USER_CASE(CHECK_DEVICE_STATE) + VHOST_USER_CASE(GET_VRING_BASE_SKIP_DRAIN) default: return "<unknown>"; } @@ -1402,12 +1404,18 @@ return g_ptr_array_index(u->notifiers, idx); } -static int vhost_user_get_vring_base(struct vhost_dev *dev, - struct vhost_vring_state *ring) +static int get_vring_base(struct vhost_dev *dev, + struct vhost_vring_state *ring, + bool skip_drain) { int ret; + int request = VHOST_USER_GET_VRING_BASE; + if (skip_drain) { + request = VHOST_USER_GET_VRING_BASE_SKIP_DRAIN; + } + VhostUserMsg msg = { - .hdr.request = VHOST_USER_GET_VRING_BASE, + .hdr.request = request, .hdr.flags = VHOST_USER_VERSION, .payload.state = *ring, .hdr.size = sizeof(msg.payload.state), @@ -1427,9 +1435,9 @@ return ret; } - if (msg.hdr.request != VHOST_USER_GET_VRING_BASE) { + if (msg.hdr.request != request) { error_report("Received unexpected msg type. Expected %d received %d", - VHOST_USER_GET_VRING_BASE, msg.hdr.request); + request, msg.hdr.request); return -EPROTO; } @@ -1443,6 +1451,25 @@ return 0; } +static int vhost_user_get_vring_base(struct vhost_dev *dev, + struct vhost_vring_state *ring) +{ + return get_vring_base(dev, ring, false); +} + +static int vhost_user_get_vring_base_skip_drain(struct vhost_dev *dev, + struct vhost_vring_state *ring) +{ + 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; + } + + return get_vring_base(dev, ring, true); +} + static int vhost_set_vring_file(struct vhost_dev *dev, VhostUserRequest request, struct vhost_vring_file *file) @@ -2569,13 +2596,18 @@ } } - if (!u->user->supports_inflight_migration || - !virtio_has_feature(protocol_features, + if (!virtio_has_feature(protocol_features, VHOST_USER_PROTOCOL_F_INFLIGHT_SHMFD)) { protocol_features &= ~(1ULL << VHOST_USER_PROTOCOL_F_GET_VRING_BASE_INFLIGHT); } + if (!virtio_has_feature(protocol_features, + VHOST_USER_PROTOCOL_F_GET_VRING_BASE_INFLIGHT)) { + protocol_features &= ~(1ULL << + VHOST_USER_PROTOCOL_F_GET_VRING_BASE_SKIP_DRAIN); + } + /* final set of protocol features */ u->protocol_features = protocol_features; err = vhost_user_set_protocol_features(dev, u->protocol_features); @@ -3434,6 +3466,7 @@ .vhost_set_vring_num = vhost_user_set_vring_num, .vhost_set_vring_base = vhost_user_set_vring_base, .vhost_get_vring_base = vhost_user_get_vring_base, + .vhost_get_vring_base_skip_drain = vhost_user_get_vring_base_skip_drain, .vhost_set_vring_kick = vhost_user_set_vring_kick, .vhost_set_vring_call = vhost_user_set_vring_call, .vhost_set_vring_err = vhost_user_set_vring_err,
diff --git a/hw/virtio/vhost-vsock-common.c b/hw/virtio/vhost-vsock-common.c index b79f4c9..4ef0376 100644 --- a/hw/virtio/vhost-vsock-common.c +++ b/hw/virtio/vhost-vsock-common.c
@@ -106,7 +106,7 @@ return 0; } - ret = vhost_dev_stop(&vvc->vhost_dev, vdev, true); + ret = vhost_dev_stop(&vvc->vhost_dev, vdev, true, false); err = k->set_guest_notifiers(qbus->parent, vvc->vhost_dev.nvqs, false); if (err < 0) {
diff --git a/hw/virtio/vhost.c b/hw/virtio/vhost.c index 371dca1..68a8e40 100644 --- a/hw/virtio/vhost.c +++ b/hw/virtio/vhost.c
@@ -23,6 +23,7 @@ #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/qemu-file-types.h" @@ -1471,8 +1472,10 @@ * will do it later. */ if (!vdev->use_guest_notifier_mask) { - /* TODO: check and handle errors. */ - vhost_virtqueue_mask(dev, vdev, idx, false); + r = vhost_virtqueue_mask(dev, vdev, idx, false); + if (r < 0) { + goto fail; + } } if (k->query_guest_notifiers && @@ -1497,8 +1500,13 @@ static int do_vhost_virtqueue_stop(struct vhost_dev *dev, struct VirtIODevice *vdev, struct vhost_virtqueue *vq, - unsigned idx, bool force) + 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, @@ -1513,7 +1521,11 @@ } if (!force) { - r = dev->vhost_ops->vhost_get_vring_base(dev, &state); + if (!skip_drain) { + r = dev->vhost_ops->vhost_get_vring_base(dev, &state); + } else { + r = dev->vhost_ops->vhost_get_vring_base_skip_drain(dev, &state); + } if (r < 0) { VHOST_OPS_DEBUG(r, "vhost VQ %u ring restore failed: %d", idx, r); } @@ -1548,9 +1560,10 @@ int vhost_virtqueue_stop(struct vhost_dev *dev, struct VirtIODevice *vdev, struct vhost_virtqueue *vq, - unsigned idx) + unsigned idx, + bool skip_drain) { - return do_vhost_virtqueue_stop(dev, vdev, vq, idx, false); + return do_vhost_virtqueue_stop(dev, vdev, vq, idx, false, skip_drain); } static int vhost_virtqueue_set_busyloop_timeout(struct vhost_dev *dev, @@ -1919,7 +1932,7 @@ } /* Mask/unmask events from this vq. */ -void vhost_virtqueue_mask(struct vhost_dev *hdev, VirtIODevice *vdev, int n, +int vhost_virtqueue_mask(struct vhost_dev *hdev, VirtIODevice *vdev, int n, bool mask) { struct VirtQueue *vvq = virtio_get_queue(vdev, n); @@ -1940,7 +1953,9 @@ r = hdev->vhost_ops->vhost_set_vring_call(hdev, &file); if (r < 0) { error_report("vhost_set_vring_call failed %d", -r); + return r; } + return 0; } bool vhost_config_pending(struct vhost_dev *hdev) @@ -2303,7 +2318,8 @@ vhost_virtqueue_stop(hdev, vdev, hdev->vqs + i, - hdev->vq_index + i); + hdev->vq_index + i, + false); } fail_mem: @@ -2318,7 +2334,7 @@ /* Host notifiers must be enabled at this point. */ static int do_vhost_dev_stop(struct vhost_dev *hdev, VirtIODevice *vdev, - bool vrings, bool force) + bool vrings, bool force, bool skip_drain) { int i; int rc = 0; @@ -2345,7 +2361,8 @@ vdev, hdev->vqs + i, hdev->vq_index + i, - force); + force, + skip_drain); } if (hdev->vhost_ops->vhost_reset_status) { hdev->vhost_ops->vhost_reset_status(hdev); @@ -2367,15 +2384,16 @@ return rc; } -int vhost_dev_stop(struct vhost_dev *hdev, VirtIODevice *vdev, bool vrings) +int vhost_dev_stop(struct vhost_dev *hdev, VirtIODevice *vdev, bool vrings, + bool skip_drain) { - return do_vhost_dev_stop(hdev, vdev, vrings, false); + return do_vhost_dev_stop(hdev, vdev, vrings, false, skip_drain); } int vhost_dev_force_stop(struct vhost_dev *hdev, VirtIODevice *vdev, bool vrings) { - return do_vhost_dev_stop(hdev, vdev, vrings, true); + return do_vhost_dev_stop(hdev, vdev, vrings, true, false); } int vhost_net_set_backend(struct vhost_dev *hdev,
diff --git a/hw/virtio/virtio-balloon.c b/hw/virtio/virtio-balloon.c index 4c5f486..07d519c 100644 --- a/hw/virtio/virtio-balloon.c +++ b/hw/virtio/virtio-balloon.c
@@ -35,6 +35,7 @@ #include "system/reset.h" #include "hw/virtio/virtio-bus.h" #include "hw/virtio/virtio-access.h" +#include "qemu/aio-wait.h" #define BALLOON_PAGE_SIZE (1 << VIRTIO_BALLOON_PFN_SHIFT) @@ -519,6 +520,9 @@ int i; while (dev->block_iothread) { + if (dev->free_page_hint_status == FREE_PAGE_HINT_S_UNREALIZE) { + return false; + } qemu_cond_wait(&dev->free_page_cond, &dev->free_page_lock); } @@ -912,6 +916,11 @@ qemu_register_resettable(OBJECT(dev)); } +static void dummy_bh(void *opaque) +{ + /* Do nothing */ +} + static void virtio_balloon_device_unrealize(DeviceState *dev) { VirtIODevice *vdev = VIRTIO_DEVICE(dev); @@ -919,9 +928,17 @@ qemu_unregister_resettable(OBJECT(dev)); if (s->free_page_bh) { + AioContext *ctx = iothread_get_aio_context(s->iothread); + qemu_bh_delete(s->free_page_bh); + + qemu_mutex_lock(&s->free_page_lock); + s->free_page_hint_status = FREE_PAGE_HINT_S_UNREALIZE; + qemu_cond_signal(&s->free_page_cond); + qemu_mutex_unlock(&s->free_page_lock); + aio_wait_bh_oneshot(ctx, dummy_bh, NULL); + object_unref(OBJECT(s->iothread)); - virtio_balloon_free_page_stop(s); precopy_remove_notifier(&s->free_page_hint_notify); } balloon_stats_destroy_timer(s);
diff --git a/hw/virtio/virtio-crypto.c b/hw/virtio/virtio-crypto.c index 79e2acb..85c7279 100644 --- a/hw/virtio/virtio-crypto.c +++ b/hw/virtio/virtio-crypto.c
@@ -1114,10 +1114,9 @@ VirtIODevice *vdev = VIRTIO_DEVICE(dev); VirtIOCrypto *vcrypto = VIRTIO_CRYPTO(dev); VirtIOCryptoQueue *q; - int i, max_queues; + int i; - max_queues = vcrypto->multiqueue ? vcrypto->max_queues : 1; - for (i = 0; i < max_queues; i++) { + for (i = 0; i < vcrypto->max_queues; i++) { virtio_delete_queue(vcrypto->vqs[i].dataq); q = &vcrypto->vqs[i]; qemu_bh_delete(q->dataq_bh);
diff --git a/hw/virtio/virtio-iommu.c b/hw/virtio/virtio-iommu.c index 533bd50..2088150 100644 --- a/hw/virtio/virtio-iommu.c +++ b/hw/virtio/virtio-iommu.c
@@ -212,6 +212,10 @@ { uint64_t delta = virt_end - virt_start; + if (virt_end < virt_start) { + return; + } + event->entry.iova = virt_start; event->entry.addr_mask = delta; @@ -807,6 +811,10 @@ return VIRTIO_IOMMU_S_INVAL; } + if (virt_end < virt_start) { + return VIRTIO_IOMMU_S_INVAL; + } + domain = g_tree_lookup(s->domains, GUINT_TO_POINTER(domain_id)); if (!domain) { return VIRTIO_IOMMU_S_NOENT; @@ -857,6 +865,10 @@ trace_virtio_iommu_unmap(domain_id, virt_start, virt_end); + if (virt_end < virt_start) { + return VIRTIO_IOMMU_S_INVAL; + } + domain = g_tree_lookup(s->domains, GUINT_TO_POINTER(domain_id)); if (!domain) { return VIRTIO_IOMMU_S_NOENT; @@ -879,7 +891,10 @@ virtio_iommu_notify_unmap(ep->iommu_mr, current_low, current_high); } - g_tree_remove(domain->mappings, iter_key); + if (!g_tree_remove(domain->mappings, iter_key)) { + ret = VIRTIO_IOMMU_S_DEVERR; + break; + } trace_virtio_iommu_unmap_done(domain_id, current_low, current_high); } else { ret = VIRTIO_IOMMU_S_RANGE;
diff --git a/hw/virtio/virtio-md-pci.c b/hw/virtio/virtio-md-pci.c index 9278b32..97ea0ec 100644 --- a/hw/virtio/virtio-md-pci.c +++ b/hw/virtio/virtio-md-pci.c
@@ -19,7 +19,7 @@ void virtio_md_pci_pre_plug(VirtIOMDPCI *vmd, MachineState *ms, Error **errp) { DeviceState *dev = DEVICE(vmd); - HotplugHandler *bus_handler = qdev_get_bus_hotplug_handler(dev); + const HotplugHandler *bus_handler = qdev_get_bus_hotplug_handler(dev); MemoryDeviceState *md = MEMORY_DEVICE(vmd); Error *local_err = NULL; @@ -47,7 +47,7 @@ void virtio_md_pci_plug(VirtIOMDPCI *vmd, MachineState *ms, Error **errp) { DeviceState *dev = DEVICE(vmd); - HotplugHandler *bus_handler = qdev_get_bus_hotplug_handler(dev); + const HotplugHandler *bus_handler = qdev_get_bus_hotplug_handler(dev); MemoryDeviceState *md = MEMORY_DEVICE(vmd); Error *local_err = NULL; @@ -71,8 +71,8 @@ { VirtIOMDPCIClass *vmdc = VIRTIO_MD_PCI_GET_CLASS(vmd); DeviceState *dev = DEVICE(vmd); - HotplugHandler *bus_handler = qdev_get_bus_hotplug_handler(dev); - HotplugHandlerClass *hdc; + const HotplugHandler *bus_handler = qdev_get_bus_hotplug_handler(dev); + const HotplugHandlerClass *hdc; Error *local_err = NULL; if (!vmdc->unplug_request_check) { @@ -110,7 +110,7 @@ void virtio_md_pci_unplug(VirtIOMDPCI *vmd, MachineState *ms, Error **errp) { DeviceState *dev = DEVICE(vmd); - HotplugHandler *bus_handler = qdev_get_bus_hotplug_handler(dev); + const HotplugHandler *bus_handler = qdev_get_bus_hotplug_handler(dev); MemoryDeviceState *md = MEMORY_DEVICE(vmd); Error *local_err = NULL;
diff --git a/hw/virtio/virtio-pci.c b/hw/virtio/virtio-pci.c index 6f5db5f..c73f1f5 100644 --- a/hw/virtio/virtio-pci.c +++ b/hw/virtio/virtio-pci.c
@@ -1692,6 +1692,13 @@ break; case VIRTIO_PCI_COMMON_Q_ENABLE: if (val == 1) { + if (proxy->vqs[vdev->queue_sel].enabled) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: queue %d already enabled - " + "reset queue before re-enabling\n", + __func__, vdev->queue_sel); + break; + } virtio_queue_set_num(vdev, vdev->queue_sel, proxy->vqs[vdev->queue_sel].num); virtio_queue_set_rings(vdev, vdev->queue_sel,
diff --git a/hw/virtio/virtio-rtc.c b/hw/virtio/virtio-rtc.c index 32de9c1..0dcbe94 100644 --- a/hw/virtio/virtio-rtc.c +++ b/hw/virtio/virtio-rtc.c
@@ -62,7 +62,8 @@ resp.head.status = VIRTIO_RTC_S_ENODEV; } else { resp.head.status = VIRTIO_RTC_S_OK; - resp.type = VIRTIO_RTC_CLOCK_UTC; + resp.type = VIRTIO_RTC_CLOCK_UTC_SMEARED; + resp.leap_second_smearing = VIRTIO_RTC_SMEAR_UNSPECIFIED; } written = iov_from_buf(elem->in_sg, elem->in_num, 0, &resp, sizeof(resp));
diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c index daa5607..c9f9e5b 100644 --- a/hw/virtio/virtio.c +++ b/hw/virtio/virtio.c
@@ -198,7 +198,8 @@ [VIRTIO_ID_AUDIO_POLICY] = "virtio-audio-pol", [VIRTIO_ID_BT] = "virtio-bluetooth", [VIRTIO_ID_GPIO] = "virtio-gpio", - [VIRTIO_ID_SPI] = "virtio-spi" + [VIRTIO_ID_SPI] = "virtio-spi", + [VIRTIO_ID_MEDIA] = "virtio-media", }; static const char *virtio_id_to_name(uint16_t device_id) @@ -716,26 +717,6 @@ return (avail != used) && (avail == wrap_counter); } -/* Fetch avail_idx from VQ memory only when we really need to know if - * guest has added some buffers. - * Called within rcu_read_lock(). */ -static int virtio_queue_empty_rcu(VirtQueue *vq) -{ - if (virtio_device_disabled(vq->vdev)) { - return 1; - } - - if (unlikely(!vq->vring.avail)) { - return 1; - } - - if (vq->shadow_avail_idx != vq->last_avail_idx) { - return 0; - } - - return vring_avail_idx(vq) == vq->last_avail_idx; -} - static int virtio_queue_split_empty(VirtQueue *vq) { bool empty; @@ -1680,36 +1661,56 @@ } } -static void virtqueue_map_iovec(VirtIODevice *vdev, struct iovec *sg, +static bool virtqueue_map_iovec(VirtIODevice *vdev, struct iovec *sg, hwaddr *addr, unsigned int num_sg, bool is_write) { unsigned int i; hwaddr len; + DMADirection dir = is_write ? DMA_DIRECTION_FROM_DEVICE : + DMA_DIRECTION_TO_DEVICE; for (i = 0; i < num_sg; i++) { len = sg[i].iov_len; - sg[i].iov_base = dma_memory_map(vdev->dma_as, - addr[i], &len, is_write ? - DMA_DIRECTION_FROM_DEVICE : - DMA_DIRECTION_TO_DEVICE, - MEMTXATTRS_UNSPECIFIED); + sg[i].iov_base = dma_memory_map(vdev->dma_as, addr[i], &len, + dir, MEMTXATTRS_UNSPECIFIED); if (!sg[i].iov_base) { error_report("virtio: error trying to map MMIO memory"); - exit(1); + goto err_undo_map; } if (len != sg[i].iov_len) { error_report("virtio: unexpected memory split"); - exit(1); + dma_memory_unmap(vdev->dma_as, sg[i].iov_base, len, dir, 0); + goto err_undo_map; } } + return true; + +err_undo_map: + while (i-- > 0) { + dma_memory_unmap(vdev->dma_as, sg[i].iov_base, sg[i].iov_len, + dir, 0); + } + return false; } -void virtqueue_map(VirtIODevice *vdev, VirtQueueElement *elem) +bool virtqueue_map(VirtIODevice *vdev, VirtQueueElement *elem) { - virtqueue_map_iovec(vdev, elem->in_sg, elem->in_addr, elem->in_num, true); - virtqueue_map_iovec(vdev, elem->out_sg, elem->out_addr, elem->out_num, - false); + if (!virtqueue_map_iovec(vdev, elem->in_sg, elem->in_addr, + elem->in_num, true)) { + return false; + } + if (!virtqueue_map_iovec(vdev, elem->out_sg, elem->out_addr, + elem->out_num, false)) { + unsigned int i; + for (i = 0; i < elem->in_num; i++) { + dma_memory_unmap(vdev->dma_as, elem->in_sg[i].iov_base, + elem->in_sg[i].iov_len, + DMA_DIRECTION_FROM_DEVICE, 0); + } + return false; + } + return true; } static void *virtqueue_alloc_element(size_t sz, unsigned out_num, unsigned in_num) @@ -1752,12 +1753,14 @@ address_space_cache_init_empty(&indirect_desc_cache); RCU_READ_LOCK_GUARD(); - if (virtio_queue_empty_rcu(vq)) { + if (unlikely(!vq->vring.avail)) { goto done; } - /* Needed after virtio_queue_empty(), see comment in - * virtqueue_num_heads(). */ - smp_rmb(); + + rc = virtqueue_num_heads(vq, vq->last_avail_idx); + if (rc <= 0) { + goto done; + } /* When we start there are none of either input nor output. */ out_num = in_num = elem_entries = 0; @@ -2206,7 +2209,10 @@ qemu_get_be32s(f, &elem->ndescs); } - virtqueue_map(vdev, elem); + if (!virtqueue_map(vdev, elem)) { + g_free(elem); + return NULL; + } return elem; } @@ -3561,6 +3567,9 @@ return -1; } qemu_get_byte(f); + if (qemu_file_get_error(f)) { + return -1; + } config_len--; }
diff --git a/hw/xen/xen-bus.c b/hw/xen/xen-bus.c index 1762816..8def3bb 100644 --- a/hw/xen/xen-bus.c +++ b/hw/xen/xen-bus.c
@@ -374,7 +374,7 @@ g_free(key); } -static void xen_bus_unplug_request(HotplugHandler *hotplug, +static void xen_bus_unplug_request(const HotplugHandler *hotplug, DeviceState *dev, Error **errp) {
diff --git a/include/hw/acpi/cpu.h b/include/hw/acpi/cpu.h index 04c821d..3664ac7 100644 --- a/include/hw/acpi/cpu.h +++ b/include/hw/acpi/cpu.h
@@ -39,10 +39,10 @@ AcpiCpuStatus *devs; } CPUHotplugState; -void acpi_cpu_plug_cb(HotplugHandler *hotplug_dev, +void acpi_cpu_plug_cb(const HotplugHandler *hotplug_dev, CPUHotplugState *cpu_st, DeviceState *dev, Error **errp); -void acpi_cpu_unplug_request_cb(HotplugHandler *hotplug_dev, +void acpi_cpu_unplug_request_cb(const HotplugHandler *hotplug_dev, CPUHotplugState *cpu_st, DeviceState *dev, Error **errp);
diff --git a/include/hw/acpi/cpu_hotplug.h b/include/hw/acpi/cpu_hotplug.h index 5b670b0..bac40cc 100644 --- a/include/hw/acpi/cpu_hotplug.h +++ b/include/hw/acpi/cpu_hotplug.h
@@ -25,7 +25,7 @@ uint8_t sts[ACPI_GPE_PROC_LEN]; } AcpiCpuHotplug; -void legacy_acpi_cpu_plug_cb(HotplugHandler *hotplug_dev, +void legacy_acpi_cpu_plug_cb(const HotplugHandler *hotplug_dev, AcpiCpuHotplug *g, DeviceState *dev, Error **errp); void legacy_acpi_cpu_hotplug_init(MemoryRegion *parent, Object *owner,
diff --git a/include/hw/acpi/generic_event_device.h b/include/hw/acpi/generic_event_device.h index 7cbfb5f..42a8033 100644 --- a/include/hw/acpi/generic_event_device.h +++ b/include/hw/acpi/generic_event_device.h
@@ -136,7 +136,8 @@ ResettablePhases parent_phases; } AcpiGedClass; -void build_ged_aml(Aml *table, const char* name, HotplugHandler *hotplug_dev, +void build_ged_aml(Aml *table, const char *name, + const HotplugHandler *hotplug_dev, uint32_t ged_irq, AmlRegionSpace rs, hwaddr ged_base); void acpi_dsdt_add_power_button(Aml *scope);
diff --git a/include/hw/acpi/ich9.h b/include/hw/acpi/ich9.h index 6d14544..6956b2b 100644 --- a/include/hw/acpi/ich9.h +++ b/include/hw/acpi/ich9.h
@@ -85,15 +85,15 @@ void ich9_pm_add_class_properties(ObjectClass *oc, ptrdiff_t pm_offset); -void ich9_pm_device_pre_plug_cb(HotplugHandler *hotplug_dev, DeviceState *dev, +void ich9_pm_device_pre_plug_cb(const HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp); -void ich9_pm_device_plug_cb(HotplugHandler *hotplug_dev, DeviceState *dev, +void ich9_pm_device_plug_cb(const HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp); -void ich9_pm_device_unplug_request_cb(HotplugHandler *hotplug_dev, +void ich9_pm_device_unplug_request_cb(const HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp); -void ich9_pm_device_unplug_cb(HotplugHandler *hotplug_dev, DeviceState *dev, +void ich9_pm_device_unplug_cb(const HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp); -bool ich9_pm_is_hotpluggable_bus(HotplugHandler *hotplug_dev, BusState *bus); +bool ich9_pm_is_hotpluggable_bus(const HotplugHandler *hotplug_dev, BusState *bus); void ich9_pm_ospm_status(AcpiDeviceIf *adev, ACPIOSTInfoList ***list); #endif /* HW_ACPI_ICH9_H */
diff --git a/include/hw/acpi/memory_hotplug.h b/include/hw/acpi/memory_hotplug.h index eb7f460..6e4f82b 100644 --- a/include/hw/acpi/memory_hotplug.h +++ b/include/hw/acpi/memory_hotplug.h
@@ -36,9 +36,9 @@ void acpi_memory_hotplug_init(MemoryRegion *as, Object *owner, MemHotplugState *state, hwaddr io_base); -void acpi_memory_plug_cb(HotplugHandler *hotplug_dev, MemHotplugState *mem_st, +void acpi_memory_plug_cb(const HotplugHandler *hotplug_dev, MemHotplugState *mem_st, DeviceState *dev, Error **errp); -void acpi_memory_unplug_request_cb(HotplugHandler *hotplug_dev, +void acpi_memory_unplug_request_cb(const HotplugHandler *hotplug_dev, MemHotplugState *mem_st, DeviceState *dev, Error **errp); void acpi_memory_unplug_cb(MemHotplugState *mem_st,
diff --git a/include/hw/acpi/pcihp.h b/include/hw/acpi/pcihp.h index efce5fd..21c0b72 100644 --- a/include/hw/acpi/pcihp.h +++ b/include/hw/acpi/pcihp.h
@@ -66,13 +66,13 @@ MemoryRegion *io, uint16_t io_base); bool acpi_pcihp_is_hotpluggable_bus(AcpiPciHpState *s, BusState *bus); -void acpi_pcihp_device_pre_plug_cb(HotplugHandler *hotplug_dev, +void acpi_pcihp_device_pre_plug_cb(const HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp); -void acpi_pcihp_device_plug_cb(HotplugHandler *hotplug_dev, AcpiPciHpState *s, +void acpi_pcihp_device_plug_cb(const HotplugHandler *hotplug_dev, AcpiPciHpState *s, DeviceState *dev, Error **errp); -void acpi_pcihp_device_unplug_cb(HotplugHandler *hotplug_dev, AcpiPciHpState *s, +void acpi_pcihp_device_unplug_cb(const HotplugHandler *hotplug_dev, AcpiPciHpState *s, DeviceState *dev, Error **errp); -void acpi_pcihp_device_unplug_request_cb(HotplugHandler *hotplug_dev, +void acpi_pcihp_device_unplug_request_cb(const HotplugHandler *hotplug_dev, AcpiPciHpState *s, DeviceState *dev, Error **errp);
diff --git a/include/hw/core/boards.h b/include/hw/core/boards.h index a436d48..dba465e 100644 --- a/include/hw/core/boards.h +++ b/include/hw/core/boards.h
@@ -322,8 +322,8 @@ SMPCompatProps smp_props; const char *default_ram_id; - HotplugHandler *(*get_hotplug_handler)(MachineState *machine, - DeviceState *dev); + const HotplugHandler *(*get_hotplug_handler)(MachineState *machine, + DeviceState *dev); bool (*hotplug_allowed)(MachineState *state, DeviceState *dev, Error **errp); CpuInstanceProperties (*cpu_index_to_instance_props)(MachineState *machine,
diff --git a/include/hw/core/hotplug.h b/include/hw/core/hotplug.h index a9840ed..0c69be6 100644 --- a/include/hw/core/hotplug.h +++ b/include/hw/core/hotplug.h
@@ -30,7 +30,7 @@ * @plugged_dev: a device that has been (un)plugged * @errp: returns an error if this function fails */ -typedef void (*hotplug_fn)(HotplugHandler *plug_handler, +typedef void (*hotplug_fn)(const HotplugHandler *plug_handler, DeviceState *plugged_dev, Error **errp); /** @@ -59,7 +59,8 @@ hotplug_fn plug; hotplug_fn unplug_request; hotplug_fn unplug; - bool (*is_hotpluggable_bus)(HotplugHandler *plug_handler, BusState *bus); + bool (*is_hotpluggable_bus)(const HotplugHandler *plug_handler, + BusState *bus); }; /** @@ -67,7 +68,7 @@ * * Call #HotplugHandlerClass.plug callback of @plug_handler. */ -void hotplug_handler_plug(HotplugHandler *plug_handler, +void hotplug_handler_plug(const HotplugHandler *plug_handler, DeviceState *plugged_dev, Error **errp); @@ -76,7 +77,7 @@ * * Call #HotplugHandlerClass.pre_plug callback of @plug_handler. */ -void hotplug_handler_pre_plug(HotplugHandler *plug_handler, +void hotplug_handler_pre_plug(const HotplugHandler *plug_handler, DeviceState *plugged_dev, Error **errp); @@ -85,7 +86,7 @@ * * Calls #HotplugHandlerClass.unplug_request callback of @plug_handler. */ -void hotplug_handler_unplug_request(HotplugHandler *plug_handler, +void hotplug_handler_unplug_request(const HotplugHandler *plug_handler, DeviceState *plugged_dev, Error **errp); /** @@ -93,7 +94,7 @@ * * Calls #HotplugHandlerClass.unplug callback of @plug_handler. */ -void hotplug_handler_unplug(HotplugHandler *plug_handler, +void hotplug_handler_unplug(const HotplugHandler *plug_handler, DeviceState *plugged_dev, Error **errp); #endif
diff --git a/include/hw/core/qdev.h b/include/hw/core/qdev.h index f054a21..1f6bf3f 100644 --- a/include/hw/core/qdev.h +++ b/include/hw/core/qdev.h
@@ -386,7 +386,7 @@ /* public: */ DeviceState *parent; char *name; - HotplugHandler *hotplug_handler; + const HotplugHandler *hotplug_handler; int max_index; bool realized; bool full; @@ -514,8 +514,8 @@ void qdev_unrealize(DeviceState *dev); void qdev_set_legacy_instance_id(DeviceState *dev, int alias_id, int required_for_version); -HotplugHandler *qdev_get_bus_hotplug_handler(DeviceState *dev); -HotplugHandler *qdev_get_machine_hotplug_handler(DeviceState *dev); +const HotplugHandler *qdev_get_bus_hotplug_handler(DeviceState *dev); +const HotplugHandler *qdev_get_machine_hotplug_handler(DeviceState *dev); bool qdev_hotplug_allowed(DeviceState *dev, BusState *bus, Error **errp); bool qdev_hotunplug_allowed(DeviceState *dev, Error **errp); @@ -529,10 +529,10 @@ * Return: pointer to object that implements TYPE_HOTPLUG_HANDLER interface * or NULL if there aren't any. */ -HotplugHandler *qdev_get_hotplug_handler(DeviceState *dev); +const HotplugHandler *qdev_get_hotplug_handler(DeviceState *dev); void qdev_unplug(DeviceState *dev, Error **errp); int qdev_sync_config(DeviceState *dev, Error **errp); -void qdev_simple_device_unplug_cb(HotplugHandler *hotplug_dev, +void qdev_simple_device_unplug_cb(const HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp); void qdev_machine_creation_done(void); bool qdev_machine_modified(void); @@ -1065,11 +1065,11 @@ static inline bool qbus_is_hotpluggable(BusState *bus) { - HotplugHandler *plug_handler = bus->hotplug_handler; + const HotplugHandler *plug_handler = bus->hotplug_handler; bool ret = !!plug_handler; if (plug_handler) { - HotplugHandlerClass *hdc; + const HotplugHandlerClass *hdc; hdc = HOTPLUG_HANDLER_GET_CLASS(plug_handler); if (hdc->is_hotpluggable_bus) {
diff --git a/include/hw/cxl/cxl.h b/include/hw/cxl/cxl.h index d8cd835..7cf3dc6 100644 --- a/include/hw/cxl/cxl.h +++ b/include/hw/cxl/cxl.h
@@ -27,6 +27,17 @@ typedef struct PXBCXLDev PXBCXLDev; +/* + * CEDT CFMWS "Window Restrictions" bits (CXL r4.0 9.18.1.3), advertising + * which capabilities the host bridges below this window support. + */ +#define CXL_FMW_DEVICE_COHERENT (1 << 0) /* HDM-D */ +#define CXL_FMW_HOST_ONLY (1 << 1) /* HDM-H */ +#define CXL_FMW_VOLATILE (1 << 2) +#define CXL_FMW_PERSISTENT (1 << 3) +#define CXL_FMW_FIXED_CONFIG (1 << 4) +#define CXL_FMW_BI (1 << 5) /* HDM-DB, Back-Invalidate */ + typedef struct CXLFixedWindow { SysBusDevice parent_obj; int index; @@ -36,6 +47,7 @@ uint8_t num_targets; uint8_t enc_int_ways; uint8_t enc_int_gran; + uint16_t restrictions; /* Todo: XOR based interleaving */ MemoryRegion mr; hwaddr base;
diff --git a/include/hw/i386/microvm.h b/include/hw/i386/microvm.h index 184b7a8..5d5eacd 100644 --- a/include/hw/i386/microvm.h +++ b/include/hw/i386/microvm.h
@@ -76,8 +76,8 @@ struct MicrovmMachineClass { X86MachineClass parent; - HotplugHandler *(*orig_hotplug_handler)(MachineState *machine, - DeviceState *dev); + const HotplugHandler *(*orig_hotplug_handler)(MachineState *machine, + DeviceState *dev); void (*x86_load_linux)(X86MachineState *x86ms, FWCfgState *fw_cfg, int acpi_data_size); };
diff --git a/include/hw/i386/x86.h b/include/hw/i386/x86.h index 71fe6b5..abb073c 100644 --- a/include/hw/i386/x86.h +++ b/include/hw/i386/x86.h
@@ -46,7 +46,7 @@ qemu_irq *gsi; DeviceState *ioapic2; GMappedFile *initrd_mapped_file; - HotplugHandler *acpi_dev; + const HotplugHandler *acpi_dev; /* * Map the whole BIOS just underneath the 4 GiB address boundary. Only used @@ -112,13 +112,13 @@ void x86_cpus_init(X86MachineState *pcms, int default_cpu_version); void x86_rtc_set_cpus_count(ISADevice *rtc, uint16_t cpus_count); -void x86_cpu_pre_plug(HotplugHandler *hotplug_dev, +void x86_cpu_pre_plug(const HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp); -void x86_cpu_plug(HotplugHandler *hotplug_dev, +void x86_cpu_plug(const HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp); -void x86_cpu_unplug_request_cb(HotplugHandler *hotplug_dev, +void x86_cpu_unplug_request_cb(const HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp); -void x86_cpu_unplug_cb(HotplugHandler *hotplug_dev, +void x86_cpu_unplug_cb(const HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp); void x86_isa_bios_init(MemoryRegion *isa_bios, MemoryRegion *isa_memory,
diff --git a/include/hw/mem/nvdimm.h b/include/hw/mem/nvdimm.h index d3b7634..169a943 100644 --- a/include/hw/mem/nvdimm.h +++ b/include/hw/mem/nvdimm.h
@@ -157,5 +157,5 @@ uint32_t ram_slots, const char *oem_id, const char *oem_table_id); void nvdimm_plug(NVDIMMState *state); -void nvdimm_acpi_plug_cb(HotplugHandler *hotplug_dev, DeviceState *dev); +void nvdimm_acpi_plug_cb(const HotplugHandler *hotplug_dev, DeviceState *dev); #endif
diff --git a/include/hw/pci/pci_bridge.h b/include/hw/pci/pci_bridge.h index b61360b..f114f0a 100644 --- a/include/hw/pci/pci_bridge.h +++ b/include/hw/pci/pci_bridge.h
@@ -141,11 +141,11 @@ void pci_bridge_initfn(PCIDevice *pci_dev, const char *typename); void pci_bridge_exitfn(PCIDevice *pci_dev); -void pci_bridge_dev_plug_cb(HotplugHandler *hotplug_dev, DeviceState *dev, +void pci_bridge_dev_plug_cb(const HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp); -void pci_bridge_dev_unplug_cb(HotplugHandler *hotplug_dev, DeviceState *dev, +void pci_bridge_dev_unplug_cb(const HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp); -void pci_bridge_dev_unplug_request_cb(HotplugHandler *hotplug_dev, +void pci_bridge_dev_unplug_request_cb(const HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp); /*
diff --git a/include/hw/pci/pcie.h b/include/hw/pci/pcie.h index 71ba948..ec25e7a 100644 --- a/include/hw/pci/pcie.h +++ b/include/hw/pci/pcie.h
@@ -146,13 +146,13 @@ void pcie_cap_fill_link_ep_usp(PCIDevice *dev, PCIExpLinkWidth width, PCIExpLinkSpeed speed, bool flitmode); -void pcie_cap_slot_pre_plug_cb(HotplugHandler *hotplug_dev, DeviceState *dev, +void pcie_cap_slot_pre_plug_cb(const HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp); -void pcie_cap_slot_plug_cb(HotplugHandler *hotplug_dev, DeviceState *dev, +void pcie_cap_slot_plug_cb(const HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp); -void pcie_cap_slot_unplug_cb(HotplugHandler *hotplug_dev, DeviceState *dev, +void pcie_cap_slot_unplug_cb(const HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp); -void pcie_cap_slot_unplug_request_cb(HotplugHandler *hotplug_dev, +void pcie_cap_slot_unplug_request_cb(const HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp); void pcie_pasid_common_init(PCIDevice *dev, uint16_t offset,
diff --git a/include/hw/pci/shpc.h b/include/hw/pci/shpc.h index fce5bdd..67becb5 100644 --- a/include/hw/pci/shpc.h +++ b/include/hw/pci/shpc.h
@@ -45,11 +45,11 @@ void shpc_cap_write_config(PCIDevice *d, uint32_t addr, uint32_t val, int len); -void shpc_device_plug_cb(HotplugHandler *hotplug_dev, DeviceState *dev, +void shpc_device_plug_cb(const HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp); -void shpc_device_unplug_cb(HotplugHandler *hotplug_dev, DeviceState *dev, +void shpc_device_unplug_cb(const HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp); -void shpc_device_unplug_request_cb(HotplugHandler *hotplug_dev, +void shpc_device_unplug_request_cb(const HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp); extern const VMStateInfo shpc_vmstate_info;
diff --git a/include/hw/ppc/spapr_nvdimm.h b/include/hw/ppc/spapr_nvdimm.h index e9436cb..386da92 100644 --- a/include/hw/ppc/spapr_nvdimm.h +++ b/include/hw/ppc/spapr_nvdimm.h
@@ -18,7 +18,7 @@ int spapr_pmem_dt_populate(SpaprDrc *drc, SpaprMachineState *spapr, void *fdt, int *fdt_start_offset, Error **errp); void spapr_dt_persistent_memory(SpaprMachineState *spapr, void *fdt); -bool spapr_nvdimm_validate(HotplugHandler *hotplug_dev, NVDIMMDevice *nvdimm, +bool spapr_nvdimm_validate(const HotplugHandler *hotplug_dev, NVDIMMDevice *nvdimm, uint64_t size, Error **errp); void spapr_add_nvdimm(DeviceState *dev, uint64_t slot); void spapr_nvdimm_finish_flushes(void);
diff --git a/include/hw/virtio/vhost-backend.h b/include/hw/virtio/vhost-backend.h index d878d7b..daa979a 100644 --- a/include/hw/virtio/vhost-backend.h +++ b/include/hw/virtio/vhost-backend.h
@@ -190,6 +190,7 @@ vhost_set_vring_num_op vhost_set_vring_num; vhost_set_vring_base_op vhost_set_vring_base; vhost_get_vring_base_op vhost_get_vring_base; + vhost_get_vring_base_op vhost_get_vring_base_skip_drain; vhost_set_vring_kick_op vhost_set_vring_kick; vhost_set_vring_call_op vhost_set_vring_call; vhost_set_vring_err_op vhost_set_vring_err;
diff --git a/include/hw/virtio/vhost-user-media.h b/include/hw/virtio/vhost-user-media.h new file mode 100644 index 0000000..d040c70 --- /dev/null +++ b/include/hw/virtio/vhost-user-media.h
@@ -0,0 +1,46 @@ +/* + * vhost-user-media virtio device + * + * Copyright Red Hat, Inc. 2026 + * + * Authors: + * Albert Esteve <aesteve@redhat.com> + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef QEMU_VHOST_USER_MEDIA_H +#define QEMU_VHOST_USER_MEDIA_H + +#include "hw/virtio/virtio.h" +#include "hw/virtio/vhost.h" +#include "hw/virtio/vhost-user.h" +#include "qom/object.h" + +#define TYPE_VHOST_USER_MEDIA "vhost-user-media-device" +OBJECT_DECLARE_SIMPLE_TYPE(VHostUserMEDIA, VHOST_USER_MEDIA) + +/* virtio-media config layout, spec 5.22.4 */ +struct virtio_media_config { + uint32_t device_caps; + uint32_t device_type; + uint8_t card[32]; +} QEMU_PACKED; + +typedef struct { + CharFrontend chardev; +} VHostUserMEDIAConf; + +struct VHostUserMEDIA { + /*< private >*/ + VirtIODevice parent; + VHostUserMEDIAConf conf; + struct vhost_dev vhost_dev; + VhostUserState vhost_user; + VirtQueue *command_vq; + VirtQueue *event_vq; + bool connected; + /*< public >*/ +}; + +#endif /* QEMU_VHOST_USER_MEDIA_H */
diff --git a/include/hw/virtio/vhost-user.h b/include/hw/virtio/vhost-user.h index 06c360a..78b4fba 100644 --- a/include/hw/virtio/vhost-user.h +++ b/include/hw/virtio/vhost-user.h
@@ -36,6 +36,7 @@ VHOST_USER_PROTOCOL_F_GET_VRING_BASE_INFLIGHT = 20, VHOST_USER_PROTOCOL_F_GPA_ADDRESSES = 21, VHOST_USER_PROTOCOL_F_SHMEM = 22, + VHOST_USER_PROTOCOL_F_GET_VRING_BASE_SKIP_DRAIN = 23, VHOST_USER_PROTOCOL_F_MAX }; @@ -72,7 +73,6 @@ GPtrArray *notifiers; int memory_slots; bool supports_config; - bool supports_inflight_migration; } VhostUserState; /**
diff --git a/include/hw/virtio/vhost.h b/include/hw/virtio/vhost.h index 684bafc..0a322ef 100644 --- a/include/hw/virtio/vhost.h +++ b/include/hw/virtio/vhost.h
@@ -228,6 +228,7 @@ * @hdev: common vhost_dev structure * @vdev: the VirtIODevice structure * @vrings: true to have vrings disabled in this call + * @skip_drain: true to notice back-end to skip draining all in-flight requests * * Stop the vhost device. After the device is stopped the notifiers * can be disabled (@vhost_dev_disable_notifiers) and the device can @@ -235,7 +236,8 @@ * * Return: 0 on success, != 0 on error when stopping dev. */ -int vhost_dev_stop(struct vhost_dev *hdev, VirtIODevice *vdev, bool vrings); +int vhost_dev_stop(struct vhost_dev *hdev, VirtIODevice *vdev, bool vrings, + bool skip_drain); /** * vhost_dev_force_stop() - force stop the vhost device @@ -310,10 +312,13 @@ */ bool vhost_virtqueue_pending(struct vhost_dev *hdev, int n); -/* Mask/unmask events from this vq. +/** + * vhost_virtqueue_mask(): mask/unmask events from this vq. + * + * Returns: 0 on success, a negative errno on failure. */ -void vhost_virtqueue_mask(struct vhost_dev *hdev, VirtIODevice *vdev, int n, - bool mask); +int vhost_virtqueue_mask(struct vhost_dev *hdev, VirtIODevice *vdev, int n, + bool mask); /** * vhost_get_features_ex() - sanitize the extended features set @@ -393,7 +398,8 @@ int vhost_virtqueue_start(struct vhost_dev *dev, struct VirtIODevice *vdev, struct vhost_virtqueue *vq, unsigned idx); int vhost_virtqueue_stop(struct vhost_dev *dev, struct VirtIODevice *vdev, - struct vhost_virtqueue *vq, unsigned idx); + struct vhost_virtqueue *vq, unsigned idx, + bool skip_drain); void vhost_dev_reset_inflight(struct vhost_inflight *inflight); void vhost_dev_free_inflight(struct vhost_inflight *inflight);
diff --git a/include/hw/virtio/virtio-balloon.h b/include/hw/virtio/virtio-balloon.h index abbf339..53bbaa4 100644 --- a/include/hw/virtio/virtio-balloon.h +++ b/include/hw/virtio/virtio-balloon.h
@@ -39,6 +39,7 @@ FREE_PAGE_HINT_S_REQUESTED = 1, FREE_PAGE_HINT_S_START = 2, FREE_PAGE_HINT_S_DONE = 3, + FREE_PAGE_HINT_S_UNREALIZE = 4, }; struct VirtIOBalloon {
diff --git a/include/hw/virtio/virtio-net.h b/include/hw/virtio/virtio-net.h index 371e376..8c96776 100644 --- a/include/hw/virtio/virtio-net.h +++ b/include/hw/virtio/virtio-net.h
@@ -230,6 +230,7 @@ struct EBPFRSSContext ebpf_rss; uint32_t nr_ebpf_rss_fds; char **ebpf_rss_fds; + bool peers_wait_incoming; }; size_t virtio_net_handle_ctrl_iov(VirtIODevice *vdev,
diff --git a/include/hw/virtio/virtio.h b/include/hw/virtio/virtio.h index c99cb19..27c5fe3 100644 --- a/include/hw/virtio/virtio.h +++ b/include/hw/virtio/virtio.h
@@ -320,7 +320,7 @@ void virtqueue_fill(VirtQueue *vq, const VirtQueueElement *elem, unsigned int len, unsigned int idx); -void virtqueue_map(VirtIODevice *vdev, VirtQueueElement *elem); +bool virtqueue_map(VirtIODevice *vdev, VirtQueueElement *elem); void *virtqueue_pop(VirtQueue *vq, size_t sz); unsigned int virtqueue_drop_all(VirtQueue *vq); void *qemu_get_virtqueue_element(VirtIODevice *vdev, QEMUFile *f, size_t sz);
diff --git a/include/migration/misc.h b/include/migration/misc.h index 2b2fbb5..019717f 100644 --- a/include/migration/misc.h +++ b/include/migration/misc.h
@@ -161,4 +161,6 @@ void migration_request_switchover_ack_legacy(const char *requester); +bool migrate_local(void); + #endif
diff --git a/include/migration/vmstate.h b/include/migration/vmstate.h index e72c3fae..7d2b786 100644 --- a/include/migration/vmstate.h +++ b/include/migration/vmstate.h
@@ -169,6 +169,8 @@ MIG_PRI_LOW, /* Must happen after default */ MIG_PRI_DEFAULT, + MIG_PRI_BACKEND, /* Must happen before emulated devices, */ + /* e.g. virtio-net */ MIG_PRI_IOMMU, /* Must happen before PCI devices */ MIG_PRI_PCI_BUS, /* Must happen before IOMMU */ MIG_PRI_VIRTIO_MEM, /* Must happen before IOMMU */
diff --git a/include/net/net.h b/include/net/net.h index 626b997..fdf6a2d 100644 --- a/include/net/net.h +++ b/include/net/net.h
@@ -82,6 +82,7 @@ typedef void (NetAnnounce)(NetClientState *); typedef bool (SetSteeringEBPF)(NetClientState *, int); typedef bool (NetCheckPeerType)(NetClientState *, ObjectClass *, Error **); +typedef bool (IsWaitIncoming)(NetClientState *); typedef struct vhost_net *(GetVHostNet)(NetClientState *nc); typedef struct NetClientInfo { @@ -110,6 +111,7 @@ NetAnnounce *announce; SetSteeringEBPF *set_steering_ebpf; NetCheckPeerType *check_peer_type; + IsWaitIncoming *is_wait_incoming; GetVHostNet *get_vhost_net; } NetClientInfo; @@ -161,6 +163,13 @@ NetClientState *qemu_find_netdev(const char *id); int qemu_find_net_clients_except(const char *id, NetClientState **ncs, NetClientDriver type, int max); +void qemu_net_client_setup(NetClientState *nc, + NetClientInfo *info, + NetClientState *peer, + const char *model, + const char *name, + NetClientDestructor *destructor, + bool is_datapath); NetClientState *qemu_new_net_client(NetClientInfo *info, NetClientState *peer, const char *model,
diff --git a/include/net/tap.h b/include/net/tap.h index 6f34f13..2685705 100644 --- a/include/net/tap.h +++ b/include/net/tap.h
@@ -28,6 +28,8 @@ #include "standard-headers/linux/virtio_net.h" +#define TYPE_TAP_NETDEV "tap-netdev" + int tap_enable(NetClientState *nc); int tap_disable(NetClientState *nc);
diff --git a/include/standard-headers/linux/ethtool.h b/include/standard-headers/linux/ethtool.h index 55f608c..622d634 100644 --- a/include/standard-headers/linux/ethtool.h +++ b/include/standard-headers/linux/ethtool.h
@@ -2015,7 +2015,13 @@ ETHTOOL_LINK_MODE_100000baseLR4_ER4_Full_BIT = 39, ETHTOOL_LINK_MODE_50000baseSR2_Full_BIT = 40, ETHTOOL_LINK_MODE_1000baseX_Full_BIT = 41, + + /* Despite the "baseCR" in 10000baseCR, this is not an IEEE 802.3 baseCR + * It represents SFF-8431 Appendix-E SFP+ Direct Attach (10G-SFI-DA). + * The name is kept as-is for uAPI backward compatibility. + */ ETHTOOL_LINK_MODE_10000baseCR_Full_BIT = 42, + ETHTOOL_LINK_MODE_10000baseSR_Full_BIT = 43, ETHTOOL_LINK_MODE_10000baseLR_Full_BIT = 44, ETHTOOL_LINK_MODE_10000baseLRM_Full_BIT = 45,
diff --git a/include/standard-headers/linux/virtio_ids.h b/include/standard-headers/linux/virtio_ids.h index 6c12db1..f9056af 100644 --- a/include/standard-headers/linux/virtio_ids.h +++ b/include/standard-headers/linux/virtio_ids.h
@@ -69,6 +69,7 @@ #define VIRTIO_ID_BT 40 /* virtio bluetooth */ #define VIRTIO_ID_GPIO 41 /* virtio gpio */ #define VIRTIO_ID_SPI 45 /* virtio spi */ +#define VIRTIO_ID_MEDIA 48 /* virtio media */ /* * Virtio Transitional IDs
diff --git a/linux-headers/linux/iommufd.h b/linux-headers/linux/iommufd.h index 10c600d..3c61caa 100644 --- a/linux-headers/linux/iommufd.h +++ b/linux-headers/linux/iommufd.h
@@ -57,6 +57,7 @@ IOMMUFD_CMD_IOAS_CHANGE_PROCESS = 0x92, IOMMUFD_CMD_VEVENTQ_ALLOC = 0x93, IOMMUFD_CMD_HW_QUEUE_ALLOC = 0x94, + IOMMUFD_CMD_IOAS_NOIOMMU_GET_PA = 0x95, }; /** @@ -220,6 +221,33 @@ #define IOMMU_IOAS_MAP _IO(IOMMUFD_TYPE, IOMMUFD_CMD_IOAS_MAP) /** + * struct iommu_ioas_noiommu_get_pa - ioctl(IOMMU_IOAS_NOIOMMU_GET_PA) + * @size: sizeof(struct iommu_ioas_noiommu_get_pa) + * @flags: Reserved, must be 0 for now + * @ioas_id: IOAS ID to query IOVA to PA mapping from + * @__reserved: Must be 0 + * @iova: IOVA to query + * @length: On input, non-zero maximum number of bytes to query starting from + * @iova. On output, number of physically contiguous bytes starting + * from @out_phys, capped by the input length. + * @out_phys: Output physical address the IOVA maps to + * + * Query the physical address backing an IOVA range. The beginning of the + * range must be mapped already and length must be non-zero. For noiommu + * devices doing unsafe DMA only. + */ +struct iommu_ioas_noiommu_get_pa { + __u32 size; + __u32 flags; + __u32 ioas_id; + __u32 __reserved; + __aligned_u64 iova; + __aligned_u64 length; + __aligned_u64 out_phys; +}; +#define IOMMU_IOAS_NOIOMMU_GET_PA _IO(IOMMUFD_TYPE, IOMMUFD_CMD_IOAS_NOIOMMU_GET_PA) + +/** * struct iommu_ioas_map_file - ioctl(IOMMU_IOAS_MAP_FILE) * @size: sizeof(struct iommu_ioas_map_file) * @flags: same as for iommu_ioas_map @@ -575,10 +603,21 @@ }; /** + * enum iommu_hw_info_arm_smmuv3_flags - Flags for ARM SMMUv3 hw_info + * @IOMMU_HW_INFO_ARM_SMMUV3_ERRATA_REPEAT_TLBI_CFGI: + * If set, user space must issue TLBI/CFGI+SYNC commands twice due to + * hardware erratum T264-SMMU-3. See the description at + * arm_smmu_erratum_repeat_tlbi_cfgi_key. + */ +enum iommu_hw_info_arm_smmuv3_flags { + IOMMU_HW_INFO_ARM_SMMUV3_ERRATA_REPEAT_TLBI_CFGI = 1 << 0, +}; + +/** * struct iommu_hw_info_arm_smmuv3 - ARM SMMUv3 hardware information * (IOMMU_HW_INFO_TYPE_ARM_SMMUV3) * - * @flags: Must be set to 0 + * @flags: Combination of enum iommu_hw_info_arm_smmuv3_flags * @__reserved: Must be 0 * @idr: Implemented features for ARM SMMU Non-secure programming interface * @iidr: Information about the implementation and implementer of ARM SMMU, @@ -594,7 +633,7 @@ * idr[0]: ST_LEVEL, TERM_MODEL, STALL_MODEL, TTENDIAN , CD2L, ASID16, TTF * idr[1]: SIDSIZE, SSIDSIZE * idr[3]: BBML, RIL - * idr[5]: VAX, GRAN64K, GRAN16K, GRAN4K + * idr[5]: VAX, GRAN64K, GRAN16K, GRAN4K, DS * * - S1P should be assumed to be true if a NESTED HWPT can be created * - VFIO/iommufd only support platforms with COHACC, it should be assumed to be @@ -602,7 +641,7 @@ * - ATS is a per-device property. If the VMM describes any devices as ATS * capable in ACPI/DT it should set the corresponding idr. * - * This list may expand in future (eg E0PD, AIE, PBHA, D128, DS etc). It is + * This list may expand in future (eg E0PD, AIE, PBHA, D128 etc). It is * important that VMMs do not read bits outside the list to allow for * compatibility with future kernels. Several features in the SMMUv3 * architecture are not currently supported by the kernel for nesting: HTTU,
diff --git a/linux-headers/linux/userfaultfd.h b/linux-headers/linux/userfaultfd.h index 4283de2..c8e99df 100644 --- a/linux-headers/linux/userfaultfd.h +++ b/linux-headers/linux/userfaultfd.h
@@ -25,7 +25,8 @@ #define UFFD_API ((__u64)0xAA) #define UFFD_API_REGISTER_MODES (UFFDIO_REGISTER_MODE_MISSING | \ UFFDIO_REGISTER_MODE_WP | \ - UFFDIO_REGISTER_MODE_MINOR) + UFFDIO_REGISTER_MODE_MINOR | \ + UFFDIO_REGISTER_MODE_RWP) #define UFFD_API_FEATURES (UFFD_FEATURE_PAGEFAULT_FLAG_WP | \ UFFD_FEATURE_EVENT_FORK | \ UFFD_FEATURE_EVENT_REMAP | \ @@ -42,10 +43,13 @@ UFFD_FEATURE_WP_UNPOPULATED | \ UFFD_FEATURE_POISON | \ UFFD_FEATURE_WP_ASYNC | \ - UFFD_FEATURE_MOVE) + UFFD_FEATURE_MOVE | \ + UFFD_FEATURE_RWP | \ + UFFD_FEATURE_RWP_ASYNC) #define UFFD_API_IOCTLS \ ((__u64)1 << _UFFDIO_REGISTER | \ (__u64)1 << _UFFDIO_UNREGISTER | \ + (__u64)1 << _UFFDIO_SET_MODE | \ (__u64)1 << _UFFDIO_API) #define UFFD_API_RANGE_IOCTLS \ ((__u64)1 << _UFFDIO_WAKE | \ @@ -54,13 +58,15 @@ (__u64)1 << _UFFDIO_MOVE | \ (__u64)1 << _UFFDIO_WRITEPROTECT | \ (__u64)1 << _UFFDIO_CONTINUE | \ - (__u64)1 << _UFFDIO_POISON) + (__u64)1 << _UFFDIO_POISON | \ + (__u64)1 << _UFFDIO_RWPROTECT) #define UFFD_API_RANGE_IOCTLS_BASIC \ ((__u64)1 << _UFFDIO_WAKE | \ (__u64)1 << _UFFDIO_COPY | \ (__u64)1 << _UFFDIO_WRITEPROTECT | \ (__u64)1 << _UFFDIO_CONTINUE | \ - (__u64)1 << _UFFDIO_POISON) + (__u64)1 << _UFFDIO_POISON | \ + (__u64)1 << _UFFDIO_RWPROTECT) /* * Valid ioctl command number range with this API is from 0x00 to @@ -79,6 +85,8 @@ #define _UFFDIO_WRITEPROTECT (0x06) #define _UFFDIO_CONTINUE (0x07) #define _UFFDIO_POISON (0x08) +#define _UFFDIO_RWPROTECT (0x09) +#define _UFFDIO_SET_MODE (0x0A) #define _UFFDIO_API (0x3F) /* userfaultfd ioctl ids */ @@ -103,6 +111,10 @@ struct uffdio_continue) #define UFFDIO_POISON _IOWR(UFFDIO, _UFFDIO_POISON, \ struct uffdio_poison) +#define UFFDIO_RWPROTECT _IOWR(UFFDIO, _UFFDIO_RWPROTECT, \ + struct uffdio_rwprotect) +#define UFFDIO_SET_MODE _IOW(UFFDIO, _UFFDIO_SET_MODE, \ + struct uffdio_set_mode) /* read() structure */ struct uffd_msg { @@ -158,6 +170,7 @@ #define UFFD_PAGEFAULT_FLAG_WRITE (1<<0) /* If this was a write fault */ #define UFFD_PAGEFAULT_FLAG_WP (1<<1) /* If reason is VM_UFFD_WP */ #define UFFD_PAGEFAULT_FLAG_MINOR (1<<2) /* If reason is VM_UFFD_MINOR */ +#define UFFD_PAGEFAULT_FLAG_RWP (1<<3) /* If reason is VM_UFFD_RWP */ struct uffdio_api { /* userland asks for an API number and the features to enable */ @@ -230,6 +243,18 @@ * * UFFD_FEATURE_MOVE indicates that the kernel supports moving an * existing page contents from userspace. + * + * UFFD_FEATURE_RWP indicates that the kernel supports + * UFFDIO_REGISTER_MODE_RWP for read-write protection tracking. + * Pages are made inaccessible via UFFDIO_RWPROTECT and faults + * are delivered when the pages are re-accessed. + * + * UFFD_FEATURE_RWP_ASYNC indicates asynchronous mode for + * UFFDIO_REGISTER_MODE_RWP. When set, faults on read-write + * protected pages are auto-resolved by the kernel (PTE + * permissions restored immediately) without delivering a message + * to the userfaultfd handler. Use PAGEMAP_SCAN with inverted + * PAGE_IS_ACCESSED to find pages that were not re-accessed. */ #define UFFD_FEATURE_PAGEFAULT_FLAG_WP (1<<0) #define UFFD_FEATURE_EVENT_FORK (1<<1) @@ -248,6 +273,8 @@ #define UFFD_FEATURE_POISON (1<<14) #define UFFD_FEATURE_WP_ASYNC (1<<15) #define UFFD_FEATURE_MOVE (1<<16) +#define UFFD_FEATURE_RWP (1<<17) +#define UFFD_FEATURE_RWP_ASYNC (1<<18) __u64 features; __u64 ioctls; @@ -263,6 +290,7 @@ #define UFFDIO_REGISTER_MODE_MISSING ((__u64)1<<0) #define UFFDIO_REGISTER_MODE_WP ((__u64)1<<1) #define UFFDIO_REGISTER_MODE_MINOR ((__u64)1<<2) +#define UFFDIO_REGISTER_MODE_RWP ((__u64)1<<3) __u64 mode; /* @@ -356,6 +384,14 @@ __s64 updated; }; +struct uffdio_rwprotect { + struct uffdio_range range; + /* !RWP means undo RWP-protection */ +#define UFFDIO_RWPROTECT_MODE_RWP ((__u64)1<<0) +#define UFFDIO_RWPROTECT_MODE_DONTWAKE ((__u64)1<<1) + __u64 mode; +}; + struct uffdio_move { __u64 dst; __u64 src; @@ -374,6 +410,16 @@ __s64 move; }; +struct uffdio_set_mode { + /* + * Toggle async mode for features at runtime. + * Supported: UFFD_FEATURE_RWP_ASYNC. + * Setting a bit in both enable and disable is invalid. + */ + __u64 enable; + __u64 disable; +}; + /* * Flags for the userfaultfd(2) system call itself. */
diff --git a/linux-headers/linux/vduse.h b/linux-headers/linux/vduse.h index e19b3c0..bab4712 100644 --- a/linux-headers/linux/vduse.h +++ b/linux-headers/linux/vduse.h
@@ -14,6 +14,12 @@ #define VDUSE_API_VERSION_1 1 +/* The VDUSE instance expects a request for vq ready */ +#define VDUSE_F_QUEUE_READY 0 + +/* The VDUSE instance expects a request for suspend */ +#define VDUSE_F_SUSPEND 1 + /* * Get the version of VDUSE API that kernel supported (VDUSE_API_VERSION). * This is used for future extension. @@ -63,6 +69,12 @@ */ #define VDUSE_DESTROY_DEV _IOW(VDUSE_BASE, 0x03, char[VDUSE_NAME_MAX]) +/* Get the VDUSE supported features */ +#define VDUSE_GET_FEATURES _IOR(VDUSE_BASE, 0x04, __u64) + +/* Set the VDUSE features */ +#define VDUSE_SET_FEATURES _IOW(VDUSE_BASE, 0x05, __u64) + /* The ioctls for VDUSE device (/dev/vduse/$NAME) */ /** @@ -325,6 +337,8 @@ VDUSE_SET_STATUS, VDUSE_UPDATE_IOTLB, VDUSE_SET_VQ_GROUP_ASID, + VDUSE_SET_VQ_READY, + VDUSE_SUSPEND, }; /** @@ -373,6 +387,15 @@ }; /** + * struct vduse_vq_ready - Virtqueue ready request message + * @num: Virtqueue number + */ +struct vduse_vq_ready { + __u32 num; + __u32 ready; +}; + +/** * struct vduse_dev_request - control request * @type: request type * @request_id: request id @@ -382,6 +405,7 @@ * @iova: IOVA range for updating * @iova_v2: IOVA range for updating if API_VERSION >= 1 * @vq_group_asid: ASID of a virtqueue group + * @vq_ready: Virtqueue ready request * @padding: padding * * Structure used by read(2) on /dev/vduse/$NAME. @@ -399,6 +423,10 @@ */ struct vduse_iova_range_v2 iova_v2; struct vduse_vq_group_asid vq_group_asid; + + /* Only if VDUSE_F_QUEUE_READY is negotiated */ + struct vduse_vq_ready vq_ready; + __u32 padding[32]; }; };
diff --git a/linux-headers/linux/vfio.h b/linux-headers/linux/vfio.h index f3282b8..c85dcbf 100644 --- a/linux-headers/linux/vfio.h +++ b/linux-headers/linux/vfio.h
@@ -1534,6 +1534,27 @@ */ #define VFIO_DEVICE_FEATURE_MIG_PRECOPY_INFOv2 12 +/** + * VFIO_DEVICE_FEATURE_ZPCI_ERROR feature provides PCI error information to + * userspace for vfio-pci devices on s390. On s390, PCI error recovery + * involves platform firmware and notification to operating systems is done + * by architecture specific mechanism. Exposing this information to + * userspace allows it to take appropriate actions to handle an + * error on the device. + * + * Userspace provides an opaque buffer of fixed length, and the kernel + * fills it with the zpci_ccdf_err data structure. The length of + * zpci_ccdf_err is provided to userspace via the + * VFIO_DEVICE_INFO_CAP_ZPCI_BASE capability. + * + * The ioctl returns -ENOMSG if there are no pending PCI errors. + */ +struct vfio_device_feature_zpci_err { + __aligned_u64 data; +}; + +#define VFIO_DEVICE_FEATURE_ZPCI_ERROR 13 + /* -------- API for Type1 VFIO IOMMU -------- */ /**
diff --git a/linux-headers/linux/vfio_zdev.h b/linux-headers/linux/vfio_zdev.h index 77f2aff..d6c1175 100644 --- a/linux-headers/linux/vfio_zdev.h +++ b/linux-headers/linux/vfio_zdev.h
@@ -32,6 +32,8 @@ /* End of version 1 */ __u32 fh; /* PCI function handle */ /* End of version 2 */ + __u32 ccdf_err_length; /* PCI CCDF length */ + /* End of version 3 */ }; /**
diff --git a/meson.build b/meson.build index 2a12071..cfac634 100644 --- a/meson.build +++ b/meson.build
@@ -746,6 +746,10 @@ '-Wno-typedef-redefinition', ] +if get_option('optimization') == 'g' + warn_flags += ['-Wno-error=maybe-uninitialized'] +endif + if host_os != 'darwin' tsa_has_cleanup = cc.compiles(''' struct __attribute__((capability("mutex"))) mutex {};
diff --git a/migration/fd.c b/migration/fd.c index f05f959..4a891e8 100644 --- a/migration/fd.c +++ b/migration/fd.c
@@ -37,16 +37,28 @@ return S_ISFIFO(statbuf.st_mode); } -static bool migration_fd_valid(int fd) +static bool migration_fd_valid(int fd, Error **errp) { - if (fd_is_socket(fd)) { + if (migrate_local() && migrate_mode() != MIG_MODE_CPR_EXEC) { + struct sockaddr_storage ss; + socklen_t sslen = sizeof(ss); + + if (getsockname(fd, (struct sockaddr *)&ss, &sslen) < 0 || + ss.ss_family != AF_UNIX) { + error_setg(errp, + "local migration requires a UNIX domain socket channel"); + return false; + } + return true; } - if (fd_is_pipe(fd)) { + if (fd_is_socket(fd) || fd_is_pipe(fd)) { return true; } + error_setg(errp, "fd: migration to a file is not supported." + " Use file: instead."); return false; } @@ -59,9 +71,8 @@ goto out; } - if (!migration_fd_valid(fd)) { - error_setg(errp, "fd: migration to a file is not supported." - " Use file: instead."); + if (!migration_fd_valid(fd, errp)) { + close(fd); goto out; } @@ -94,9 +105,8 @@ return; } - if (!migration_fd_valid(fd)) { - error_setg(errp, "fd: migration to a file is not supported." - " Use file: instead."); + if (!migration_fd_valid(fd, errp)) { + close(fd); return; }
diff --git a/migration/migration.c b/migration/migration.c index d0b864a..d14beb5 100644 --- a/migration/migration.c +++ b/migration/migration.c
@@ -260,6 +260,19 @@ errp); } + if (migrate_local() && migrate_mode() != MIG_MODE_CPR_EXEC && + (addr->transport != MIGRATION_ADDRESS_TYPE_SOCKET || + (addr->u.socket.type != SOCKET_ADDRESS_TYPE_UNIX && + addr->u.socket.type != SOCKET_ADDRESS_TYPE_FD))) { + /* + * For SOCKET_ADDRESS_TYPE_FD we do check the fd itself later + * in migration_fd_valid(). + */ + error_setg(errp, + "local migration requires a UNIX domain socket channel"); + return false; + } + return true; }
diff --git a/migration/options.c b/migration/options.c index d2575ed..05b026f 100644 --- a/migration/options.c +++ b/migration/options.c
@@ -14,6 +14,7 @@ #include "qemu/osdep.h" #include "qemu/error-report.h" #include "qemu/units.h" +#include "qapi/util.h" #include "exec/target_page.h" #include "qapi/clone-visitor.h" #include "qapi/error.h" @@ -25,6 +26,7 @@ #include "migration/colo.h" #include "migration/cpr.h" #include "migration/misc.h" +#include "migration/options.h" #include "migration.h" #include "migration-stats.h" #include "qemu-file.h" @@ -350,6 +352,12 @@ return s->capabilities[MIGRATION_CAPABILITY_MAPPED_RAM]; } +bool migrate_local(void) +{ + MigrationState *s = migrate_get_current(); + return s->parameters.local; +} + bool migrate_ignore_shared(void) { MigrationState *s = migrate_get_current(); @@ -1101,7 +1109,7 @@ &p->has_announce_step, &p->has_block_bitmap_mapping, &p->has_x_vcpu_dirty_limit_period, &p->has_vcpu_dirty_limit, &p->has_mode, &p->has_zero_page_detection, &p->has_direct_io, - &p->has_x_rdma_chunk_size, &p->has_cpr_exec_command, + &p->has_x_rdma_chunk_size, &p->has_cpr_exec_command, &p->has_local, }; len = ARRAY_SIZE(has_fields); @@ -1449,6 +1457,10 @@ qapi_free_strList(dest->cpr_exec_command); dest->cpr_exec_command = QAPI_CLONE(strList, params->cpr_exec_command); } + + if (params->has_local) { + dest->local = params->local; + } } static void migrate_params_apply(MigrationParameters *params) @@ -1581,6 +1593,10 @@ s->parameters.cpr_exec_command = QAPI_CLONE(strList, params->cpr_exec_command); } + + if (params->has_local) { + s->parameters.local = params->local; + } } void qmp_migrate_set_parameters(MigrationParameters *params, Error **errp)
diff --git a/net/net.c b/net/net.c index 1161183..d7fa637 100644 --- a/net/net.c +++ b/net/net.c
@@ -261,13 +261,13 @@ int iovcnt, void *opaque); -static void qemu_net_client_setup(NetClientState *nc, - NetClientInfo *info, - NetClientState *peer, - const char *model, - const char *name, - NetClientDestructor *destructor, - bool is_datapath) +void qemu_net_client_setup(NetClientState *nc, + NetClientInfo *info, + NetClientState *peer, + const char *model, + const char *name, + NetClientDestructor *destructor, + bool is_datapath) { nc->info = info; nc->model = g_strdup(model);
diff --git a/net/tap-solaris.c b/net/tap-solaris.c index 8704b10..2c6c7af 100644 --- a/net/tap-solaris.c +++ b/net/tap-solaris.c
@@ -60,8 +60,6 @@ */ static int tap_alloc(char *dev, size_t dev_size, Error **errp) { - /* FIXME leaks like a sieve on error paths */ - /* FIXME suspicious: many errors are reported, then ignored */ int tap_fd, if_fd, ppa = -1; static int ip_fd = 0; char *ptr; @@ -102,45 +100,61 @@ strioc_ppa.ic_timout = 0; strioc_ppa.ic_len = sizeof(ppa); strioc_ppa.ic_dp = (char *)&ppa; - if ((ppa = ioctl (tap_fd, I_STR, &strioc_ppa)) < 0) + if ((ppa = ioctl (tap_fd, I_STR, &strioc_ppa)) < 0) { error_report("Can't assign new interface"); + goto fail_tap_fd; + } if_fd = RETRY_ON_EINTR(open("/dev/tap", O_RDWR, 0)); if (if_fd < 0) { error_setg_file_open(errp, errno, "/dev/tap"); - return -1; + goto fail_tap_fd; } if(ioctl(if_fd, I_PUSH, "ip") < 0){ error_setg(errp, "Can't push IP module"); - return -1; + goto fail_if_fd; } - if (ioctl(if_fd, SIOCGLIFFLAGS, &ifr) < 0) + if (ioctl(if_fd, SIOCGLIFFLAGS, &ifr) < 0) { error_report("Can't get flags"); + goto fail_if_fd; + } - snprintf (actual_name, 32, "tap%d", ppa); + snprintf(actual_name, 32, "tap%d", ppa); pstrcpy(ifr.lifr_name, sizeof(ifr.lifr_name), actual_name); ifr.lifr_ppa = ppa; - /* Assign ppa according to the unit number returned by tun device */ - - if (ioctl (if_fd, SIOCSLIFNAME, &ifr) < 0) + if (ioctl(if_fd, SIOCSLIFNAME, &ifr) < 0) { error_report("Can't set PPA %d", ppa); - if (ioctl(if_fd, SIOCGLIFFLAGS, &ifr) <0) + goto fail_if_fd; + } + + if (ioctl(if_fd, SIOCGLIFFLAGS, &ifr) < 0) { error_report("Can't get flags"); + goto fail_if_fd; + } + /* Push arp module to if_fd */ - if (ioctl (if_fd, I_PUSH, "arp") < 0) + if (ioctl(if_fd, I_PUSH, "arp") < 0) { error_report("Can't push ARP module (2)"); + goto fail_if_fd; + } /* Push arp module to ip_fd */ - if (ioctl (ip_fd, I_POP, NULL) < 0) + if (ioctl(ip_fd, I_POP, NULL) < 0) { error_report("I_POP failed"); - if (ioctl (ip_fd, I_PUSH, "arp") < 0) + goto fail_if_fd; + } + if (ioctl(ip_fd, I_PUSH, "arp") < 0) { error_report("Can't push ARP module (3)"); + goto fail_if_fd; + } /* Open arp_fd */ arp_fd = RETRY_ON_EINTR(open("/dev/tap", O_RDWR, 0)); - if (arp_fd < 0) + if (arp_fd < 0) { error_report("Can't open %s", "/dev/tap"); + goto fail_if_fd; + } /* Set ifname to arp */ strioc_if.ic_cmd = SIOCSLIFNAME; @@ -149,32 +163,45 @@ strioc_if.ic_dp = (char *)𝔦 if (ioctl(arp_fd, I_STR, &strioc_if) < 0){ error_report("Can't set ifname to arp"); + goto fail_arp_fd; } if((ip_muxid = ioctl(ip_fd, I_LINK, if_fd)) < 0){ error_setg(errp, "Can't link TAP device to IP"); - return -1; + goto fail_arp_fd; } - if ((arp_muxid = ioctl (ip_fd, link_type, arp_fd)) < 0) + if ((arp_muxid = ioctl(ip_fd, link_type, arp_fd)) < 0) { error_report("Can't link TAP device to ARP"); + goto fail_ip_muxid; + } - close (if_fd); + close(if_fd); memset(&ifr, 0x0, sizeof(ifr)); pstrcpy(ifr.lifr_name, sizeof(ifr.lifr_name), actual_name); ifr.lifr_ip_muxid = ip_muxid; ifr.lifr_arp_muxid = arp_muxid; - if (ioctl (ip_fd, SIOCSLIFMUXID, &ifr) < 0) + if (ioctl(ip_fd, SIOCSLIFMUXID, &ifr) < 0) { - ioctl (ip_fd, I_PUNLINK , arp_muxid); - ioctl (ip_fd, I_PUNLINK, ip_muxid); + ioctl(ip_fd, I_PUNLINK, arp_muxid); + ioctl(ip_fd, I_PUNLINK, ip_muxid); error_report("Can't set multiplexor id"); } snprintf(dev, dev_size, "tap%d", ppa); return tap_fd; + +fail_ip_muxid: + ioctl(ip_fd, I_PUNLINK, ip_muxid); +fail_arp_fd: + close(arp_fd); +fail_if_fd: + close(if_fd); +fail_tap_fd: + close(tap_fd); + return -1; } int tap_open(char *ifname, int ifname_size, int *vnet_hdr,
diff --git a/net/tap.c b/net/tap.c index 57ffb09..d71e3dd 100644 --- a/net/tap.c +++ b/net/tap.c
@@ -36,13 +36,19 @@ #include "net/net.h" #include "clients.h" #include "monitor/monitor.h" +#include "system/runstate.h" #include "system/system.h" +#include "migration/misc.h" #include "qapi/error.h" #include "qemu/cutils.h" #include "qemu/error-report.h" #include "qemu/main-loop.h" #include "qemu/sockets.h" #include "hw/virtio/vhost.h" +#include "hw/core/vmstate-if.h" +#include "migration/vmstate.h" +#include "qom/object.h" +#include "qom/compat-properties.h" #include "net/tap.h" #include "net/util.h" @@ -68,9 +74,17 @@ VHOST_INVALID_FEATURE_BIT }; -typedef struct TAPState { +OBJECT_DECLARE_SIMPLE_TYPE(TAPState, TAP_NETDEV) + +static const VMStateDescription vmstate_tap; + +struct TAPState { + Object parent_obj; + NetClientState nc; int fd; + int vhostfd; + uint32_t vhost_busyloop_timeout; char down_script[1024]; char down_script_arg[128]; uint8_t buf[NET_BUFSIZE]; @@ -84,7 +98,12 @@ VHostNetState *vhost_net; unsigned host_vnet_hdr_len; Notifier exit; -} TAPState; + + int queue_index; + bool enable_poll_on_resume; + VMChangeStateEntry *vmstate; + bool permit_local_migration; +}; static void launch_script(const char *setup_script, const char *ifname, int fd, Error **errp); @@ -92,19 +111,39 @@ static void tap_send(void *opaque); static void tap_writable(void *opaque); -static char *tap_parse_script(const char *script_arg, const char *default_path) +static bool tap_is_explicit_no_script(const char *script_arg_name, + const char *script_arg_value) { - g_autofree char *res = g_strdup(script_arg); - - if (!res) { - res = get_relocated_path(default_path); + if (!script_arg_value) { + return false; } - if (res[0] == '\0' || strcmp(res, "no") == 0) { + if (script_arg_value[0] == '\0') { + return true; + } + + if (strcmp(script_arg_value, "no") == 0) { + warn_report("'%s=no' is deprecated; use '%s=' instead", + script_arg_name, script_arg_name); + return true; + } + + return false; +} + +static char *tap_parse_script(const char *script_arg_name, + const char *script_arg_value, + const char *default_path) +{ + if (tap_is_explicit_no_script(script_arg_name, script_arg_value)) { return NULL; } - return g_steal_pointer(&res); + if (!script_arg_value) { + return get_relocated_path(default_path); + } + + return g_strdup(script_arg_value); } static void tap_update_fd_handler(TAPState *s) @@ -117,10 +156,31 @@ static void tap_read_poll(TAPState *s, bool enable) { + if (enable && runstate_check(RUN_STATE_FINISH_MIGRATE)) { + s->enable_poll_on_resume = true; + return; + } s->read_poll = enable; tap_update_fd_handler(s); } +static void tap_vm_state_change(void *opaque, bool running, RunState state) +{ + TAPState *s = opaque; + + if (running) { + if (s->enable_poll_on_resume) { + tap_read_poll(s, true); + s->enable_poll_on_resume = false; + } + } else if (state == RUN_STATE_FINISH_MIGRATE) { + if (s->read_poll) { + s->enable_poll_on_resume = true; + tap_read_poll(s, false); + } + } +} + static void tap_write_poll(TAPState *s, bool enable) { s->write_poll = enable; @@ -153,7 +213,7 @@ static ssize_t tap_receive_iov(NetClientState *nc, const struct iovec *iov, int iovcnt) { - TAPState *s = DO_UPCAST(TAPState, nc, nc); + TAPState *s = container_of(nc, TAPState, nc); const struct iovec *iovp = iov; g_autofree struct iovec *iov_copy = NULL; struct virtio_net_hdr hdr = { }; @@ -189,7 +249,7 @@ static void tap_send_completed(NetClientState *nc, ssize_t len) { - TAPState *s = DO_UPCAST(TAPState, nc, nc); + TAPState *s = container_of(nc, TAPState, nc); tap_read_poll(s, true); } @@ -249,7 +309,7 @@ static bool tap_has_ufo(NetClientState *nc) { - TAPState *s = DO_UPCAST(TAPState, nc, nc); + TAPState *s = container_of(nc, TAPState, nc); assert(nc->info->type == NET_CLIENT_DRIVER_TAP); @@ -258,7 +318,7 @@ static bool tap_has_uso(NetClientState *nc) { - TAPState *s = DO_UPCAST(TAPState, nc, nc); + TAPState *s = container_of(nc, TAPState, nc); assert(nc->info->type == NET_CLIENT_DRIVER_TAP); @@ -267,7 +327,7 @@ static bool tap_has_tunnel(NetClientState *nc) { - TAPState *s = DO_UPCAST(TAPState, nc, nc); + TAPState *s = container_of(nc, TAPState, nc); assert(nc->info->type == NET_CLIENT_DRIVER_TAP); return s->has_tunnel; @@ -275,7 +335,7 @@ static bool tap_has_vnet_hdr(NetClientState *nc) { - TAPState *s = DO_UPCAST(TAPState, nc, nc); + TAPState *s = container_of(nc, TAPState, nc); assert(nc->info->type == NET_CLIENT_DRIVER_TAP); @@ -289,7 +349,7 @@ static void tap_set_vnet_hdr_len(NetClientState *nc, int len) { - TAPState *s = DO_UPCAST(TAPState, nc, nc); + TAPState *s = container_of(nc, TAPState, nc); assert(nc->info->type == NET_CLIENT_DRIVER_TAP); @@ -300,21 +360,21 @@ static int tap_set_vnet_le(NetClientState *nc, bool is_le) { - TAPState *s = DO_UPCAST(TAPState, nc, nc); + TAPState *s = container_of(nc, TAPState, nc); return tap_fd_set_vnet_le(s->fd, is_le); } static int tap_set_vnet_be(NetClientState *nc, bool is_be) { - TAPState *s = DO_UPCAST(TAPState, nc, nc); + TAPState *s = container_of(nc, TAPState, nc); return tap_fd_set_vnet_be(s->fd, is_be); } static void tap_set_offload(NetClientState *nc, const NetOffloads *ol) { - TAPState *s = DO_UPCAST(TAPState, nc, nc); + TAPState *s = container_of(nc, TAPState, nc); if (s->fd < 0) { return; } @@ -335,7 +395,7 @@ static void tap_cleanup(NetClientState *nc) { - TAPState *s = DO_UPCAST(TAPState, nc, nc); + TAPState *s = container_of(nc, TAPState, nc); if (s->vhost_net) { vhost_net_cleanup(s->vhost_net); @@ -351,22 +411,29 @@ s->exit.notify = NULL; } + if (s->vmstate) { + qemu_del_vm_change_state_handler(s->vmstate); + s->vmstate = NULL; + } + tap_read_poll(s, false); tap_write_poll(s, false); close(s->fd); s->fd = -1; + + vmstate_unregister(VMSTATE_IF(s), &vmstate_tap, s); } static void tap_poll(NetClientState *nc, bool enable) { - TAPState *s = DO_UPCAST(TAPState, nc, nc); + TAPState *s = container_of(nc, TAPState, nc); tap_read_poll(s, enable); tap_write_poll(s, enable); } static bool tap_set_steering_ebpf(NetClientState *nc, int prog_fd) { - TAPState *s = DO_UPCAST(TAPState, nc, nc); + TAPState *s = container_of(nc, TAPState, nc); assert(nc->info->type == NET_CLIENT_DRIVER_TAP); return tap_fd_set_steering_ebpf(s->fd, prog_fd) == 0; @@ -374,7 +441,7 @@ int tap_get_fd(NetClientState *nc) { - TAPState *s = DO_UPCAST(TAPState, nc, nc); + TAPState *s = container_of(nc, TAPState, nc); assert(nc->info->type == NET_CLIENT_DRIVER_TAP); return s->fd; } @@ -386,11 +453,140 @@ */ static VHostNetState *tap_get_vhost_net(NetClientState *nc) { - TAPState *s = DO_UPCAST(TAPState, nc, nc); + TAPState *s = container_of(nc, TAPState, nc); assert(nc->info->type == NET_CLIENT_DRIVER_TAP); return s->vhost_net; } +static bool tap_is_wait_incoming(NetClientState *nc) +{ + TAPState *s = container_of(nc, TAPState, nc); + assert(nc->info->type == NET_CLIENT_DRIVER_TAP); + return s->fd == -1; +} + +static bool tap_pre_load(void *opaque, Error **errp) +{ + ERRP_GUARD(); + TAPState *s = opaque; + + if (s->fd != -1) { + error_setg(errp, + "TAP is already initialized and cannot receive " + "incoming fd"); + error_append_hint(errp, + "Migration parameter 'local' must be set" + " before creating the TAP device."); + return false; + } + + return true; +} + +static bool tap_setup_vhost(TAPState *s, Error **errp); + +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; + } + + if (!tap_setup_vhost(s, errp)) { + error_prepend(errp, + "Failed to setup vhost during TAP post-load: "); + return false; + } + + return true; +} + +static bool tap_needed(void *opaque) +{ + TAPState *s = opaque; + + return s->permit_local_migration && migrate_local(); +} + +static const VMStateDescription vmstate_tap = { + .name = "net-tap", + .priority = MIG_PRI_BACKEND, + .pre_load_errp = tap_pre_load, + .post_load_errp = tap_post_load, + .needed = tap_needed, + .fields = (const VMStateField[]) { + VMSTATE_FD(fd, TAPState), + VMSTATE_BOOL(using_vnet_hdr, TAPState), + VMSTATE_BOOL(has_ufo, TAPState), + VMSTATE_BOOL(has_uso, TAPState), + VMSTATE_BOOL(has_tunnel, TAPState), + VMSTATE_BOOL(enabled, TAPState), + VMSTATE_UINT32(host_vnet_hdr_len, TAPState), + VMSTATE_END_OF_LIST() + } +}; + +static char *tap_vmstate_if_get_id(VMStateIf *obj) +{ + TAPState *s = TAP_NETDEV(obj); + char *res = g_strdup_printf("%s/%d", s->nc.name, s->queue_index); + return res; +} + +static bool tap_get_permit_local_migration_prop(Object *obj, Error **errp) +{ + TAPState *s = TAP_NETDEV(obj); + return s->permit_local_migration; +} + +static void tap_set_permit_local_migration_prop(Object *obj, bool value, + Error **errp) +{ + TAPState *s = TAP_NETDEV(obj); + s->permit_local_migration = value; +} + +static void tap_instance_init(Object *obj) +{ + TAPState *s = TAP_NETDEV(obj); + s->permit_local_migration = false; +} + +static void tap_class_init(ObjectClass *klass, const void *data) +{ + VMStateIfClass *vc = VMSTATE_IF_CLASS(klass); + + vc->get_id = tap_vmstate_if_get_id; + + object_class_property_add_bool(klass, "x-permit-local-migration", + tap_get_permit_local_migration_prop, + tap_set_permit_local_migration_prop); +} + +static const TypeInfo tap_netdev_info = { + .name = TYPE_TAP_NETDEV, + .parent = TYPE_OBJECT, + .instance_size = sizeof(TAPState), + .instance_init = tap_instance_init, + .instance_post_init = object_apply_compat_props, + .class_init = tap_class_init, + .interfaces = (const InterfaceInfo[]) { + { TYPE_VMSTATE_IF }, + { } + }, +}; + +static void tap_net_client_destructor(NetClientState *nc) +{ + TAPState *s = container_of(nc, TAPState, nc); + object_unref(OBJECT(s)); +} + /* fd support */ static NetClientInfo net_tap_info = { @@ -410,22 +606,46 @@ .set_vnet_le = tap_set_vnet_le, .set_vnet_be = tap_set_vnet_be, .set_steering_ebpf = tap_set_steering_ebpf, + .is_wait_incoming = tap_is_wait_incoming, .get_vhost_net = tap_get_vhost_net, }; +static TAPState *new_tap(NetClientState *peer, + const char *model, + const char *name, + int queue_index, + bool has_permit_local_migration, + bool permit_local_migration) +{ + TAPState *s = TAP_NETDEV(object_new(TYPE_TAP_NETDEV)); + + qemu_net_client_setup(&s->nc, &net_tap_info, peer, model, name, + tap_net_client_destructor, true); + + s->queue_index = queue_index; + + if (has_permit_local_migration) { + s->permit_local_migration = permit_local_migration; + } + + vmstate_register(VMSTATE_IF(s), VMSTATE_INSTANCE_ID_ANY, &vmstate_tap, s); + + return s; +} + static TAPState *net_tap_fd_init(NetClientState *peer, const char *model, const char *name, int fd, - int vnet_hdr) + int vnet_hdr, + int queue_index, + bool has_permit_local_migration, + bool permit_local_migration) { NetOffloads ol = {}; - NetClientState *nc; - TAPState *s; - - nc = qemu_new_net_client(&net_tap_info, peer, model, name); - - s = DO_UPCAST(TAPState, nc, nc); + TAPState *s = new_tap(peer, model, name, queue_index, + has_permit_local_migration, + permit_local_migration); s->fd = fd; s->host_vnet_hdr_len = vnet_hdr ? sizeof(struct virtio_net_hdr) : 0; @@ -662,7 +882,7 @@ close(fd); return -1; } - s = net_tap_fd_init(peer, "bridge", name, fd, vnet_hdr); + s = net_tap_fd_init(peer, "bridge", name, fd, vnet_hdr, 0, true, false); qemu_set_info_str(&s->nc, "helper=%s,br=%s", helper, br); @@ -702,18 +922,56 @@ return fd; } +static bool tap_setup_vhost(TAPState *s, Error **errp) +{ + VhostNetOptions options; + + if (s->vhostfd == -1) { + return true; + } + + options.backend_type = VHOST_BACKEND_TYPE_KERNEL; + options.net_backend = &s->nc; + options.busyloop_timeout = s->vhost_busyloop_timeout; + options.opaque = (void *)(uintptr_t)s->vhostfd; + options.nvqs = 2; + options.feature_bits = kernel_feature_bits; + options.get_acked_features = NULL; + options.save_acked_features = NULL; + options.max_tx_queue_size = 0; + options.is_vhost_user = false; + + s->vhost_net = vhost_net_init(&options); + if (!s->vhost_net) { + error_setg(errp, + "vhost-net requested but could not be initialized"); + return false; + } + + /* vhostfd ownership is passed to s->vhost_net */ + s->vhostfd = -1; + + return true; +} + static bool net_init_tap_one(const NetdevTapOptions *tap, NetClientState *peer, const char *name, const char *ifname, const char *script, const char *downscript, int vhostfd, - int vnet_hdr, int fd, Error **errp) + int vnet_hdr, int fd, int queue_index, + Error **errp) { TAPState *s = net_tap_fd_init(peer, tap->helper ? "bridge" : "tap", - name, fd, vnet_hdr); + name, fd, vnet_hdr, queue_index, + tap->has_x_permit_local_migration, + tap->x_permit_local_migration); bool sndbuf_required = tap->has_sndbuf; 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; @@ -725,7 +983,7 @@ qemu_set_info_str(&s->nc, "helper=%s", tap->helper); } else { qemu_set_info_str(&s->nc, "ifname=%s,script=%s,downscript=%s", ifname, - script ?: "no", downscript ?: "no"); + script ?: "", downscript ?: ""); if (downscript) { snprintf(s->down_script, sizeof(s->down_script), "%s", downscript); @@ -736,42 +994,10 @@ } } - if (tap->has_vhost ? tap->vhost : - (vhostfd != -1) || (tap->has_vhostforce && tap->vhostforce)) { - VhostNetOptions options; - - options.backend_type = VHOST_BACKEND_TYPE_KERNEL; - options.net_backend = &s->nc; - if (tap->has_poll_us) { - options.busyloop_timeout = tap->poll_us; - } else { - options.busyloop_timeout = 0; - } - - if (vhostfd == -1) { - vhostfd = open("/dev/vhost-net", O_RDWR); - if (vhostfd < 0) { - error_setg_file_open(errp, errno, "/dev/vhost-net"); - goto failed; - } - if (!qemu_set_blocking(vhostfd, false, errp)) { - goto failed; - } - } - options.opaque = (void *)(uintptr_t)vhostfd; - options.nvqs = 2; - options.feature_bits = kernel_feature_bits; - options.get_acked_features = NULL; - options.save_acked_features = NULL; - options.max_tx_queue_size = 0; - options.is_vhost_user = false; - - s->vhost_net = vhost_net_init(&options); - if (!s->vhost_net) { - error_setg(errp, - "vhost-net requested but could not be initialized"); - goto failed; - } + s->vhostfd = vhostfd; + s->vhost_busyloop_timeout = tap->has_poll_us ? tap->poll_us : 0; + if (!tap_setup_vhost(s, errp)) { + return false; } return true; @@ -843,14 +1069,31 @@ static bool tap_parse_vhost_fds(const NetdevTapOptions *tap, int **vhost_fds, int queues, Error **errp) { - if (!(tap->vhostfd || tap->vhostfds)) { + bool need_vhost = tap->has_vhost ? tap->vhost : + ((tap->vhostfd || tap->vhostfds) || + (tap->has_vhostforce && tap->vhostforce)); + + if (!need_vhost) { *vhost_fds = NULL; return true; } - if (net_parse_fds(tap->vhostfd ?: tap->vhostfds, - vhost_fds, queues, errp) < 0) { - return false; + if (tap->vhostfd || tap->vhostfds) { + if (net_parse_fds(tap->vhostfd ?: tap->vhostfds, + vhost_fds, queues, errp) < 0) { + return false; + } + } else { + *vhost_fds = g_new(int, queues); + for (int i = 0; i < queues; i++) { + int vhostfd = open("/dev/vhost-net", O_RDWR); + if (vhostfd < 0) { + error_setg_file_open(errp, errno, "/dev/vhost-net"); + net_free_fds(*vhost_fds, i); + return false; + } + (*vhost_fds)[i] = vhostfd; + } } if (!unblock_fds(*vhost_fds, queues, errp)) { @@ -869,6 +1112,7 @@ /* for the no-fd, no-helper case */ char ifname[128]; int *fds = NULL, *vhost_fds = NULL; + bool incoming_fds; assert(netdev->type == NET_CLIENT_DRIVER_TAP); tap = &netdev->u.tap; @@ -891,6 +1135,23 @@ return -1; } + incoming_fds = tap->x_permit_local_migration && migrate_local() && + runstate_check(RUN_STATE_INMIGRATE); + + if (incoming_fds && + (tap->fd || tap->fds || tap->helper || tap->br || tap->ifname || + tap->has_sndbuf || tap->has_vnet_hdr || + !tap_is_explicit_no_script("script", tap->script) || + !tap_is_explicit_no_script("downscript", tap->downscript))) { + error_setg(errp, "Local incoming migration of TAP device (-incoming, " + "migration parameter @local is set, " + "TAP parameter @x-permit-local-migration is set) " + "is incompatible with " + "fd=, fds=, helper=, br=, ifname=, sndbuf= and vnet_hdr=, " + "and requires explicit empty script= and downscript="); + return -1; + } + queues = tap_parse_fds_and_queues(tap, &fds, errp); if (queues < 0) { return -1; @@ -909,7 +1170,22 @@ goto fail; } - if (fds) { + if (incoming_fds) { + for (i = 0; i < queues; i++) { + TAPState *s = new_tap(peer, "tap", name, i, + tap->has_x_permit_local_migration, + tap->x_permit_local_migration); + qemu_set_info_str(&s->nc, "incoming"); + + s->fd = -1; + if (vhost_fds) { + s->vhostfd = vhost_fds[i]; + s->vhost_busyloop_timeout = tap->has_poll_us ? tap->poll_us : 0; + } else { + s->vhostfd = -1; + } + } + } else if (fds) { for (i = 0; i < queues; i++) { if (i == 0) { vnet_hdr = tap_probe_vnet_hdr(fds[i], errp); @@ -925,15 +1201,16 @@ if (!net_init_tap_one(tap, peer, name, ifname, NULL, NULL, vhost_fds ? vhost_fds[i] : -1, - vnet_hdr, fds[i], errp)) { + vnet_hdr, fds[i], i, errp)) { goto fail; } } } else { g_autofree char *script = - tap_parse_script(tap->script, DEFAULT_NETWORK_SCRIPT); + tap_parse_script("script", tap->script, DEFAULT_NETWORK_SCRIPT); g_autofree char *downscript = - tap_parse_script(tap->downscript, DEFAULT_NETWORK_DOWN_SCRIPT); + tap_parse_script("downscript", tap->downscript, + DEFAULT_NETWORK_DOWN_SCRIPT); if (tap->ifname) { pstrcpy(ifname, sizeof ifname, tap->ifname); @@ -959,7 +1236,7 @@ i >= 1 ? NULL : script, i >= 1 ? NULL : downscript, vhost_fds ? vhost_fds[i] : -1, - vnet_hdr, fd, errp)) { + vnet_hdr, fd, i, errp)) { goto fail; } } @@ -976,7 +1253,7 @@ int tap_enable(NetClientState *nc) { - TAPState *s = DO_UPCAST(TAPState, nc, nc); + TAPState *s = container_of(nc, TAPState, nc); int ret; if (s->enabled) { @@ -993,7 +1270,7 @@ int tap_disable(NetClientState *nc) { - TAPState *s = DO_UPCAST(TAPState, nc, nc); + TAPState *s = container_of(nc, TAPState, nc); int ret; if (s->enabled == 0) { @@ -1008,3 +1285,10 @@ return ret; } } + +static void tap_register_types(void) +{ + type_register_static(&tap_netdev_info); +} + +type_init(tap_register_types)
diff --git a/qapi/machine.json b/qapi/machine.json index 2d63c1b..de6460f 100644 --- a/qapi/machine.json +++ b/qapi/machine.json
@@ -552,15 +552,43 @@ # accesses will go to a given interleave target. Accepted values # [256, 512, 1k, 2k, 4k, 8k, 16k] # +# @device-coherent: Window permits device-coherent (minimally HDM-D) +# accesses. (since 11.1) +# +# @host-only: Window permits host-only coherent (HDM-H) accesses. +# (since 11.1) +# +# @volatile: Window permits volatile memory. (since 11.1) +# +# @persistent: Window permits persistent memory. (since 11.1) +# +# @fixed-config: Window has a fixed device configuration. Advertised +# in the CEDT only; not otherwise emulated. (since 11.1) +# +# @back-invalidate: Window permits Back-Invalidate (HDM-DB). Implies +# @device-coherent. (since 11.1) +# # @targets: Target root bridge IDs from -device ...,id=<ID> for each # root bridge. # +# Coherency defaults to both device-coherent and host-only; disabling +# one (host-only=off or device-coherent=off) narrows to the other. +# @back-invalidate implies device-coherent and is not host-only. +# @volatile and @persistent default on. host-only coherent combined +# with @back-invalidate is rejected as undefined behavior. +# # Since: 7.1 ## { 'struct': 'CXLFixedMemoryWindowOptions', 'data': { 'size': 'size', '*interleave-granularity': 'size', + '*device-coherent': 'bool', + '*host-only': 'bool', + '*volatile': 'bool', + '*persistent': 'bool', + '*fixed-config': 'bool', + '*back-invalidate': 'bool', 'targets': ['str'] }} ##
diff --git a/qapi/migration.json b/qapi/migration.json index b1eaf7b..f7628b5 100644 --- a/qapi/migration.json +++ b/qapi/migration.json
@@ -830,7 +830,8 @@ 'zero-page-detection', 'direct-io', { 'name': 'x-rdma-chunk-size', 'features': [ 'unstable' ] }, - 'cpr-exec-command'] } + 'cpr-exec-command', + 'local'] } ## # @migrate-set-parameters: @@ -1012,10 +1013,20 @@ # Must be set to the same value on both source and destination # before migration starts. (Since 11.1) # +# @local: Permit the use of optimizations for local migration. This +# must only be set when either both the source and destination +# QEMU processes are directly connected with a UNIX domain socket +# as the migration channel (to enable use of file descriptor +# passing) or cpr-exec migration mode is enabled (this way file +# descriptors are inherited by new process). Individual device +# backends may need additional configuration flags set to enable +# local migration optimizations. This will be documented against +# the device backends where it applies. (Since 11.2) +# # Features: # -# @unstable: Members @x-checkpoint-delay, @x-rdma-chunk-size, and -# @x-vcpu-dirty-limit-period are experimental. +# @unstable: Members @local, @x-checkpoint-delay, @x-rdma-chunk-size, +# and @x-vcpu-dirty-limit-period are experimental. # # Since: 2.4 ## @@ -1053,7 +1064,9 @@ '*direct-io': 'bool', '*x-rdma-chunk-size': { 'type': 'uint64', 'features': [ 'unstable' ] }, - '*cpr-exec-command': [ 'str' ]} } + '*cpr-exec-command': [ 'str' ], + '*local': { 'type': 'bool', + 'features': [ 'unstable' ] } } } ## # @query-migrate-parameters:
diff --git a/qapi/net.json b/qapi/net.json index bb62600..b46af1f 100644 --- a/qapi/net.json +++ b/qapi/net.json
@@ -399,9 +399,21 @@ # @fds: multiple file descriptors of already opened multiqueue capable # tap # -# @script: script to initialize the interface +# @script: script to initialize the interface. An empty string +# disables script execution. Defaults to +# ``<sysconfdir>/qemu-ifup``, where ``<sysconfdir>`` is the +# system configuration directory at build time (typically /etc). +# Using "no" to disable script execution is deprecated (since +# 11.2); use an empty string instead. In a future version, "no" +# will be treated as a plain file name. # -# @downscript: script to shut down the interface +# @downscript: script to shut down the interface. An empty string +# disables script execution. Defaults to +# ``<sysconfdir>/qemu-ifdown``, where ``<sysconfdir>`` is the +# system configuration directory at build time (typically /etc). +# Using "no" to disable script execution is deprecated (since +# 11.2); use an empty string instead. In a future version, "no" +# will be treated as a plain file name. # # @br: bridge name (since 2.8) # @@ -425,6 +437,31 @@ # @poll-us: maximum number of microseconds that could be spent on busy # polling for tap (since 2.7) # +# @x-permit-local-migration: permit local migration for this TAP +# backend. When set, local migration is enabled/disabled by +# migration parameter @local for this TAP backend. When unset, +# migration parameter @local is ignored for this TAP backend. +# +# To be able to do incoming local migration of a TAP backend, +# migration parameter @local must be set _before_ creating the +# TAP backend. Otherwise, TAP backend is initialized as usual, +# opening/creating TAP devices in kernel. In this case further +# local incoming migration (with migration parameter @local set +# after creating TAP backend with @x-permit-local-migration +# parameter set) will simply fail. +# +# Moreover, when QEMU is in incoming migration state, migration +# parameter @local is set and @x-permit-local-migration is set, +# the following options are not supported and must not be set: +# @fd, @fds, @helper, @br, @ifname, @sndbuf, @vnet_hdr. +# Additionally, @script and @downscript must be explicitly +# disabled then. +# (default: false, since 11.2) +# +# Features: +# +# @unstable: Member @x-permit-local-migration is experimental. +# # Since: 1.2 ## { 'struct': 'NetdevTapOptions', @@ -443,7 +480,9 @@ '*vhostfds': 'str', '*vhostforce': 'bool', '*queues': 'uint32', - '*poll-us': 'uint32'} } + '*poll-us': 'uint32', + '*x-permit-local-migration': { + 'type': 'bool', 'features' : [ 'unstable'] } } } ## # @NetdevSocketOptions:
diff --git a/qemu-options.hx b/qemu-options.hx index 6c1713e..2f68631 100644 --- a/qemu-options.hx +++ b/qemu-options.hx
@@ -3022,7 +3022,8 @@ " use network scripts 'file' (default=" DEFAULT_NETWORK_SCRIPT ")\n" " to configure it and 'dfile' (default=" DEFAULT_NETWORK_DOWN_SCRIPT ")\n" " to deconfigure it\n" - " use '[down]script=no' to disable script execution\n" + " use '[down]script=' to disable script execution\n" + " ('[down]script=no' is deprecated and will be treated as a file name in future)\n" " use network helper 'helper' (default=" DEFAULT_BRIDGE_HELPER ") to\n" " configure it\n" " use 'fd=h' to connect to an already opened TAP interface\n" @@ -3558,9 +3559,13 @@ Use the network script file to configure it and the network script dfile to deconfigure it. If name is not provided, the OS automatically provides one. The default network configure script is - ``/etc/qemu-ifup`` and the default network deconfigure script is - ``/etc/qemu-ifdown``. Use ``script=no`` or ``downscript=no`` to - disable script execution. + ``<sysconfdir>/qemu-ifup`` and the default network deconfigure script is + ``<sysconfdir>/qemu-ifdown``, where ``<sysconfdir>`` is the system + configuration directory at build time (typically ``/etc``). + Use ``[down]script=`` to disable script execution. + Using ``[down]script=no`` is deprecated; it disables script + execution now, but in a future version it will be treated as a + plain file name. If running QEMU as an unprivileged user, use the network helper to configure the TAP interface and attach it to the bridge.
diff --git a/stubs/hotplug-stubs.c b/stubs/hotplug-stubs.c index 0f592ee..32b4af7 100644 --- a/stubs/hotplug-stubs.c +++ b/stubs/hotplug-stubs.c
@@ -14,19 +14,19 @@ #include "qemu/osdep.h" #include "hw/core/qdev.h" -HotplugHandler *qdev_get_hotplug_handler(DeviceState *dev) +const HotplugHandler *qdev_get_hotplug_handler(DeviceState *dev) { return NULL; } -void hotplug_handler_pre_plug(HotplugHandler *plug_handler, +void hotplug_handler_pre_plug(const HotplugHandler *plug_handler, DeviceState *plugged_dev, Error **errp) { g_assert_not_reached(); } -void hotplug_handler_plug(HotplugHandler *plug_handler, +void hotplug_handler_plug(const HotplugHandler *plug_handler, DeviceState *plugged_dev, Error **errp) {
diff --git a/subprojects/libvhost-user/libvhost-user.c b/subprojects/libvhost-user/libvhost-user.c index a74d814..2fed792 100644 --- a/subprojects/libvhost-user/libvhost-user.c +++ b/subprojects/libvhost-user/libvhost-user.c
@@ -948,6 +948,20 @@ vu_add_mem_reg(VuDev *dev, VhostUserMsg *vmsg) { VhostUserMemoryRegion m = vmsg->payload.memreg.region, *msg_region = &m; + /* + * If we are in postcopy mode and we receive a u64 payload with a 0 value + * we know all the postcopy client bases have been received, and we + * should start generating faults. This message carries no file + * descriptor, so it has to be recognised before the fd count of a real + * region is validated below. + */ + if (dev->postcopy_listening && + vmsg->size == sizeof(vmsg->payload.u64) && + vmsg->payload.u64 == 0) { + (void)generate_faults(dev); + return false; + } + if (vmsg->fd_num != 1) { vmsg_close_fds(vmsg); vu_panic(dev, "VHOST_USER_ADD_MEM_REG received %d fds - only 1 fd " @@ -971,23 +985,17 @@ return false; } - /* - * If we are in postcopy mode and we receive a u64 payload with a 0 value - * we know all the postcopy client bases have been received, and we - * should start generating faults. - */ - if (dev->postcopy_listening && - vmsg->size == sizeof(vmsg->payload.u64) && - vmsg->payload.u64 == 0) { - (void)generate_faults(dev); - return false; - } - _vu_add_mem_reg(dev, msg_region, vmsg->fds[0]); close(vmsg->fds[0]); if (dev->postcopy_listening) { - /* Send the message back to qemu with the addresses filled in. */ + /* + * Send the message back to qemu with the addresses filled in. + * _vu_add_mem_reg() worked on our copy of the region, so it has to be + * put back into the message payload. A pointer into the payload + * cannot be handed out instead, VhostUserMsg is packed. + */ + vmsg->payload.memreg.region = m; vmsg->fd_num = 0; DPRINT("Successfully added new region in postcopy\n"); return true;
diff --git a/system/qdev-monitor.c b/system/qdev-monitor.c index 5c87fda..a62ad23 100644 --- a/system/qdev-monitor.c +++ b/system/qdev-monitor.c
@@ -917,8 +917,8 @@ void qdev_unplug(DeviceState *dev, Error **errp) { - HotplugHandler *hotplug_ctrl; - HotplugHandlerClass *hdc; + const HotplugHandler *hotplug_ctrl; + const HotplugHandlerClass *hdc; Error *local_err = NULL; if (!qdev_hotunplug_allowed(dev, errp)) {
diff --git a/tests/data/acpi/x86/q35/CEDT.cxl b/tests/data/acpi/x86/q35/CEDT.cxl index c35f388..ff8203a 100644 --- a/tests/data/acpi/x86/q35/CEDT.cxl +++ b/tests/data/acpi/x86/q35/CEDT.cxl Binary files differ
diff --git a/tests/data/acpi/x86/q35/IVRS.ivrs b/tests/data/acpi/x86/q35/IVRS.ivrs index 7f9e91a..b3b809c 100644 --- a/tests/data/acpi/x86/q35/IVRS.ivrs +++ b/tests/data/acpi/x86/q35/IVRS.ivrs Binary files differ
diff --git a/tests/qtest/cxl-test.c b/tests/qtest/cxl-test.c index 8fb7e58..49d820c 100644 --- a/tests/qtest/cxl-test.c +++ b/tests/qtest/cxl-test.c
@@ -81,6 +81,13 @@ "-object memory-backend-file,id=lsa3,mem-path=%s,size=256M " \ "-device cxl-type3,bus=rp3,persistent-memdev=cxl-mem3,lsa=lsa3,id=pmem3 " +/* Single pxb + volatile type3 + one window; base for the CFMW tests. */ +#define QEMU_CFMW \ + "-machine q35,cxl=on " \ + "-device pxb-cxl,id=cxl.0,bus=pcie.0,bus_nr=52 " \ + QEMU_RP QEMU_T3D_VMEM \ + "-M cxl-fmw.0.targets.0=cxl.0,cxl-fmw.0.size=4G" + static void cxl_basic_hb(void) { qtest_start("-machine q35,cxl=on"); @@ -117,6 +124,81 @@ qtest_end(); } +/* + * CFMWS "Window Restrictions" (CXL r4.0 9.18.1.3) are configurable per + * window; check that valid restriction sets are accepted. + */ +static void cxl_cfmw_default(void) +{ + /* No coherency flag advertises both HDM-D and HDM-H. */ + qtest_start(QEMU_CFMW); + qtest_end(); +} + +static void cxl_cfmw_host_only_off(void) +{ + /* Disabling host-only leaves device-coherent (HDM-D). */ + qtest_start(QEMU_CFMW ",cxl-fmw.0.host-only=off"); + qtest_end(); +} + +static void cxl_cfmw_device_coherent_off(void) +{ + /* Disabling device-coherent leaves host-only (HDM-H). */ + qtest_start(QEMU_CFMW ",cxl-fmw.0.device-coherent=off"); + qtest_end(); +} + +static void cxl_cfmw_back_invalidate(void) +{ + /* Back-Invalidate implies device-coherent (HDM-DB). */ + qtest_start(QEMU_CFMW ",cxl-fmw.0.back-invalidate=on"); + qtest_end(); +} + +static void cxl_cfmw_fixed_config(void) +{ + qtest_start(QEMU_CFMW ",cxl-fmw.0.fixed-config=on"); + qtest_end(); +} + +static void cxl_cfmw_device_and_host(void) +{ + /* + * device-coherent and host-only may both be advertised (the spec + * permits multiple bits set); this must be accepted -- a regression + * guard against reinstating a device/host-only mutual exclusion. + */ + qtest_start(QEMU_CFMW + ",cxl-fmw.0.device-coherent=on,cxl-fmw.0.host-only=on"); + qtest_end(); +} + +static void cxl_cfmw_volatile_only(void) +{ + qtest_start(QEMU_CFMW ",cxl-fmw.0.persistent=off"); + qtest_end(); +} + +static void cxl_cfmw_persistent_only(void) +{ + qtest_start(QEMU_CFMW ",cxl-fmw.0.volatile=off"); + qtest_end(); +} + +/* Two windows in one VM: a Back-Invalidate window and a host-only window. */ +static void cxl_cfmw_two_windows(void) +{ + qtest_start("-machine q35,cxl=on " + "-device pxb-cxl,id=cxl.0,bus=pcie.0,bus_nr=52 " + "-device pxb-cxl,id=cxl.1,bus=pcie.0,bus_nr=53 " + QEMU_RP QEMU_T3D_VMEM + "-M cxl-fmw.0.targets.0=cxl.0,cxl-fmw.0.size=4G," + "cxl-fmw.0.back-invalidate=on," + "cxl-fmw.1.targets.0=cxl.1,cxl-fmw.1.size=4G"); + qtest_end(); +} + #ifdef CONFIG_POSIX static void cxl_t3d_deprecated(void) { @@ -233,6 +315,19 @@ qtest_add_func("/pci/cxl/pxb_x2_with_window", cxl_2pxb_with_window); qtest_add_func("/pci/cxl/rp", cxl_root_port); qtest_add_func("/pci/cxl/rp_x2", cxl_2root_port); + qtest_add_func("/pci/cxl/cfmw/default", cxl_cfmw_default); + qtest_add_func("/pci/cxl/cfmw/host_only_off", cxl_cfmw_host_only_off); + qtest_add_func("/pci/cxl/cfmw/device_coherent_off", + cxl_cfmw_device_coherent_off); + qtest_add_func("/pci/cxl/cfmw/back_invalidate", + cxl_cfmw_back_invalidate); + qtest_add_func("/pci/cxl/cfmw/fixed_config", cxl_cfmw_fixed_config); + qtest_add_func("/pci/cxl/cfmw/device_and_host", + cxl_cfmw_device_and_host); + qtest_add_func("/pci/cxl/cfmw/volatile_only", cxl_cfmw_volatile_only); + qtest_add_func("/pci/cxl/cfmw/persistent_only", + cxl_cfmw_persistent_only); + qtest_add_func("/pci/cxl/cfmw/two_windows", cxl_cfmw_two_windows); #ifdef CONFIG_POSIX qtest_add_func("/pci/cxl/type3_device", cxl_t3d_deprecated); qtest_add_func("/pci/cxl/type3_device_pmem", cxl_t3d_persistent);