[RFC PATCH 0/1] iommu/riscv: mark endpoint ATS-broken on ATS invalidation timeout
From: bingyu.xian
Date: Thu Jun 25 2026 - 05:35:01 EST
From: Bingyu Shan <shanbeeyoo@xxxxxxxxx>
This RFC proposes handling ATS invalidation timeout in the RISC-V IOMMU
driver, following the class of problem ARM SMMUv3 recently addressed as
"ATC invalidation timeout / quarantine": a PCIe endpoint that fails to
complete an ATS invalidation keeps stale ATC entries and can keep DMA-ing
through them.
The RISC-V IOMMU driver currently has no such handling.
riscv_iommu_cmd_sync() only logs "Hardware error: command execution
timeout" once and discards the failure, with no attribution to a device
and no policy response.
Patch 1 adds the first, conservative milestone:
* On a command-queue timeout, read CQH to find the offending command; if
it is an ATS.INVAL, extract its device-id (the RISC-V analog of ARM
SMMUv3 CERROR_ATC_INV_IDX) and schedule deferred marking.
* The marking worker sets a per-device ats_broken flag, increments a
counter, calls pci_disable_ats(), and logs. Deferred to a work item
because cmd_sync() may run with only RCU held while the device lookup
takes a sleeping mutex.
* riscv_iommu_enable_pdev() refuses pci_enable_ats() for an ats_broken
device, so a broken endpoint is not put back on the ATS path.
* Device tracking (the DID-keyed rbtree) is moved from the PRI-only path
to probe_device/release_device, so ATS-only devices are tracked and can
be attributed. This also fixes dev_to_iommu() not being usable inside
probe_device() (the iommu core only links dev->iommu after probe
returns), and a latent duplicate-devid double-unlock in the rbtree
insert.
* Debugfs fault injection (inject_ats_inval_timeout) and a state table
(ats_devices) so the marking path can be exercised. Real RISC-V IOMMU
hardware and unmodified QEMU complete ATS.INVAL synchronously, so a
native timeout cannot be reproduced against a real endpoint; the knob
schedules the same worker the real cmd_sync() timeout path would.
Full quarantine and PCI reset recovery are intentionally left out of this
step; this RFC is to discuss the timeout-handling policy.
Base / dependency
-----------------
This RFC is based on Tomasz Jeznach's riscv_iommu.next branch, because the
RISC-V IOMMU ATS support series (ATS / PRI / SVA) is not yet in the IOMMU
tree. It is intended to discuss design, not for immediate merge.
Base: tjeznach/riscv_iommu.next (Linux 6.15 + RISC-V ATS/PRI/SVA)
Not based on: iommu/linux.git next (no RISC-V ATS support yet)
Open question for the list: should the ATS invalidation timeout handling
be folded into the ATS support series itself, since an ATS implementation
that does not handle timeouts is arguably incomplete?
Validation environment
----------------------
QEMU: qemu-system-riscv64 10.0.0 (hw/riscv unmodified, stock)
Kernel base: tjeznach/riscv_iommu.next + this patch
Cross compiler: riscv64-linux-gnu-gcc 11.4.0
Kconfig: RISCV_IOMMU=y, RISCV_IOMMU_PCI=y, PCI_ATS/PASID/PRI=y,
VIRTIO_NET=y, VIRTIO_PCI=y, DEBUG_FS=y
Endpoint: stock -device virtio-net-pci,ats=on (BDF 0000:00:03.0,
IOMMU device-id 0x18 = PCI requester id dev<<3|func)
Boot cmdline: root=/dev/ram rw console=ttyS0 earlycon=sbi \
iommu.strict=1 fw_devlink=off
fw_devlink=off works around a platform-IOMMU deferred-probe on this QEMU
setup ("deferred probe pending: ... /cpus/cpu@0"). No QEMU patches are
used; the ATS endpoint and the IOMMU are stock.
Validation log
--------------
Boot is clean (no panic). The IOMMU probes and manages the endpoint:
[ 0.442968] riscv,iommu 3010000.iommu: using wire-signaled interrupts
[ 0.448530] pci 0000:00:00.0: Adding to iommu group 0
[ 0.448908] pci 0000:00:03.0: Adding to iommu group 1
[ 0.538245] virtio-pci 0000:00:03.0: enabling device (0000 -> 0003)
ats_devices before the trigger (both RC and virtio tracked via the rbtree
populated at probe):
devid device ats_broken ats_inval_timeouts
0x0 0000:00:00.0 0 0
0x18 0000:00:03.0 0 0
Inject an ATS.INVAL timeout for the virtio endpoint (DID 0x18) via the
debugfs knob, which schedules the same worker the real cmd_sync() timeout
path would:
Injecting ATS.INVAL timeout for devid 0x18 (24) (BDF 0000:00:03.0)
[ 6.910242] virtio-pci 0000:00:03.0: marked ATS-broken after ATS.INVAL timeout
ats_devices after the trigger:
devid device ats_broken ats_inval_timeouts
0x0 0000:00:00.0 0 0
0x18 0000:00:03.0 1 1
This confirms, on the real ATS path and with unmodified QEMU:
ATS.INVAL timeout -> DID 0x18 -> 0000:00:03.0 marked ATS-broken
(ats_broken=1, ats_inval_timeouts=1)
Caveats
-------
* The real cmd_sync() CQH->DID path is implemented but cannot be
triggered on unmodified QEMU (synchronous ATS.INVAL completion); the
debugfs knob schedules the same worker to validate the marking logic.
It would fire on real hardware or on a QEMU modelled to stall ATS.INVAL.
* pci_disable_ats() inside the worker was a no-op in this run: ATS was
not auto-enabled at boot (no "ats" sysfs attribute), so
info->ats_enabled was false. The disable and the ATS-enable guard are
correct by inspection but not dynamically triggered here.
* Stale-ATC data corruption (device keeps an old IOVA->PA mapping and
DMAs through it after a missed invalidation) is out of scope; that
needs an endpoint with a real device-side ATC, which stock virtio-pci
does not model.
Todo / open questions
---------------------
* Should this be split (rbtree tracking / timeout marking / debugfs) for
a non-RFC version?
* Quarantine vs. just "mark and refuse re-enable": what policy does the
list want?
* PCI reset recovery semantics.
* Re-base onto iommu/linux.git next once the ATS support series lands.
Bingyu Shan (1):
iommu/riscv: RFC: mark endpoint ATS-broken on ATS invalidation timeout
drivers/iommu/riscv/iommu.c | 210 ++++++++++++++++++++++++++++++++++--
drivers/iommu/riscv/iommu.h | 9 ++
2 files changed, 210 insertions(+), 9 deletions(-)
--
2.54.0