iommu/arm-smmu-v3: Add PRI support
Subsystems (3)
v5 cover iommu/arm-smmu-v3: Add PRI support
The SMMUv3 driver doesn't handle events on the PRI queue or respond to IOPF faults. This series adds the missing pieces, using the IOPF infrastructure, to convert PRI page requests into iopf_faults and issue CMDQ_OP_PRI_RESP.
The iopf_queue_flush_dev() contract requires the driver to first drain the hardware PRI queue and synchronize using a threaded IRQ handler before the IOPF software flush. This drove the additional commits compared to v1:
- arm_smmu_wait_for_queue_drained() drains the hardware queue by counting the entries that the threaded IRQ handler consumes
- arm_smmu_attach_release() moves the teardown outside the global lock
- synchronize_irq() closes the gap before the final flush
Note that, although this series is only about PRIQ and EVTQ, the Q_POS(), Q_DIFF() and arm_smmu_wait_for_queue_drained() helpers will be shared with Pranj's RPM series, per discussion below for CMDQ to use as well:
https://lore.kernel.org/all/[email protected]/
This is on Github:
https://github.com/nicolinc/iommufd/commits/smmuv3_pri-v5
FWIW, engineers on the NVIDIA side have managed to verify the PRI feature.
Changelog
v5:
* Add review tags from Jonathan
* Gate the PASID Stop Marker discard on SSV
* Follow coding-assistants.rst for the Assisted-by tags
* Sample the drain wait early in arm_smmu_wait_for_queue_drained(), for Sashiko finding:
https://sashiko.dev/#/patchset/cover.1789081084.git.nicolinc%40nvidia.com?part=4
v4:
https://lore.kernel.org/all/[email protected]/
* Add review tags from Jonathan
* Simplify verbose commit messages
* Use fsleep() in the drain poll loop
* Drop the goto in arm_smmu_set_pasid()
* Drop the WARN_ON() on a drain timeout
* Gate the evtq/priq drain on using_iopf
* Add a prm pointer in arm_smmu_page_response()
* Run impl_ops->device_disable() before SMMU shutdown
* Track a drain timeout in a bool instead of an errno
* Add a Q_DIFF() macro for the queue position differences
* Rename arm_smmu_drain_queue() to arm_smmu_wait_for_queue_drained()
* Split the fault report and the overflow discard into two patches
v3:
https://lore.kernel.org/all/[email protected]/
* Rebase on v7.3-rc1
* Drop FEAT_SVA gate on IOPF queue allocation
* Discard partial PRI faults on PRIQ overflow
* Refuse PRI on a PCIe master with stall_enabled
* Merge and ratelimit the unexpected PRI request prints
* Disable EVTQ/PRIQ/combined IRQs before iopf_queue_free()
* Reclaim a leaked IOPF enrollment in arm_smmu_release_device()
* Leak master_domain on a drain timeout instead of risking a UAF
* Fix the IOMMU_PAGE_RESP code mapping in arm_smmu_page_response()
* Rework arm_smmu_drain_queue() into a counting-based sleeping poll
* Split the fault event drain and the fault work flush into two patches
* Drop IOMMU_FAULT_PAGE_REQUEST_STALLS_TRANS; dispatch on per-master state
v2:
https://lore.kernel.org/all/[email protected]/
* Allocate evtq.iopf for ARM_SMMU_FEAT_PRI
* Pick up Jean's PRI stubs and PRI export patches
* Enable PRI for PCI devices in arm_smmu_probe_device()
* Add arm_smmu_drain_queue_for_iopf() for EVTQ and PRIQ
* Add arm_smmu_attach_release() to rework the IOPF drain
* Add IOMMU_FAULT_PAGE_REQUEST_STALLS_TRANS for STALL mode
* Gate pci_enable_pri() on FEAT_PRI plus a non-NULL evtq.iopf
* Deny unrecognised-StreamID PRG_LAST in arm_smmu_handle_ppr()
* Disable PRI when no IRQ handler is registered (unique or combined IRQ)
v1:
https://lore.kernel.org/all/[email protected]/
Jean-Philippe Brucker (2):
PCI/ATS: Add PRI stubs
PCI/ATS: Export pci_enable_pri() and pci_reset_pri()
Malak Marrid (1):
iommu/arm-smmu-v3: Submit CMDQ_OP_PRI_RESP for IOPF event
Nicolin Chen (12):
iommu/arm-smmu-v3: Disable the impl before disabling the SMMU on
shutdown
iommu/arm-smmu-v3: Add arm_smmu_attach_release() iommu/arm-smmu-v3: Add Q_POS() macro
iommu/arm-smmu-v3: Drain in-flight fault events on domain detach iommu/arm-smmu-v3: Flush in-flight fault work on domain detach iommu/arm-smmu-v3: Allocate IOPF queue without FEAT_SVA iommu/arm-smmu-v3: Disable the queue IRQs before disabling the SMMU iommu/arm-smmu-v3: Disable PRI when no IRQ handler is registered iommu/arm-smmu-v3: Support PRI Page Request in arm_smmu_handle_ppr() iommu/arm-smmu-v3: Discard partial PRI faults on PRIQ overflow iommu/arm-smmu-v3: Allocate IOPF queue for ARM_SMMU_FEAT_PRI iommu/arm-smmu-v3: Enable PRI for PCI device in
arm_smmu_probe_device()
drivers/iommu/arm/Kconfig | 2 + drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h | 7 + include/linux/pci-ats.h | 5 + .../arm/arm-smmu-v3/arm-smmu-v3-iommufd.c | 1 + drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c | 444 +++++++++++++++--- drivers/pci/ats.c | 2 + 6 files changed, 405 insertions(+), 56 deletions(-)
base-commit: cee9395acd8043be0644b25c34bfa86623f2b935
Signature · 1 line
2.43.0
PATCH v5 1/15 iommu/arm-smmu-v3: Disable the impl before disabling the SMMU on shutdown
arm_smmu_device_shutdown() only calls arm_smmu_device_disable(), and never the impl_ops->device_disable() hook. On tegra241-cmdqv, that hook quiesces every VINTF through tegra241_vintf_hw_deinit(), which issues a CMD_SYNC on the shared CMDQ while it is still enabled.
arm_smmu_disable_action() already runs this hook before disabling the SMMU, to cover an unbind or a probe failure, and no devres release ever follows a reboot or a kexec to run it again. Have arm_smmu_device_shutdown() call it directly, instead of reimplementing part of what it already does.
--- drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c +1 -1
@@ -5633,7 +5633,7 @@ static void arm_smmu_device_shutdown(struct platform_device *pdev) {struct arm_smmu_device *smmu =platform_get_drvdata (pdev); -arm_smmu_device_disable (smmu); +arm_smmu_disable_action (smmu); }static const struct of_device_id arm_smmu_of_match[] = {
Signature · 1 line
2.43.0
[email protected]
AI reviewRe: [PATCH v5 01/15] iommu/arm-smmu-v3: Disable the impl before disabling the SMMU on shutdown
Sashiko has reviewed this patch and found no issues. It looks great!
Quoted 10 lines
arm_smmu_device_shutdown()only callsarm_smmu_device_disable(), and never theimpl_ops->device_disable()hook. On tegra241-cmdqv, that hook quiesces every VINTF throughtegra241_vintf_hw_deinit(), which issues aCMD_SYNCon the shared CMDQ while it is still enabled.
arm_smmu_disable_action()already runs this hook before disabling the SMMU, to cover an unbind or a probe failure, and no devres release ever follows a reboot or a kexec to run it again. Havearm_smmu_device_shutdown()call it directly, instead of reimplementing part of what it already does.
Quoted 5 lines
Fixes 9ff145a25c5c ("iommu/tegra241-cmdqv: Fix CMD_SYNC use-after-free on teardown")Reviewed-by Jonathan Cameron <[email protected]>Assisted-by LLMSigned-off-by Nicolin Chen <[email protected]>
Sashiko has reviewed this patch and found no issues. It looks great!
Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=1
Re: [PATCH v5 01/15] iommu/arm-smmu-v3: Disable the impl before disabling the SMMU on shutdown
In reply to Nicolin ChenQuoted 11 lines · Nicolin Chen
arm_smmu_device_shutdown()only callsarm_smmu_device_disable(), and never theimpl_ops->device_disable()hook. On tegra241-cmdqv, that hook quiesces every VINTF throughtegra241_vintf_hw_deinit(), which issues aCMD_SYNCon the shared CMDQ while it is still enabled.
arm_smmu_disable_action()already runs this hook before disabling the SMMU, to cover an unbind or a probe failure, and no devres release ever follows a reboot or a kexec to run it again. Havearm_smmu_device_shutdown()call it directly, instead of reimplementing part of what it already does.[...]
Signature · 1 line
Jason
PATCH v5 2/15 iommu/arm-smmu-v3: Add arm_smmu_attach_release()
In reply to Nicolin ChenThe IOPF teardown is done in arm_smmu_remove_master_domain() when releasing the master_domain on detach, under the global arm_smmu_asid_lock mutex.
A later change will add an IOPF workqueue flush to that teardown, which can block on a user-faulting page-fault handler. Holding the arm_smmu_asid_lock across it would stall every unrelated attachment in the system.
Split the teardown out of arm_smmu_remove_master_domain(), to a new helper arm_smmu_attach_release() that runs after arm_smmu_asid_lock is released.
No functional change: the old master_domain belongs to no other device, so freeing it outside the lock stays safe, still under iommu_group->mutex.
--- drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h | 2 + .../arm/arm-smmu-v3/arm-smmu-v3-iommufd.c | 1 + drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c | 44 ++++++++++++++----- 3 files changed, 36 insertions(+), 11 deletions(-)
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h +2 -0
@@ -1204,12 +1204,14 @@ struct arm_smmu_attach_state {struct arm_smmu_vmaster *vmaster;struct arm_smmu_inv_state old_domain_invst;struct arm_smmu_inv_state new_domain_invst; +struct arm_smmu_master_domain *old_master_domain;bool ats_enabled; };int arm_smmu_attach_prepare (struct arm_smmu_attach_state *state,struct iommu_domain *new_domain);void arm_smmu_attach_commit (struct arm_smmu_attach_state *state); +void arm_smmu_attach_release (struct arm_smmu_attach_state *state);void arm_smmu_install_ste_for_dev (struct arm_smmu_master *master,const struct arm_smmu_ste *target);
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-iommufd.c +1 -0
@@ -194,6 +194,7 @@ static int arm_smmu_attach_dev_nested(struct iommu_domain *domain,arm_smmu_install_ste_for_dev (master, &ste);arm_smmu_attach_commit (&state);mutex_unlock (&arm_smmu_asid_lock); +arm_smmu_attach_release (&state);return 0 ; }
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c +33 -11
103 lines not on this page · raw message
Signature · 1 line
2.43.0
[email protected]
AI reviewRe: [PATCH v5 02/15] iommu/arm-smmu-v3: Add arm_smmu_attach_release()
Sashiko has reviewed this patch and found no issues. It looks great!
Quoted 7 lines
The IOPF teardown is done in
arm_smmu_remove_master_domain()when releasing themaster_domainon detach, under the globalarm_smmu_asid_lockmutex.A later change will add an IOPF workqueue flush to that teardown, which can block on a user-faulting page-fault handler. Holding the
arm_smmu_asid_lockacross it would stall every unrelated attachment in the system.
Quoted 8 lines
Split the teardown out of
arm_smmu_remove_master_domain(), to a new helperarm_smmu_attach_release()that runs afterarm_smmu_asid_lockis released.No functional change: the old
master_domainbelongs to no other device, so freeing it outside the lock stays safe, still underiommu_group->mutex.Reviewed-by Jonathan Cameron <[email protected]>Signed-off-by Nicolin Chen <[email protected]>
Sashiko has reviewed this patch and found no issues. It looks great!
Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=2
Re: [PATCH v5 02/15] iommu/arm-smmu-v3: Add arm_smmu_attach_release()
In reply to Nicolin ChenQuoted 8 lines · Nicolin Chen
The IOPF teardown is done in
arm_smmu_remove_master_domain()when releasing themaster_domainon detach, under the globalarm_smmu_asid_lockmutex.A later change will add an IOPF workqueue flush to that teardown, which can block on a user-faulting page-fault handler. Holding the
arm_smmu_asid_lockacross it would stall every unrelated attachment in the system.[...]
Signature · 1 line
Jason
PATCH v5 3/15 iommu/arm-smmu-v3: Add Q_POS() macro
In reply to Nicolin ChenA queue position, the wrap bit combined with the index, is Q_WRP | Q_IDX. It is a (max_n_shift + 1)-bit value that wraps at twice the queue capacity. queue_inc_cons(), queue_sync_cons_ovf(), and queue_inc_prod_n() currently compute such positions by open-coding the two macros at each call site.
Add a Q_POS() macro and switch the open-coded sites to it.
A subsequent change will build a Q_DIFF() on it, to count the entries that a queue pointer moved past.
No functional change intended.
--- drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h | 2 ++ drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c | 11 +++++------ 2 files changed, 7 insertions(+), 6 deletions(-)
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h +2 -0
@@ -187,6 +187,8 @@ struct arm_vsmmu;#define Q_IDX (llq, p) ((p) & ((1 << (llq)->max_n_shift) - 1))#define Q_WRP (llq, p) ((p) & (1 << (llq)->max_n_shift)) +/* A position is Q_WRP | Q_IDX, wrapping at twice the queue capacity */ +#define Q_POS (llq, p) (Q_WRP(llq, p) | Q_IDX(llq, p))#define Q_OVERFLOW_FLAG (1U << 31)#define Q_OVF (p) ((p) & Q_OVERFLOW_FLAG)#define Q_ENT (q, p) ((q)->base + \
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c +5 -6
@@ -181,8 +181,8 @@ static void queue_sync_cons_out(struct arm_smmu_queue *q)static void queue_inc_cons (struct arm_smmu_ll_queue *q) { -u32 cons = (Q_WRP (q, q->cons) |Q_IDX (q, q->cons)) +1 ; - q->cons =Q_OVF (q->cons) |Q_WRP (q, cons) |Q_IDX (q, cons); +u32 cons =Q_POS (q, q->cons) +1 ; + q->cons =Q_OVF (q->cons) |Q_POS (q, cons); }static void queue_sync_cons_ovf (struct arm_smmu_queue *q)
@@ -192,8 +192,7 @@ static void queue_sync_cons_ovf(struct arm_smmu_queue *q)if (likely (Q_OVF (llq->prod) ==Q_OVF (llq->cons)))return ; - llq->cons =Q_OVF (llq->prod) |Q_WRP (llq, llq->cons) | -Q_IDX (llq, llq->cons); + llq->cons =Q_OVF (llq->prod) |Q_POS (llq, llq->cons);queue_sync_cons_out (q); }
@@ -218,8 +217,8 @@ static int queue_sync_prod_in(struct arm_smmu_queue *q)static u32 queue_inc_prod_n (struct arm_smmu_ll_queue *q,int n) { -u32 prod = (Q_WRP (q, q->prod) |Q_IDX (q, q->prod)) + n; -return Q_OVF (q->prod) |Q_WRP (q, prod) |Q_IDX (q, prod); +u32 prod =Q_POS (q, q->prod) + n; +return Q_OVF (q->prod) |Q_POS (q, prod); } static void queue_poll_init(struct arm_smmu_device *smmu,
Signature · 1 line
2.43.0
[email protected]
AI reviewRe: [PATCH v5 03/15] iommu/arm-smmu-v3: Add Q_POS() macro
Sashiko has reviewed this patch and found no issues. It looks great!
Quoted 7 lines
A queue position, the wrap bit combined with the index, is
Q_WRP|Q_IDX. It is a (max_n_shift+ 1)-bit value that wraps at twice the queue capacity.queue_inc_cons(),queue_sync_cons_ovf(), andqueue_inc_prod_n()currently compute such positions by open-coding the two macros at each call site.Add a
Q_POS()macro and switch the open-coded sites to it.
Quoted 7 lines
A subsequent change will build a
Q_DIFF()on it, to count the entries that a queue pointer moved past.No functional change intended.
Assisted-by LLMSigned-off-by Nicolin Chen <[email protected]>
Sashiko has reviewed this patch and found no issues. It looks great!
Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=3
Re: [PATCH v5 03/15] iommu/arm-smmu-v3: Add Q_POS() macro
In reply to Nicolin ChenQuoted 7 lines · On Tue, 15 Sep 2026 09:38:15 -0700 Nicolin Chen <[email protected]> wrote:
A queue position, the wrap bit combined with the index, is
Q_WRP|Q_IDX. It is a (max_n_shift+ 1)-bit value that wraps at twice the queue capacity.queue_inc_cons(),queue_sync_cons_ovf(), andqueue_inc_prod_n()currently compute such positions by open-coding the two macros at each call site.Add a
Q_POS()macro and switch the open-coded sites to it.
Quoted 7 lines
A subsequent change will build a
Q_DIFF()on it, to count the entries that a queue pointer moved past.No functional change intended.
Assisted-by LLMSigned-off-by Nicolin Chen <[email protected]>
PATCH v5 4/15 iommu/arm-smmu-v3: Drain in-flight fault events on domain detach
In reply to Nicolin ChenWhen a device leaves a domain, fault events for the old domain may remain in the SMMU event queue or the IOPF workqueue. If the IOMMU core frees that domain before those events are handled, the work may use freed memory.
Start with the hardware queue by using arm_smmu_wait_for_queue_drained() to count entries consumed by the threaded IRQ handler, and poll the EVTQ when an IOPF-enabled attachment ends. This prevents a pending IRQ from queuing old-domain work after the drain. Its until_empty mode can drain the CMDQ as well during suspend and runtime PM.
queue_poll() cannot be used because it is an atomic busy-wait that expects hardware to consume entries. The EVTQ and PRIQ are drained by threaded IRQ handlers, so a busy-wait could starve a handler sharing the same CPU on a non-preemptible kernel. The new helper sleeps, and might_sleep() catches an atomic-context caller even when the queue is already empty.
Sample the deadline ahead of the queue state that it judges, and honour it only after the exit conditions, so a drain that completes while this poll is preempted still returns success instead of a spurious timeout. This is the same ordering that poll_timeout_us() guarantees.
Note that a drained event is dequeued, but not necessarily handled, since queue_remove_raw() moves the MMIO CONS before the threaded IRQ handler gets to push the event onto the IOPF workqueue. A subsequent change will invoke synchronize_irq() and iopf_queue_flush_dev() to close that gap, and it will act on the errno of a timed-out drain too.
--- drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h | 2 + drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c | 101 ++++++++++++++++++++ 2 files changed, 103 insertions(+)
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h +2 -0
@@ -189,6 +189,8 @@ struct arm_vsmmu;#define Q_WRP (llq, p) ((p) & (1 << (llq)->max_n_shift))/* A position is Q_WRP | Q_IDX, wrapping at twice the queue capacity */ #define Q_POS (llq, p) (Q_WRP(llq, p) | Q_IDX(llq, p)) +/* Entries between two positions, i.e. how far @b leads @a */ +#define Q_DIFF (llq, a, b) Q_POS(llq, (b) - (a))#define Q_OVERFLOW_FLAG (1U << 31)#define Q_OVF (p) ((p) & Q_OVERFLOW_FLAG)#define Q_ENT (q, p) ((q)->base + \
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c +101 -0
119 lines not on this page · raw message
Signature · 1 line
2.43.0
[email protected]
AI reviewRe: [PATCH v5 04/15] iommu/arm-smmu-v3: Drain in-flight fault events on domain detach
Sashiko has reviewed this patch and found no issues. It looks great!
Quoted 15 lines
When a device leaves a domain, fault events for the old domain may remain in the SMMU event queue or the IOPF workqueue. If the IOMMU core frees that domain before those events are handled, the work may use freed memory.
Start with the hardware queue by using
arm_smmu_wait_for_queue_drained()to count entries consumed by the threaded IRQ handler, and poll the EVTQ when an IOPF-enabled attachment ends. This prevents a pending IRQ from queuing old-domain work after the drain. Itsuntil_emptymode can drain the CMDQ as well during suspend and runtime PM.
queue_poll()cannot be used because it is an atomic busy-wait that expects hardware to consume entries. The EVTQ and PRIQ are drained by threaded IRQ handlers, so a busy-wait could starve a handler sharing the same CPU on a non-preemptible kernel. The new helper sleeps, andmight_sleep()catches an atomic-context caller even when the queue is already empty.
Quoted 6 lines
[ ... ]Fixes cfea71aea921 ("iommu/arm-smmu-v3: Put iopf enablement in the domain attach path")Cc [email protected] # v6.16Reviewed-by Jonathan Cameron <[email protected]>Assisted-by LLMSigned-off-by Nicolin Chen <[email protected]>
Sashiko has reviewed this patch and found no issues. It looks great!
Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=4
Re: [PATCH v5 04/15] iommu/arm-smmu-v3: Drain in-flight fault events on domain detach
In reply to Nicolin Chendrivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c static int arm_smmu_cmdq_batch_submit(struct arm_smmu_device *smmu, · line 982
Quoted 5 lines
[ ... 73 lines skipped ... ]
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c +4
-0+static int arm_smmu_wait_for_queue_drained (struct arm_smmu_device *smmu, +struct arm_smmu_queue *q, +bool until_empty) +{
Nothing uses until_empty = false ?
Is that for the power management series? How does it make sense? Shouldn't we already know the queue is not seeing new entries in that case?
Quoted 19 lines
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c +19
-0+ktime_t timeout =ktime_add_us (ktime_get (),ARM_SMMU_POLL_TIMEOUT_US ); +u32 cons, prod, pending; +u32 drained =0 ; + +might_sleep (); + + cons =readl_relaxed (q->cons_reg); + prod =readl_relaxed (q->prod_reg); +/* The exit target: the number of entries in the queue at entry */ + pending =Q_DIFF (&q->llq, cons, prod); + +while (true) { +u32 prev, undrained; +bool expired; + +/* +* Sample the deadline ahead of the queue state it judges, but +* break only after the exit conditions below, so a queue that +* drained during a long preemption still exits with a success.
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c static int arm_smmu_cmdq_batch_submit(struct arm_smmu_device *smmu, · line 1011
Quoted 10 lines
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c +10
-0+*/ + expired =ktime_compare (ktime_get (), timeout) >0 ; + +/* Accumulate the entries consumed since the last poll */ + prev = cons; + cons =readl_relaxed (q->cons_reg); + drained +=Q_DIFF (&q->llq, prev, cons); + + prod =readl_relaxed (q->prod_reg); + undrained =Q_DIFF (&q->llq, cons, prod);
I'm not sure how this all can work, the queue is running on its own with some other CPU handling interrupts.
You can't do this sort of Q_DIFF math unless you've somehow guaranteed one side of the queue is stable for this logic. If both pointers are moving forward then the points pointers can progress and wrap without this noticing that happened. That will lock up.
Can you just replace this whole function with:
static void arm_smmu_irq_thread_fence(struct arm_smmu_device *smmu,
unsigned int irq)
{
if (!irq)
return;
irq_wake_thread(irq, smmu);
synchronize_irq(irq);
}?
This forces the thread to run and waits for it to finish. Since the thread fully drains the queue at the moment it starts, that should be sufficient?
But I wonder if the point of this has been lost? Prior to calling the driver attach functions the core code already changes the xarray:
curr = xa_cmpxchg(&group->pasid_array, pasid, NULL, XA_ZERO_ENTRY, GFP_KERNEL);
That immediately makes the threaded IRQ safe since it calls iommu_attach_handle_get() which now fails.
So all that is needed is to synchronize_irq() to make sure the irq thread sees the xa update
Then to flush the workqueue that iommu_report_device_fault() pushes into.
We don't need to do anything with the HW queue.
Signature · 1 line
Jason
Re: [PATCH v5 04/15] iommu/arm-smmu-v3: Drain in-flight fault events on domain detach
Quoted 7 lines · Jason Gunthorpe
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c static int arm_smmu_cmdq_batch_submit(struct arm_smmu_device *smmu, · line 982
Quoted 5 lines
[ ... 73 lines skipped ... ]
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c +4
-0+static int arm_smmu_wait_for_queue_drained (struct arm_smmu_device *smmu, +struct arm_smmu_queue *q, +bool until_empty) +{Nothing uses
until_empty= false ?
This series (PRIQ/EVTQ) use until_empty=false.
Quoted 3 lines
Is that for the power management series? How does it make sense? Shouldn't we already know the queue is not seeing new entries in that case?
The RPM series (CMDQ) uses until_empty=true, as it knows that there are no new entries.
Quoted 33 lines
Quoted 19 lines
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c +19
-0+ktime_t timeout =ktime_add_us (ktime_get (),ARM_SMMU_POLL_TIMEOUT_US ); +u32 cons, prod, pending; +u32 drained =0 ; + +might_sleep (); + + cons =readl_relaxed (q->cons_reg); + prod =readl_relaxed (q->prod_reg); +/* The exit target: the number of entries in the queue at entry */ + pending =Q_DIFF (&q->llq, cons, prod); + +while (true) { +u32 prev, undrained; +bool expired; + +/* +* Sample the deadline ahead of the queue state it judges, but +* break only after the exit conditions below, so a queue that +* drained during a long preemption still exits with a success. drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c static int arm_smmu_cmdq_batch_submit(struct arm_smmu_device *smmu, · line 1011
Quoted 10 lines
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c +10
-0+*/ + expired =ktime_compare (ktime_get (), timeout) >0 ; + +/* Accumulate the entries consumed since the last poll */ + prev = cons; + cons =readl_relaxed (q->cons_reg); + drained +=Q_DIFF (&q->llq, prev, cons); + + prod =readl_relaxed (q->prod_reg); + undrained =Q_DIFF (&q->llq, cons, prod);I'm not sure how this all can work, the queue is running on its own with some other CPU handling interrupts.
Quoted 4 lines
You can't do this sort of
Q_DIFFmath unless you've somehow guaranteed one side of the queue is stable for this logic. If both pointers are moving forward then the points pointers can progress and wrap without this noticing that happened. That will lock up.
One side of the queue (pointers) is actually stable. EVTQ/PRIQ uses the snapshot mode (until_empty=false):
* With @until_empty == false (for EVTQ/PRIQ), exit once "drained" reaches its
* target: "pending" (i.e. prod0 - cons0, frozen at the entry time):
*
* cons0 cons prod0 (prod)
* |<---- drained ---->| | |
* ---+###################+=====================+=============+--->
* |<--------------- pending --------------->|
"cons0", "prod0", and "pending" are frozen/stable at the start; the function only needs to track a moving "cons".
Arguably the other model for CMDQ (until_empty=true) does have two moving pointers in theory:
* With @until_empty == true (for CMDQ), exit once the queue is observed empty:
*
* cons0 cons prod
* | | |
* ---+###################+===================================+--->
* |<--------- undrained==0? --------->|
But in its practical use case (RPM), as you pointed out, there would not be new entries. IOW, its "prod" is fixed/stable; only "cons" is moving.
Quoted 11 lines
Can you just replace this whole function with:
static void arm_smmu_irq_thread_fence(struct
arm_smmu_device*smmu,
unsigned int irq){ if (!irq) return; irq_wake_thread(irq, smmu); synchronize_irq(irq); }
Quoted 5 lines
?
This forces the thread to run and waits for it to finish. Since the thread fully drains the queue at the moment it starts, that should be sufficient?
Ah, that seems legit to me. The point is to make sure no pending IRQ on the HW queues, so everything is drained. And this likely suffices.
Yea, I will try that one!
Quoted 8 lines
But I wonder if the point of this has been lost? Prior to calling the driver attach functions the core code already changes the xarray:
curr = xa_cmpxchg(&group->pasid_array, pasid, NULL, XA_ZERO_ENTRY, GFP_KERNEL);That immediately makes the threaded IRQ safe since it calls
iommu_attach_handle_get()which now fails.
I am not sure about that. Looking at iommufd_hwpt_replace_device(), there can be a old_handle != NULL, in which case the cmpxchg() would not change the xarray?
Quoted 7 lines
So all that is needed is to
synchronize_irq()to make sure the irq thread sees the xa updateThen to flush the workqueue that
iommu_report_device_fault()pushes into.We don't need to do anything with the HW queue.
FWIW, the idea of HW drain came from intel_iommu_drain_pasid_prq()..
Nicolin
Re: [PATCH v5 04/15] iommu/arm-smmu-v3: Drain in-flight fault events on domain detach
Quoted 10 lines · Nicolin Chen
Quoted 7 lines
I'm not sure how this all can work, the queue is running on its own with some other CPU handling interrupts.
You can't do this sort of
Q_DIFFmath unless you've somehow guaranteed one side of the queue is stable for this logic. If both pointers are moving forward then the points pointers can progress and wrap without this noticing that happened. That will lock up.One side of the queue (pointers) is actually stable. EVTQ/PRIQ uses the snapshot mode (until_empty=false):
It isn't stable, just because this reads it once doesn't mean the actual values are not changing, which is the point.
If one of the pointers is held stable then the HW cannot advance its value past it.
If both are advancing all bets are off and you have no idea how the values are related to each other since everything is modulo the ring size.
For instance you can read cons0=10, then you read prod=15, then you next read prod=11. What does that mean? It means since cons was actually advancing prod & cons went around the whole ring and wrapped.
You could only do tricks like this if you had full 64 bit counters, not truncated versions with modulo that can wrap quickly.
Quoted 8 lines
Quoted 8 lines
But I wonder if the point of this has been lost? Prior to calling the driver attach functions the core code already changes the xarray:
curr = xa_cmpxchg(&group->pasid_array, pasid, NULL, XA_ZERO_ENTRY, GFP_KERNEL);That immediately makes the threaded IRQ safe since it calls
iommu_attach_handle_get()which now fails.
Hmm, actually that's a sneaky cmpxchg that is only doing reserve..
Quoted 3 lines
I am not sure about that. Looking at
iommufd_hwpt_replace_device(), there can be aold_handle!= NULL, in which case thecmpxchg()would not change the xarray?
I think this is wrong, there is no way it can work like this where the attach continues to see the to-be-detached domain across the flushes. No amount of flushing can fix it.
Somehow we broke it :\
Quoted 9 lines
Quoted 7 lines
So all that is needed is to
synchronize_irq()to make sure the irq thread sees the xa updateThen to flush the workqueue that
iommu_report_device_fault()pushes into.We don't need to do anything with the HW queue.
FWIW, the idea of HW drain came from
intel_iommu_drain_pasid_prq()..
Yeah, but I think they might have over done it too..
Jason
Re: [PATCH v5 04/15] iommu/arm-smmu-v3: Drain in-flight fault events on domain detach
Quoted 9 lines · Jason Gunthorpe
Quoted 8 lines · Nicolin Chen
Quoted 7 lines
I'm not sure how this all can work, the queue is running on its own with some other CPU handling interrupts.
You can't do this sort of
Q_DIFFmath unless you've somehow guaranteed one side of the queue is stable for this logic. If both pointers are moving forward then the points pointers can progress and wrap without this noticing that happened. That will lock up.
Quoted 8 lines
Quoted 2 lines
One side of the queue (pointers) is actually stable. EVTQ/PRIQ uses the snapshot mode (until_empty=false):
It isn't stable, just because this reads it once doesn't mean the actual values are not changing, which is the point.
If one of the pointers is held stable then the HW cannot advance its value past it.
Oh, HW pointers cannot be stable, as EVTQ and EVTQ are shared with other devices that could constantly add new entries onto the queue.
The stunt was to pick a snapshot of CONS/PROD, while HW CONS/PROD are still advancing. And it would only wait for the length of that snapshot. The device is detached, so any event after that must be irrelevant.
Quoted 11 lines
If both are advancing all bets are off and you have no idea how the values are related to each other since everything is modulo the ring size.
For instance you can read cons0=10, then you read prod=15, then you next read prod=11. What does that mean? It means since cons was actually advancing prod & cons went around the whole ring and wrapped.
You could only do tricks like this if you had full 64 bit counters, not truncated versions with modulo that can wrap quickly.
The Q_DIFF was calculated including the WRAP bits. So, it wouldn't be a problem when any pointer wraps (once).
There is a problem, however, if one of them wraps twice (i.e. 2 x queue size): then it would miss the exit at the target length even if it is already much longer; and the penalty would be a timeout, yet at that moment the queue is definitely drained.
Or maybe I am still missing a key point?
Quoted 15 lines
Quoted 8 lines
Quoted 8 lines
But I wonder if the point of this has been lost? Prior to calling the driver attach functions the core code already changes the xarray:
curr = xa_cmpxchg(&group->pasid_array, pasid, NULL, XA_ZERO_ENTRY, GFP_KERNEL);That immediately makes the threaded IRQ safe since it calls
iommu_attach_handle_get()which now fails.Hmm, actually that's a sneaky cmpxchg that is only doing reserve..
Quoted 3 lines
I am not sure about that. Looking at
iommufd_hwpt_replace_device(), there can be aold_handle!= NULL, in which case thecmpxchg()would not change the xarray?
Quoted 5 lines
I think this is wrong, there is no way it can work like this where the attach continues to see the to-be-detached domain across the flushes. No amount of flushing can fix it.
Somehow we broke it :\
Hmm, I will take a deeper look.
Quoted 11 lines
Quoted 9 lines
Quoted 7 lines
So all that is needed is to
synchronize_irq()to make sure the irq thread sees the xa updateThen to flush the workqueue that
iommu_report_device_fault()pushes into.We don't need to do anything with the HW queue.
FWIW, the idea of HW drain came from
intel_iommu_drain_pasid_prq()..Yeah, but I think they might have over done it too..
I see.
Thanks
Nicolin
Re: [PATCH v5 04/15] iommu/arm-smmu-v3: Drain in-flight fault events on domain detach
Quoted 4 lines · Nicolin Chen
There is a problem, however, if one of them wraps twice (i.e. 2 x queue size): then it would miss the exit at the target length even if it is already much longer; and the penalty would be a timeout, yet at that moment the queue is definitely drained.
Yes, we this routine should not hit timeouts, that's really bad. The point is you can hit a false timeout because the algorithm does not work.
Quoted 5 lines
Quoted 3 lines
Quoted 1 line
FWIW, the idea of HW drain came from
intel_iommu_drain_pasid_prq()..Yeah, but I think they might have over done it too..
I see.
The HW queue may need flushing on the device hot remove path, that depends how they implemented things. Like on ARM you had a mutex around the whole thing dev pointer so we don't even need that..
Jason
Re: [PATCH v5 04/15] iommu/arm-smmu-v3: Drain in-flight fault events on domain detach
In reply to Jason GunthorpeQuoted 16 lines · Jason Gunthorpe
Quoted 8 lines · Nicolin Chen
Quoted 8 lines
But I wonder if the point of this has been lost? Prior to calling the driver attach functions the core code already changes the xarray:
curr = xa_cmpxchg(&group->pasid_array, pasid, NULL, XA_ZERO_ENTRY, GFP_KERNEL);That immediately makes the threaded IRQ safe since it calls
iommu_attach_handle_get()which now fails.Hmm, actually that's a sneaky cmpxchg that is only doing reserve..
Quoted 3 lines
I am not sure about that. Looking at
iommufd_hwpt_replace_device(), there can be aold_handle!= NULL, in which case thecmpxchg()would not change the xarray?
Quoted 5 lines
I think this is wrong, there is no way it can work like this where the attach continues to see the to-be-detached domain across the flushes. No amount of flushing can fix it.
Somehow we broke it :\
I'm trying to fix this by replacing xa_cmpxchg() with xa_store().
However, an asynchronous iommu_attach_handle_get() could return the old_handle in the iopf path, before xa_store() in the detach path:
- If a different domain is replaced, a driver callback is invoked calling synchronize_irq() and iopf_queue_flush_dev(). And this closes the window on any the old_handle reference before being released.
- If a different handle is replaced on the same domain, the driver callback is skipped. Then, the iopf path could hit UAF while the old_handle being released by the detach path?
To close that window, I think we might need an ABI contract for the second case by asking drivers to sync irq and flush the iopf queue? This would require to roll out driver-level changes as well.
Thanks
Nicolin
Re: [PATCH v5 04/15] iommu/arm-smmu-v3: Drain in-flight fault events on domain detach
Quoted 28 lines · Nicolin Chen
Quoted 16 lines · Jason Gunthorpe
Quoted 8 lines · Nicolin Chen
Quoted 8 lines
But I wonder if the point of this has been lost? Prior to calling the driver attach functions the core code already changes the xarray:
curr = xa_cmpxchg(&group->pasid_array, pasid, NULL, XA_ZERO_ENTRY, GFP_KERNEL);That immediately makes the threaded IRQ safe since it calls
iommu_attach_handle_get()which now fails.Hmm, actually that's a sneaky cmpxchg that is only doing reserve..
Quoted 3 lines
I am not sure about that. Looking at
iommufd_hwpt_replace_device(), there can be aold_handle!= NULL, in which case thecmpxchg()would not change the xarray?Quoted 5 lines
I think this is wrong, there is no way it can work like this where the attach continues to see the to-be-detached domain across the flushes. No amount of flushing can fix it.
Somehow we broke it :\
I'm trying to fix this by replacing
xa_cmpxchg()withxa_store().However, an asynchronous
iommu_attach_handle_get()could return theold_handlein the iopf path, beforexa_store()in the detach path:
Quoted 8 lines
- If a different domain is replaced, a driver callback is invoked calling
synchronize_irq()andiopf_queue_flush_dev(). And this closes the window on any theold_handlereference before being released.- If a different handle is replaced on the same domain, the driver callback is skipped. Then, the iopf path could hit UAF while the
old_handlebeing released by the detach path?
False alarm. It's actually not reachable today. There is only one caller from iommufd, where a domain reattachment doesn't swap the attach handle but bypass that:
if (hwpt == old_hwpt) {
mutex_unlock(&igroup->lock);
return NULL;
}I will send a small iommu-core series for review.
Nicolin
PATCH v5 5/15 iommu/arm-smmu-v3: Flush in-flight fault work on domain detach
In reply to Nicolin ChenAfter the hardware queue is drained, an event may still be moving from the IRQ thread to the IOPF workqueue, while earlier IOPF work is still running.
Synchronize the EVTQ and combined IRQs, then call iopf_queue_flush_dev(). This finishes all old-domain work before the IOMMU core frees the domain. Skip synchronize_irq() after a drain timeout because a stuck consumer can otherwise leave it waiting forever.
If arm_smmu_wait_for_queue_drained() times out, fault work may still be in flight, and iopf_queue_remove_device() would free iopf groups that the work also references. Skip the iopf teardown and leak the master_domain, rather than risk a use-after-free.
The skip also leaks the iopf refcount, keeping the device enrolled on the IOPF queue, which would strand its fault parameter on the queue list once the device teardown frees dev->iommu, crashing a later iopf_queue_free(). Reclaim the enrollment in arm_smmu_release_device(), where all the attach handles are gone so a straggler report cannot queue a new fault group.
Note that a residual race window remains between an iopf_queue_flush_dev() and iopf_queue_remove_device(): a fault arriving in between still resolves to the old attach handle, as the IOMMU core publishes a handle change only after the driver ops return. This window predates the drain narrowing it, and is only closable by an ordering fix in the IOMMU core. Furthermore, a timed-out drain shares exactly the same window, given that it must keep the device enrolled on the IOPF queue, where iopf_queue_remove_device() would free the iopf groups that any in-flight fault work still references.
--- drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c | 46 +++++++++++++++++++-- 1 file changed, 43 insertions(+), 3 deletions(-)
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c +43 -3
@@ -3410,6 +3410,7 @@ void arm_smmu_attach_release(struct arm_smmu_attach_state *state)struct arm_smmu_master_domain *master_domain = state->old_master_domain;struct arm_smmu_master *master = state->master;struct arm_smmu_device *smmu = master->smmu; +bool timed_out = false;lockdep_assert_not_held (&arm_smmu_asid_lock);iommu_group_mutex_assert (master->dev);
@@ -3422,8 +3423,38 @@ void arm_smmu_attach_release(struct arm_smmu_attach_state *state)* which the IOMMU core might free once this returns. Drain the hardware * eventq, so that a pending event cannot turn into new fault work. */ -if (master_domain->using_iopf && master->stall_enabled) -arm_smmu_wait_for_queue_drained (smmu, &smmu->evtq.q, false); +if (master_domain->using_iopf && master->stall_enabled) { + timed_out =arm_smmu_wait_for_queue_drained (smmu, &smmu->evtq.q, + false); +/* +* Ensure pending events have reached the IOPF queue, unless +* the drain timed out: a stuck consumer would also block an +* unbounded wait_event() inside the synchronize_irq(). +*/ +if (!timed_out) { +if (smmu->evtq.q.irq) +synchronize_irq (smmu->evtq.q.irq); +/* Pending events might be in the combined_irq handler */ +if (smmu->combined_irq) +synchronize_irq (smmu->combined_irq); + } + } + +/* Lastly, flush the fault work that the drained events queued */ +if (master_domain->using_iopf) { +iopf_queue_flush_dev (master->dev); + +/* +* A timed-out drain may leave fault work in flight, and +* iopf_queue_remove_device() would free iopf groups that +* such work still references. Skip the iopf teardown and +* leak master_domain, rather than risk a UAF. +*/ +if (WARN_ON (timed_out)) { + state->old_master_domain =NULL ; +return ; + } + }arm_smmu_disable_iopf (master, master_domain);kfree (master_domain);
@@ -4407,7 +4438,16 @@ static void arm_smmu_release_device(struct device *dev) {struct arm_smmu_master *master =dev_iommu_priv_get (dev); -WARN_ON (master->iopf_refcount); +/* +* A timed-out drain in arm_smmu_attach_release() leaks the refcount, +* keeping the device on the IOPF queue. Reclaim it here, since every +* attach handle is gone: a straggler fault can no longer queue a new +* fault group, so the queue turns stable once flushed. +*/ +if (WARN_ON (master->iopf_refcount)) { +iopf_queue_flush_dev (dev); +iopf_queue_remove_device (master->smmu->evtq.iopf, dev); + }arm_smmu_disable_pasid (master);arm_smmu_remove_master (master);
Signature · 1 line
2.43.0
[email protected]
AI reviewRe: [PATCH v5 05/15] iommu/arm-smmu-v3: Flush in-flight fault work on domain detach
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider: - [Medium] iommu/arm-smmu-v3: memory leak of master_domain on timeout path --
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] iommu/arm-smmu-v3: memory leak of master_domain on timeout path --
commit 93bfbb1f6ed056db517f551ddf8e2b1b26915570
Author: Nicolin Chen <[email protected]>
iommu/arm-smmu-v3: Flush in-flight fault work on domain detach
This commit flushes in-flight fault work on domain detach and skips IOPF teardown if the hardware queue drain times out. This aims to prevent a use-after-free by intentionally leaking the master_domain and iopf refcount on timeout.
Quoted 4 lines
If
arm_smmu_wait_for_queue_drained()times out, fault work may still be in flight, andiopf_queue_remove_device()would free iopf groups that the work also references. Skip the iopf teardown and leak themaster_domain, rather than risk a use-after-free.
[Severity: Medium]
The commit message states the intent to leak master_domain to avoid a use-after-free. Is this description accurate?
The risk appears to apply exclusively to the iopf_group structures that would be freed by the skipped iopf_queue_remove_device() call. The master_domain structure is entirely driver-internal, has already been unlinked from the smmu_domain->devices list in arm_smmu_remove_master_domain(), and is not referenced by in-flight IOPF works or any other background threads.
Quoted 4 lines
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c +0
-0
[ ... ]
Quoted 1 line
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c +0
-0@@ -3422,8 +3423,38 @@ void arm_smmu_attach_release(struct arm_smmu_attach_state *state)
[ ... ]
Quoted 4 lines
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c +4
-0+/* Lastly, flush the fault work that the drained events queued */ +if (master_domain->using_iopf) { +iopf_queue_flush_dev (master->dev); +
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c void arm_smmu_attach_release(struct arm_smmu_attach_state *state) · line 3456
Quoted 10 lines
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c +10
-0+/* +* A timed-out drain may leave fault work in flight, and +* iopf_queue_remove_device() would free iopf groups that +* such work still references. Skip the iopf teardown and +* leak master_domain, rather than risk a UAF. +*/ +if (WARN_ON (timed_out)) { + state->old_master_domain =NULL ; +return ; + }
[Severity: Medium]
Does this early return leak master_domain unnecessarily?
Since kfree(master_domain) appears safe to call here even if the drain times out, could we free it before returning to avoid leaking master_domain?
Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=5
Re: [PATCH v5 05/15] iommu/arm-smmu-v3: Flush in-flight fault work on domain detach
In reply to Nicolin ChenQuoted 12 lines
[ ... 54 lines skipped ... ]
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c +5
-2@@ -3422,8 +3423,38 @@ void arm_smmu_attach_release(struct arm_smmu_attach_state *state)* which the IOMMU core might free once this returns. Drain the hardware * eventq, so that a pending event cannot turn into new fault work. */ -if (master_domain->using_iopf && master->stall_enabled) -arm_smmu_wait_for_queue_drained (smmu, &smmu->evtq.q, false); +if (master_domain->using_iopf && master->stall_enabled) { + timed_out =arm_smmu_wait_for_queue_drained (smmu, &smmu->evtq.q, + false); +/* +* Ensure pending events have reached the IOPF queue, unless
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c void arm_smmu_attach_release(struct arm_smmu_attach_state *state) · line 3440
Quoted 10 lines
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c +10
-0+* the drain timed out: a stuck consumer would also block an +* unbounded wait_event() inside the synchronize_irq(). +*/ +if (!timed_out) { +if (smmu->evtq.q.irq) +synchronize_irq (smmu->evtq.q.irq); +/* Pending events might be in the combined_irq handler */ +if (smmu->combined_irq) +synchronize_irq (smmu->combined_irq); + }
I think this logic is overkill. If we can't flush the queue we are probably going to crash next. Chuck a warn on and hope for the best. We don't need this fancy half attempt at recovery.
Signature · 1 line
Jason
PATCH v5 6/15 iommu/arm-smmu-v3: Allocate IOPF queue without FEAT_SVA
In reply to Nicolin Chenarm_smmu_init_queues() allocates the IOPF queue only for FEAT_SVA together with FEAT_STALLS. This queue has no SVA dependency, and FEAT_STALLS alone can produce events that need IOPF.
On HW with FEAT_STALLS but no FEAT_SVA, a master with the "dma-can-stall" device-tree property has master->stall_enabled set at probe. Then also with CONFIG_ARM_SMMU_V3_SVA=y, arm_smmu_enable_iopf() sees stall_enabled, calls iopf_queue_add_device(NULL, ...) at fault-handling domain attach, which can oops on mutex_lock(&queue->lock).
Allocate the queue whenever FEAT_STALLS is present. Also select IOMMU_IOPF so iopf_queue_alloc() is always available without CONFIG_ARM_SMMU_V3_SVA. Since IOPF does not depend on SVA, drop the SVA guards from its enable and disable paths.
--- drivers/iommu/arm/Kconfig | 1 + drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c | 9 +-------- 2 files changed, 2 insertions(+), 8 deletions(-)
drivers/iommu/arm/Kconfig +1 -0
@@ -78,6 +78,7 @@ config ARM_SMMU_V3 depends on ARM64 select IOMMU_API select IOMMU_IO_PGTABLE_LPAE + select IOMMU_IOPF select GENERIC_MSI_IRQ select IOMMUFD_DRIVER if IOMMUFD help
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c +1 -8
@@ -3215,9 +3215,6 @@ static int arm_smmu_enable_iopf(struct arm_smmu_master *master,iommu_group_mutex_assert (master->dev); -if (!IS_ENABLED (CONFIG_ARM_SMMU_V3_SVA )) -return -EOPNOTSUPP ; -/* * Drivers for devices supporting PRI or stall require iopf others have * device-specific fault handlers and don't need IOPF, so this is not a
@@ -3249,9 +3246,6 @@ static void arm_smmu_disable_iopf(struct arm_smmu_master *master, {iommu_group_mutex_assert (master->dev); -if (!IS_ENABLED (CONFIG_ARM_SMMU_V3_SVA )) -return ; -if (!master_domain || !master_domain->using_iopf)return ;
@@ -4667,8 +4661,7 @@ static int arm_smmu_init_queues(struct arm_smmu_device *smmu)if (ret)return ret; -if ((smmu->features &ARM_SMMU_FEAT_SVA ) && - (smmu->features &ARM_SMMU_FEAT_STALLS )) { +if (smmu->features &ARM_SMMU_FEAT_STALLS ) { smmu->evtq.iopf =iopf_queue_alloc (dev_name (smmu->dev));if (!smmu->evtq.iopf)return -ENOMEM ;
Signature · 1 line
2.43.0
[email protected]
AI reviewRe: [PATCH v5 06/15] iommu/arm-smmu-v3: Allocate IOPF queue without FEAT_SVA
Sashiko has reviewed this patch and found no issues. It looks great!
Quoted 15 lines
arm_smmu_init_queues()allocates the IOPF queue only forFEAT_SVAtogether withFEAT_STALLS. This queue has no SVA dependency, andFEAT_STALLSalone can produce events that need IOPF.On HW with
FEAT_STALLSbut noFEAT_SVA, a master with the "dma-can-stall" device-tree property hasmaster->stall_enabledset at probe. Then also withCONFIG_ARM_SMMU_V3_SVA=y,arm_smmu_enable_iopf()seesstall_enabled, callsiopf_queue_add_device(NULL, ...)at fault-handling domain attach, which can oops onmutex_lock(&queue->lock).Allocate the queue whenever
FEAT_STALLSis present. Also selectIOMMU_IOPFsoiopf_queue_alloc()is always available withoutCONFIG_ARM_SMMU_V3_SVA. Since IOPF does not depend on SVA, drop the SVA guards from its enable and disable paths.
Quoted 5 lines
Fixes 395ad89d11fd9 ("iommu/arm-smmu-v3: Add stall support for platform devices")Cc [email protected] # v5.14Reviewed-by Jonathan Cameron <[email protected]>Assisted-by LLMSigned-off-by Nicolin Chen <[email protected]>
Sashiko has reviewed this patch and found no issues. It looks great!
Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=6
Re: [PATCH v5 06/15] iommu/arm-smmu-v3: Allocate IOPF queue without FEAT_SVA
In reply to Nicolin ChenQuoted 3 lines
arm_smmu_init_queues()allocates the IOPF queue only forFEAT_SVAtogether withFEAT_STALLS. This queue has no SVA dependency, andFEAT_STALLSalone can produce events that need IOPF.
Until iommufd there was no other way to create faults in the first place. Doesn't matter if the HW supports it.
Quoted 6 lines
On HW with
FEAT_STALLSbut noFEAT_SVA, a master with the "dma-can-stall" device-tree property hasmaster->stall_enabledset at probe. Then also withCONFIG_ARM_SMMU_V3_SVA=y,arm_smmu_enable_iopf()seesstall_enabled, callsiopf_queue_add_device(NULL, ...)at fault-handling domain attach, which can oops onmutex_lock(&queue->lock).
Yeah the guards got mismatched. There should have been a
arm_smmu_iopf_enabled(smmu)
if (!IS_ENABLED(CONFIG_ARM_SMMU_V3_SVA)) return false
if (!(smmu->features & ARM_SMMU_FEAT_SVA)) return falseAnd that is used everywhere. The fact one place used the feat and the other used the CONFIG is the cause of this crash.
Quoted 12 lines
[ ... 32 lines skipped ... ]
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c +0
-3@@ -3215,9 +3215,6 @@ static int arm_smmu_enable_iopf(struct arm_smmu_master *master,iommu_group_mutex_assert (master->dev); -if (!IS_ENABLED (CONFIG_ARM_SMMU_V3_SVA )) -return -EOPNOTSUPP ; -/* * Drivers for devices supporting PRI or stall require iopf others have * device-specific fault handlers and don't need IOPF, so this is not a
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c static void arm_smmu_disable_iopf(struct arm_smmu_master *master, · line 3251
Quoted 9 lines
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c +0
-3@@ -3249,9 +3246,6 @@ static void arm_smmu_disable_iopf(struct arm_smmu_master *master, {iommu_group_mutex_assert (master->dev); -if (!IS_ENABLED (CONFIG_ARM_SMMU_V3_SVA )) -return ; -if (!master_domain || !master_domain->using_iopf)return ;
I think this is fine
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c static int arm_smmu_init_queues(struct arm_smmu_device *smmu) · line 4667
Quoted 10 lines
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c +1
-2@@ -4667,8 +4661,7 @@ static int arm_smmu_init_queues(struct arm_smmu_device *smmu)if (ret)return ret; -if ((smmu->features &ARM_SMMU_FEAT_SVA ) && - (smmu->features &ARM_SMMU_FEAT_STALLS )) { +if (smmu->features &ARM_SMMU_FEAT_STALLS ) { smmu->evtq.iopf =iopf_queue_alloc (dev_name (smmu->dev));if (!smmu->evtq.iopf)return -ENOMEM ;
But here it would still be nice to support the non-SVA optimization..
I'd change the kconfig to move the select IOMMU_IOPF down to the iommufd and SVA blocks
Then guard this with an IS_ENABLED(CONFIG_IOMMU_IOPF)
That way you can choose to compile out IOPF if you don't want to use any SMMU features that rely on it.
Signature · 1 line
Jason
PATCH v5 7/15 iommu/arm-smmu-v3: Submit CMDQ_OP_PRI_RESP for IOPF event
In reply to Nicolin ChenFrom: Malak Marrid <[email protected]>
To handle IOMMU_FAULT_PAGE_REQ from the PRI queue, arm_smmu_page_response() must issue a CMDQ_OP_PRI_RESP back to the SMMU.
Both EVTQ stall events and PRIQ requests reach the IOPF layer with the same IOMMU_FAULT_PAGE_REQ, but these two fault models are mutually exclusive. A stalling master holds a transaction until CMDQ_OP_RESUME and has no reason to send a page request. A later patch rejects PRI for such a master, so the response command can be selected from stall_enabled or pri_enabled.
Note that a CMD_PRI_RESP.Resp encodes 0b00 as ResponseFailure (a permanent non-paging error), 0b01 as InvalidRequest (page-in unsuccessful), and 0b10 as Success. So IOMMU_PAGE_RESP_FAILURE maps to PRI_RESP_DENY (0b00) while IOMMU_PAGE_RESP_INVALID maps to PRI_RESP_FAIL (0b01), following the codes rather than the similarity of the enum names.
Extend arm_smmu_enable_iopf() to also proceed for a PRI-enabled master, so that attaching a fault-capable domain would set up IOPF for it. Note that a later change will set master->pri_enabled, once all PRI paths are ready.
Note: streams[0].id remains the RID because arm_smmu_enable_iopf() rejects num_streams != 1.
--- drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h | 1 + drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c | 74 +++++++++++++++------ 2 files changed, 56 insertions(+), 19 deletions(-)
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h +1 -0
@@ -1017,6 +1017,7 @@ struct arm_smmu_master {/* Locked by the iommu core using the group mutex */ struct arm_smmu_ctx_desc_cfg cd_table;unsigned int num_streams; + bool pri_enabled : 1; bool ats_enabled : 1; bool ste_ats_enabled : 1;bool stall_enabled;
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c +55 -19
94 lines not on this page · raw message
Signature · 1 line
2.43.0
[email protected]
AI reviewRe: [PATCH v5 07/15] iommu/arm-smmu-v3: Submit CMDQ_OP_PRI_RESP for IOPF event
Sashiko has reviewed this patch and found no issues. It looks great!
Quoted 15 lines
From: Malak Marrid <[email protected]>
To handle
IOMMU_FAULT_PAGE_REQfrom the PRI queue,arm_smmu_page_response()must issue aCMDQ_OP_PRI_RESPback to the SMMU.Both EVTQ stall events and PRIQ requests reach the IOPF layer with the same
IOMMU_FAULT_PAGE_REQ, but these two fault models are mutually exclusive. A stalling master holds a transaction untilCMDQ_OP_RESUMEand has no reason to send a page request. A later patch rejects PRI for such a master, so the response command can be selected fromstall_enabledorpri_enabled.Note that a CMD_PRI_RESP.Resp encodes 0b00 as ResponseFailure (a permanent non-paging error), 0b01 as InvalidRequest (page-in unsuccessful), and 0b10 as Success. So
IOMMU_PAGE_RESP_FAILUREmaps toPRI_RESP_DENY(0b00) whileIOMMU_PAGE_RESP_INVALIDmaps toPRI_RESP_FAIL(0b01), following the codes
Quoted 6 lines
[ ... ]Co-developed-by Stefan Kaestle <[email protected]>Signed-off-by Stefan Kaestle <[email protected]>Signed-off-by Malak Marrid <[email protected]>Reviewed-by Jonathan Cameron <[email protected]>Signed-off-by Nicolin Chen <[email protected]>
Sashiko has reviewed this patch and found no issues. It looks great!
Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=7
Re: [PATCH v5 07/15] iommu/arm-smmu-v3: Submit CMDQ_OP_PRI_RESP for IOPF event
In reply to Nicolin ChenQuoted 10 lines
[ ... 66 lines skipped ... ]
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c +9
-0+/* IOPF is gated to num_streams == 1 in arm_smmu_enable_iopf() */ + sid = master->streams[0 ].id; + +if (master->stall_enabled) { +u8 resume_resp; + +switch (resp->code) { +case IOMMU_PAGE_RESP_INVALID : +case IOMMU_PAGE_RESP_FAILURE :
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c static int arm_smmu_wait_for_queue_drained(struct arm_smmu_device *smmu, · line 1070
Quoted 10 lines
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c +10
-0+ resume_resp =CMDQ_RESUME_0_RESP_ABORT ; +break ; +case IOMMU_PAGE_RESP_SUCCESS : + resume_resp =CMDQ_RESUME_0_RESP_RETRY ; +break ; +default : + resume_resp =CMDQ_RESUME_0_RESP_TERM ; +break ; + } + cmd =arm_smmu_make_cmd_resume (sid, resp->grpid, resume_resp);
[Severity: Medium]
This is a pre-existing issue, but does hardcoding the SID to streams[0].id cause the wrong stream to be resumed on multi-SID devices?
This seems right. Stall mode should encode the SID in the iopf_fault event, maybe in the private data and recover it here.
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c static int arm_smmu_wait_for_queue_drained(struct arm_smmu_device *smmu, · line 1095
Quoted 6 lines
[ ... 20 lines skipped ... ]
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c +5
-0+default : +WARN_ON (true); +return ; + } + ssv = !!(prm->flags &IOMMU_FAULT_PAGE_REQUEST_PASID_VALID );
No need for !! when asigning or casting through a bool type. Compiler does it automatically.
Signature · 1 line
Jason
PATCH v5 8/15 iommu/arm-smmu-v3: Disable the queue IRQs before disabling the SMMU
In reply to Nicolin ChenThe EVTQ, PRIQ and combined IRQ handlers are threaded and issue commands of their own, e.g. a CMDQ_OP_PRI_RESP for a page request. Disabling the SMMU while one is in flight hands that command to a queue consuming nothing, so its poll waits out a full timeout.
Two paths disable the SMMU while those IRQs are still requested: a failing arm_smmu_device_reset() returns to a probe that disables the device itself, and arm_smmu_disable_action() covers an unbind or any later probe failure. Both can run after arm_smmu_setup_irqs() requested the IRQs.
Disable those IRQs first in both paths, so that no handler is left running once the SMMU goes down. arm_smmu_device_shutdown() needs no change of its own here, since it already just calls arm_smmu_disable_action().
Also clear an IRQ number when its request fails or is skipped for a missing ARM_SMMU_FEAT_PRI, keeping disable_irq() to the IRQs that this driver truly owns.
Note that the IOPF queue needs no such care of its own, as devres frees the IRQs before running the release action of that queue, which came earlier in arm_smmu_init_queues().
--- drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c | 36 ++++++++++++++++++--- 1 file changed, 31 insertions(+), 5 deletions(-)
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c +31 -5
@@ -4914,8 +4914,10 @@ static void arm_smmu_setup_unique_irqs(struct arm_smmu_device *smmu) arm_smmu_evtq_thread,IRQF_ONESHOT , "arm-smmu-v3-evtq", smmu); -if (ret <0 ) +if (ret <0 ) {dev_warn (smmu->dev,"failed to enable evtq irq\n" ); + smmu->evtq.q.irq =0 ; + } }else {dev_warn (smmu->dev,"no evtq irq - events will not be reported!\n" ); }
@@ -4938,12 +4940,17 @@ static void arm_smmu_setup_unique_irqs(struct arm_smmu_device *smmu)IRQF_ONESHOT ,"arm-smmu-v3-priq" , smmu); -if (ret <0 ) +if (ret <0 ) {dev_warn (smmu->dev,"failed to enable priq irq\n" ); + smmu->priq.q.irq =0 ; + } }else {dev_warn (smmu->dev,"no priq irq - PRI will be broken\n" ); } + }else { +/* An unrequested IRQ (e.g. set by DT) must not be disabled */ + smmu->priq.q.irq =0 ; } }
@@ -4971,8 +4978,10 @@ static int arm_smmu_setup_irqs(struct arm_smmu_device *smmu) arm_smmu_combined_irq_thread,IRQF_ONESHOT , "arm-smmu-v3-combined-irq", smmu); -if (ret <0 ) +if (ret <0 ) {dev_warn (smmu->dev,"failed to enable combined irq\n" ); + smmu->combined_irq =0 ; + } }else arm_smmu_setup_unique_irqs (smmu);
@@ -4999,10 +5008,22 @@ static int arm_smmu_device_disable(struct arm_smmu_device *smmu)return ret; } +/* Quiesce the queue IRQ threads, e.g. before disabling the SMMU */ +static void arm_smmu_disable_irqs (struct arm_smmu_device *smmu) +{ +if (smmu->combined_irq) +disable_irq (smmu->combined_irq); +if (smmu->evtq.q.irq) +disable_irq (smmu->evtq.q.irq); +if (smmu->priq.q.irq) +disable_irq (smmu->priq.q.irq); +} +static void arm_smmu_disable_action (void *data) {struct arm_smmu_device *smmu = data; +arm_smmu_disable_irqs (smmu);if (smmu->impl_ops && smmu->impl_ops->device_disable) smmu->impl_ops->device_disable (smmu);arm_smmu_device_disable (smmu);
@@ -5149,18 +5170,23 @@ static int arm_smmu_device_reset(struct arm_smmu_device *smmu) ARM_SMMU_CR0ACK);if (ret) {dev_err (smmu->dev,"failed to enable SMMU interface\n" ); -return ret; + goto err_disable_irqs; }if (smmu->impl_ops && smmu->impl_ops->device_reset) { ret = smmu->impl_ops->device_reset (smmu);if (ret) {dev_err (smmu->dev,"failed to reset impl\n" ); -return ret; + goto err_disable_irqs; } }return 0 ; + +err_disable_irqs: +/* The probe error path cannot tell if the IRQs were requested */ +arm_smmu_disable_irqs (smmu); +return ret; }#define IIDR_IMPLEMENTER_ARM 0x43b
Signature · 1 line
2.43.0
[email protected]
AI reviewRe: [PATCH v5 08/15] iommu/arm-smmu-v3: Disable the queue IRQs before disabling the SMMU
Sashiko has reviewed this patch and found no issues. It looks great!
Quoted 18 lines
The EVTQ, PRIQ and combined IRQ handlers are threaded and issue commands of their own, e.g. a
CMDQ_OP_PRI_RESPfor a page request. Disabling the SMMU while one is in flight hands that command to a queue consuming nothing, so its poll waits out a full timeout.Two paths disable the SMMU while those IRQs are still requested: a failing
arm_smmu_device_reset()returns to a probe that disables the device itself, andarm_smmu_disable_action()covers an unbind or any later probe failure. Both can run afterarm_smmu_setup_irqs()requested the IRQs.Disable those IRQs first in both paths, so that no handler is left running once the SMMU goes down.
arm_smmu_device_shutdown()needs no change of its own here, since it already just callsarm_smmu_disable_action().Also clear an IRQ number when its request fails or is skipped for a missing
ARM_SMMU_FEAT_PRI, keepingdisable_irq()to the IRQs that this driver truly owns.
Quoted 6 lines
Note that the IOPF queue needs no such care of its own, as devres frees the IRQs before running the release action of that queue, which came earlier in
arm_smmu_init_queues().Assisted-by LLMSigned-off-by Nicolin Chen <[email protected]>
Sashiko has reviewed this patch and found no issues. It looks great!
Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=8
Re: [PATCH v5 08/15] iommu/arm-smmu-v3: Disable the queue IRQs before disabling the SMMU
In reply to Nicolin ChenQuoted 18 lines · On Tue, 15 Sep 2026 09:38:20 -0700 Nicolin Chen <[email protected]> wrote:
The EVTQ, PRIQ and combined IRQ handlers are threaded and issue commands of their own, e.g. a
CMDQ_OP_PRI_RESPfor a page request. Disabling the SMMU while one is in flight hands that command to a queue consuming nothing, so its poll waits out a full timeout.Two paths disable the SMMU while those IRQs are still requested: a failing
arm_smmu_device_reset()returns to a probe that disables the device itself, andarm_smmu_disable_action()covers an unbind or any later probe failure. Both can run afterarm_smmu_setup_irqs()requested the IRQs.Disable those IRQs first in both paths, so that no handler is left running once the SMMU goes down.
arm_smmu_device_shutdown()needs no change of its own here, since it already just callsarm_smmu_disable_action().Also clear an IRQ number when its request fails or is skipped for a missing
ARM_SMMU_FEAT_PRI, keepingdisable_irq()to the IRQs that this driver truly owns.
Quoted 6 lines
Note that the IOPF queue needs no such care of its own, as devres frees the IRQs before running the release action of that queue, which came earlier in
arm_smmu_init_queues().Assisted-by LLMSigned-off-by Nicolin Chen <[email protected]>
I'm still not loving the disable_irq() in here just because to me it feels like it may end up masking stuff we ought to handle more carefully but I don't mind that much
Re: [PATCH v5 08/15] iommu/arm-smmu-v3: Disable the queue IRQs before disabling the SMMU
In reply to Nicolin ChenQuoted 4 lines
The EVTQ, PRIQ and combined IRQ handlers are threaded and issue commands of their own, e.g. a
CMDQ_OP_PRI_RESPfor a page request. Disabling the SMMU while one is in flight hands that command to a queue consuming nothing, so its poll waits out a full timeout.
This seems like a sashiko hallucination?
There are no PRIs during probe. The initial STE table does not permit it.
Quoted 4 lines
Two paths disable the SMMU while those IRQs are still requested: a failing
arm_smmu_device_reset()returns to a probe that disables the device itself, andarm_smmu_disable_action()covers an unbind or any later probe failure. Both can run afterarm_smmu_setup_irqs()requested the IRQs.
That's fscking devm again. Do not mix devm and non devm CALLS. It doesn't work:
ret = arm_smmu_device_reset(smmu);
if (ret) {
arm_smmu_device_disable(smmu); <-------------------
return ret;
}
/* Register last so it unwinds first, while the CMDQ is still up. */
ret = devm_add_action_or_reset(smmu->dev, arm_smmu_disable_action, smmu);
if (ret)
return ret;When devm is devm'ing properly the existing devm irq handlers will be torn down before disable_action and we don't need to open code a disable_irq
That just leaves shutdown, and I'm not sure how that is supposed to work, we can't just halt the SMMU while there are any threads running around still poking at it. I forget the context shutdown runs in, but if concurrency is shut down already then its fine to not disable the irqs.
If not, it doesn't work safely for lots of reasons, and disable_irq is not going to fix it enough.
Maybe that is something to tackle after the power management series as shutdown is sort of like power down.
Signature · 1 line
Jason
Re: [PATCH v5 08/15] iommu/arm-smmu-v3: Disable the queue IRQs before disabling the SMMU
Quoted 9 lines · Jason Gunthorpe
Quoted 4 lines
The EVTQ, PRIQ and combined IRQ handlers are threaded and issue commands of their own, e.g. a
CMDQ_OP_PRI_RESPfor a page request. Disabling the SMMU while one is in flight hands that command to a queue consuming nothing, so its poll waits out a full timeout.This seems like a sashiko hallucination?
There are no PRIs during probe. The initial STE table does not permit it.
Checked my long rework history. Yes... my bad..
Quoted 8 lines
Quoted 4 lines
Two paths disable the SMMU while those IRQs are still requested: a failing
arm_smmu_device_reset()returns to a probe that disables the device itself, andarm_smmu_disable_action()covers an unbind or any later probe failure. Both can run afterarm_smmu_setup_irqs()requested the IRQs.That's fscking devm again. Do not mix devm and non devm CALLS. It doesn't work:
Quoted 10 lines
ret = arm_smmu_device_reset(smmu);
if (ret) {
arm_smmu_device_disable(smmu); <-------------------
return ret;
}
/* Register last so it unwinds first, while the CMDQ is still up. */
ret = devm_add_action_or_reset(smmu->dev, arm_smmu_disable_action, smmu);
if (ret)
return ret;This seems requiring a reorder.
Quoted 3 lines
When devm is devm'ing properly the existing devm irq handlers will be torn down before
disable_actionand we don't need to open code adisable_irq
I see. Yes.
Quoted 11 lines
That just leaves shutdown, and I'm not sure how that is supposed to work, we can't just halt the SMMU while there are any threads running around still poking at it. I forget the context shutdown runs in, but if concurrency is shut down already then its fine to not disable the irqs.
If not, it doesn't work safely for lots of reasons, and
disable_irqis not going to fix it enough.Maybe that is something to tackle after the power management series as shutdown is sort of like power down.
OK. Let's leave it.
Thanks
Nicolin
PATCH v5 9/15 iommu/arm-smmu-v3: Disable PRI when no IRQ handler is registered
In reply to Nicolin Chenarm_smmu_setup_irqs() can leave the PRIQ without any registered handler if its IRQ is missing, its request fails, or the combined IRQ request fails. These paths warn but leave ARM_SMMU_FEAT_PRI set.
This function itself would then go on to enable IRQ_CTRL_PRIQ_IRQEN, even though no handler will drain the queue. A later patch also enables PRI on such endpoints. The caller already enabled CR0_PRIQEN before this function even runs.
Clear ARM_SMMU_FEAT_PRI in all three failure paths, so that subsequent code treats PRI as unavailable. And disable CR0_PRIQEN after the setup function returns.
A kdump kernel doesn't service page requests: strip FEAT_PRI when detecting features, so downstream PRI setup is skipped.
--- drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-)
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c +8 -1
@@ -4944,9 +4944,11 @@ static void arm_smmu_setup_unique_irqs(struct arm_smmu_device *smmu)dev_warn (smmu->dev,"failed to enable priq irq\n" ); smmu->priq.q.irq =0 ; + smmu->features &= ~ARM_SMMU_FEAT_PRI ; } }else {dev_warn (smmu->dev,"no priq irq - PRI will be broken\n" ); + smmu->features &= ~ARM_SMMU_FEAT_PRI ; } }else {/* An unrequested IRQ (e.g. set by DT) must not be disabled */
@@ -4981,6 +4983,7 @@ static int arm_smmu_setup_irqs(struct arm_smmu_device *smmu)if (ret <0 ) {dev_warn (smmu->dev,"failed to enable combined irq\n" ); smmu->combined_irq =0 ; + smmu->features &= ~ARM_SMMU_FEAT_PRI ; } }else arm_smmu_setup_unique_irqs (smmu);
@@ -5161,6 +5164,10 @@ static int arm_smmu_device_reset(struct arm_smmu_device *smmu)return ret; } +/* arm_smmu_setup_irqs() might have unset the ARM_SMMU_FEAT_PRI */ +if (!(smmu->features &ARM_SMMU_FEAT_PRI )) + enables &= ~CR0_PRIQEN ; +if (is_kdump_kernel ()) enables &= ~(CR0_EVTQEN |CR0_PRIQEN );
@@ -5302,7 +5309,7 @@ static int arm_smmu_device_hw_probe(struct arm_smmu_device *smmu) }/* Boolean feature flags */ -if (IS_ENABLED (CONFIG_PCI_PRI ) && reg &IDR0_PRI ) +if (IS_ENABLED (CONFIG_PCI_PRI ) && reg &IDR0_PRI && !is_kdump_kernel ()) smmu->features |=ARM_SMMU_FEAT_PRI ;if (IS_ENABLED (CONFIG_PCI_ATS ) && reg &IDR0_ATS )
Signature · 1 line
2.43.0
[email protected]
AI reviewRe: [PATCH v5 09/15] iommu/arm-smmu-v3: Disable PRI when no IRQ handler is registered
Sashiko has reviewed this patch and found no issues. It looks great!
Quoted 13 lines
arm_smmu_setup_irqs()can leave the PRIQ without any registered handler if its IRQ is missing, its request fails, or the combined IRQ request fails. These paths warn but leaveARM_SMMU_FEAT_PRIset.This function itself would then go on to enable
IRQ_CTRL_PRIQ_IRQEN, even though no handler will drain the queue. A later patch also enables PRI on such endpoints. The caller already enabledCR0_PRIQENbefore this function even runs.Clear
ARM_SMMU_FEAT_PRIin all three failure paths, so that subsequent code treats PRI as unavailable. And disableCR0_PRIQENafter the setup function returns.
Quoted 6 lines
A kdump kernel doesn't service page requests: strip
FEAT_PRIwhen detecting features, so downstream PRI setup is skipped.Reviewed-by Jonathan Cameron <[email protected]>Assisted-by LLMSigned-off-by Nicolin Chen <[email protected]>
Sashiko has reviewed this patch and found no issues. It looks great!
Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=9
Re: [PATCH v5 09/15] iommu/arm-smmu-v3: Disable PRI when no IRQ handler is registered
In reply to Nicolin ChenQuoted 10 lines · Nicolin Chen
arm_smmu_setup_irqs()can leave the PRIQ without any registered handler if its IRQ is missing, its request fails, or the combined IRQ request fails. These paths warn but leaveARM_SMMU_FEAT_PRIset.This function itself would then go on to enable
IRQ_CTRL_PRIQ_IRQEN, even though no handler will drain the queue. A later patch also enables PRI on such endpoints. The caller already enabledCR0_PRIQENbefore this function even runs.[...]
Signature · 1 line
Jason
PATCH v5 10/15 iommu/arm-smmu-v3: Support PRI Page Request in arm_smmu_handle_ppr()
In reply to Nicolin ChenFor a PRI-enabled master, convert every PRIQ entry into an iopf_fault and report it through iommu_report_device_fault().
For an unknown StreamID or a master without PRI enabled, keep the existing LAST-page DENY path. Merge its two messages into one ratelimited line so a faulty device cannot flood the kernel log.
Discard any PASID Stop Marker (LRW = 0b100) prior to the fault report and the DENY fallback both, because a Stop Marker does not expect a response. The IOPF infrastructure requires the driver to discard it, as per the doc at iommu_report_device_fault(). This also matches the intel-iommu code.
When SSV is clear, the same LRW encoding is a PRI Page Request that expects a response, so let it fall through.
Note that master->pri_enabled will only be set by a later change, once all the PRI paths are ready.
--- drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c | 50 +++++++++++++++++++-- 1 file changed, 47 insertions(+), 3 deletions(-)
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c +47 -3
@@ -2378,6 +2378,7 @@ static irqreturn_t arm_smmu_evtq_thread(int irq, void *dev)static void arm_smmu_handle_ppr (struct arm_smmu_device *smmu,u64 *evt) { +struct arm_smmu_master *master;u32 sid, ssid;u16 grpid;bool ssv, last;
@@ -2388,9 +2389,52 @@ static void arm_smmu_handle_ppr(struct arm_smmu_device *smmu, u64 *evt) last =FIELD_GET (PRIQ_0_PRG_LAST , evt[0 ]); grpid =FIELD_GET (PRIQ_1_PRG_IDX , evt[1 ]); -dev_info (smmu->dev,"unexpected PRI request received:\n" ); - dev_info(smmu->dev, - "\tsid 0x%08x.0x%05x: [%u%s] %sprivileged %s%s%s access at iova 0x%016llx\n", +/* +* A PASID Stop Marker (LRW = 0b100) does not expect a response and +* must be discarded before fault reporting: see the documentation +* at iommu_report_device_fault(). +* +* Gate it on SSV, as without a PASID that same encoding is a PRI Page +* Request and it does expect a response. So let it fall through and be +* answered with a PRI_RESP_FAIL, since iommu_sva_handle_mm() rejects a +* PASID-less fault, or by the PRI_RESP_DENY issued below. +*/ +if (last && ssv && !(evt[0 ] & (PRIQ_0_PERM_READ |PRIQ_0_PERM_WRITE ))) +return ; + +mutex_lock (&smmu->streams_mutex); + master =arm_smmu_find_master (smmu, sid); +if (master && master->pri_enabled) { +struct iopf_fault iopf_fault = {}; +struct iommu_fault *fault = &iopf_fault.fault; + + fault->type =IOMMU_FAULT_PAGE_REQ ; +if (last) + fault->prm.flags |=IOMMU_FAULT_PAGE_REQUEST_LAST_PAGE ; +if (ssv) { + fault->prm.flags |= +IOMMU_FAULT_PAGE_REQUEST_PASID_VALID ; + fault->prm.pasid = ssid; + } + fault->prm.grpid = grpid; +if (evt[0 ] &PRIQ_0_PERM_READ ) + fault->prm.perm |=IOMMU_FAULT_PERM_READ ; +if (evt[0 ] &PRIQ_0_PERM_WRITE ) + fault->prm.perm |=IOMMU_FAULT_PERM_WRITE ; +if (evt[0 ] &PRIQ_0_PERM_EXEC ) + fault->prm.perm |=IOMMU_FAULT_PERM_EXEC ; +if (evt[0 ] &PRIQ_0_PERM_PRIV ) + fault->prm.perm |=IOMMU_FAULT_PERM_PRIV ; + fault->prm.addr =FIELD_GET (PRIQ_1_ADDR_MASK , evt[1 ]) <<12 ; + +iommu_report_device_fault (master->dev, &iopf_fault); +mutex_unlock (&smmu->streams_mutex); +return ; + } +mutex_unlock (&smmu->streams_mutex); + + dev_info_ratelimited(smmu->dev, + "unexpected PRI request: sid 0x%08x.0x%05x: [%u%s] %sprivileged %s%s%s access at iova 0x%016llx\n", sid, ssid, grpid, last ? "L" : "", evt[0] & PRIQ_0_PERM_PRIV ? "" : "un", evt[0] & PRIQ_0_PERM_READ ? "R" : "",
Signature · 1 line
2.43.0
[email protected]
AI reviewRe: [PATCH v5 10/15] iommu/arm-smmu-v3: Support PRI Page Request in arm_smmu_handle_ppr()
Sashiko has reviewed this patch and found no issues. It looks great!
Quoted 18 lines
For a PRI-enabled master, convert every PRIQ entry into an
iopf_faultand report it throughiommu_report_device_fault().For an unknown StreamID or a master without PRI enabled, keep the existing LAST-page DENY path. Merge its two messages into one ratelimited line so a faulty device cannot flood the kernel log.
Discard any PASID Stop Marker (LRW = 0b100) prior to the fault report and the DENY fallback both, because a Stop Marker does not expect a response. The IOPF infrastructure requires the driver to discard it, as per the doc at
iommu_report_device_fault(). This also matches the intel-iommu code.When SSV is clear, the same LRW encoding is a PRI Page Request that expects a response, so let it fall through.
Note that
master->pri_enabledwill only be set by a later change, once all the PRI paths are ready.
Quoted 6 lines
Co-developed-by Barak Biber <[email protected]>Signed-off-by Barak Biber <[email protected]>Co-developed-by Stefan Kaestle <[email protected]>Signed-off-by Stefan Kaestle <[email protected]>Signed-off-by Malak Marrid <[email protected]>Signed-off-by Nicolin Chen <[email protected]>
Sashiko has reviewed this patch and found no issues. It looks great!
Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=10
Re: [PATCH v5 10/15] iommu/arm-smmu-v3: Support PRI Page Request in arm_smmu_handle_ppr()
In reply to Nicolin ChenQuoted 18 lines · On Tue, 15 Sep 2026 09:38:22 -0700 Nicolin Chen <[email protected]> wrote:
For a PRI-enabled master, convert every PRIQ entry into an
iopf_faultand report it throughiommu_report_device_fault().For an unknown StreamID or a master without PRI enabled, keep the existing LAST-page DENY path. Merge its two messages into one ratelimited line so a faulty device cannot flood the kernel log.
Discard any PASID Stop Marker (LRW = 0b100) prior to the fault report and the DENY fallback both, because a Stop Marker does not expect a response. The IOPF infrastructure requires the driver to discard it, as per the doc at
iommu_report_device_fault(). This also matches the intel-iommu code.When SSV is clear, the same LRW encoding is a PRI Page Request that expects a response, so let it fall through.
Note that
master->pri_enabledwill only be set by a later change, once all the PRI paths are ready.
Quoted 6 lines
Co-developed-by Barak Biber <[email protected]>Signed-off-by Barak Biber <[email protected]>Co-developed-by Stefan Kaestle <[email protected]>Signed-off-by Stefan Kaestle <[email protected]>Signed-off-by Malak Marrid <[email protected]>Signed-off-by Nicolin Chen <[email protected]>
Re: [PATCH v5 10/15] iommu/arm-smmu-v3: Support PRI Page Request in arm_smmu_handle_ppr()
In reply to Nicolin ChenQuoted 18 lines
[ ... 60 lines skipped ... ]
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c +17
-0+mutex_lock (&smmu->streams_mutex); + master =arm_smmu_find_master (smmu, sid); +if (master && master->pri_enabled) { +struct iopf_fault iopf_fault = {}; +struct iommu_fault *fault = &iopf_fault.fault; + + fault->type =IOMMU_FAULT_PAGE_REQ ; +if (last) + fault->prm.flags |=IOMMU_FAULT_PAGE_REQUEST_LAST_PAGE ; +if (ssv) { + fault->prm.flags |= +IOMMU_FAULT_PAGE_REQUEST_PASID_VALID ; + fault->prm.pasid = ssid; + } + fault->prm.grpid = grpid; +if (evt[0 ] &PRIQ_0_PERM_READ ) + fault->prm.perm |=IOMMU_FAULT_PERM_READ ;
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c static void arm_smmu_handle_ppr(struct arm_smmu_device *smmu, u64 *evt) · line 2431
Quoted 10 lines
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c +10
-0+if (evt[0 ] &PRIQ_0_PERM_WRITE ) + fault->prm.perm |=IOMMU_FAULT_PERM_WRITE ; +if (evt[0 ] &PRIQ_0_PERM_EXEC ) + fault->prm.perm |=IOMMU_FAULT_PERM_EXEC ; +if (evt[0 ] &PRIQ_0_PERM_PRIV ) + fault->prm.perm |=IOMMU_FAULT_PERM_PRIV ; + fault->prm.addr =FIELD_GET (PRIQ_1_ADDR_MASK , evt[1 ]) <<12 ; + +iommu_report_device_fault (master->dev, &iopf_fault); +mutex_unlock (&smmu->streams_mutex);
This doesn't need to hold this mutex when calling report, the irq should be synchronized when the master is removed to keep dev stable.
Signature · 1 line
Jason
PATCH v5 11/15 iommu/arm-smmu-v3: Discard partial PRI faults on PRIQ overflow
In reply to Nicolin ChenOn PRIQ overflow, partial requests stored via report_partial_fault() whose LAST-page entry was lost stay in iopf_param->partial. Drop them by calling iopf_queue_discard_partial(), matching intel-iommu's handling.
Do it only after the queue is fully drained, since the visible entries all precede the loss point: a group whose LAST-page entry is still in the queue gets assembled before the discard, rather than losing its stored partials to it.
Nothing can arrive in between, as an active overflow inhibits new entries from being written to the PRI queue until a final CONS write acknowledges it (IHI0070 8.1).
--- drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-)
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c +13 -1
@@ -2454,15 +2454,27 @@ static irqreturn_t arm_smmu_priq_thread(int irq, void *dev)struct arm_smmu_queue *q = &smmu->priq.q;struct arm_smmu_ll_queue *llq = &q->llq;u64 evt[PRIQ_ENT_DWORDS ]; +bool overflow = false;do {while (!queue_remove_raw (q, evt))arm_smmu_handle_ppr (smmu, evt); -if (queue_sync_prod_in (q) == -EOVERFLOW ) +if (queue_sync_prod_in (q) == -EOVERFLOW ) {dev_err (smmu->dev,"PRIQ overflow detected -- requests lost\n" ); + overflow = true; + } }while (!queue_empty (llq)); +/* +* Discard the partial faults after the drain, so any group with its +* LAST-page entry visible in the queue gets assembled beforehand. An +* active overflow condition inhibits new entries from being written +* to the PRI queue, until it gets acknowledged below. +*/ +if (overflow) +iopf_queue_discard_partial (smmu->evtq.iopf); +/* Sync our overflow flag, as we believe we're up to speed */ queue_sync_cons_ovf (q);return IRQ_HANDLED ;
Signature · 1 line
2.43.0
[email protected]
AI reviewRe: [PATCH v5 11/15] iommu/arm-smmu-v3: Discard partial PRI faults on PRIQ overflow
Sashiko has reviewed this patch and found no issues. It looks great!
Quoted 9 lines
On PRIQ overflow, partial requests stored via
report_partial_fault()whose LAST-page entry was lost stay iniopf_param->partial. Drop them by callingiopf_queue_discard_partial(), matching intel-iommu's handling.Do it only after the queue is fully drained, since the visible entries all precede the loss point: a group whose LAST-page entry is still in the queue gets assembled before the discard, rather than losing its stored partials to it.
Quoted 8 lines
Nothing can arrive in between, as an active overflow inhibits new entries from being written to the PRI queue until a final CONS write acknowledges it (IHI0070 8.1).
Suggested-by Harsha Vardhan V <[email protected]>Reviewed-by Jonathan Cameron <[email protected]>Assisted-by LLMSigned-off-by Nicolin Chen <[email protected]>
Sashiko has reviewed this patch and found no issues. It looks great!
Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=11
Re: [PATCH v5 11/15] iommu/arm-smmu-v3: Discard partial PRI faults on PRIQ overflow
In reply to Nicolin ChenQuoted 10 lines · Nicolin Chen
On PRIQ overflow, partial requests stored via
report_partial_fault()whose LAST-page entry was lost stay iniopf_param->partial. Drop them by callingiopf_queue_discard_partial(), matching intel-iommu's handling.Do it only after the queue is fully drained, since the visible entries all precede the loss point: a group whose LAST-page entry is still in the queue gets assembled before the discard, rather than losing its stored partials to it.
[...]
Signature · 1 line
Jason
PATCH v5 12/15 iommu/arm-smmu-v3: Allocate IOPF queue for ARM_SMMU_FEAT_PRI
In reply to Nicolin ChenAfter the previous change drops the FEAT_SVA gate, arm_smmu_init_queues() allocates smmu->evtq.iopf when the SMMU advertises FEAT_STALLS. PRI support is being added on top of the IOPF infrastructure, so extend the allocation condition to cover SMMUs that advertise FEAT_PRI without FEAT_STALLS.
--- drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c +1 -1
@@ -4753,7 +4753,7 @@ static int arm_smmu_init_queues(struct arm_smmu_device *smmu)if (ret)return ret; -if (smmu->features &ARM_SMMU_FEAT_STALLS ) { +if (smmu->features & (ARM_SMMU_FEAT_STALLS |ARM_SMMU_FEAT_PRI )) { smmu->evtq.iopf =iopf_queue_alloc (dev_name (smmu->dev));if (!smmu->evtq.iopf)return -ENOMEM ;
Signature · 1 line
2.43.0
[email protected]
AI reviewRe: [PATCH v5 12/15] iommu/arm-smmu-v3: Allocate IOPF queue for ARM_SMMU_FEAT_PRI
Sashiko has reviewed this patch and found no issues. It looks great!
Quoted 8 lines
After the previous change drops the
FEAT_SVAgate,arm_smmu_init_queues()allocatessmmu->evtq.iopfwhen the SMMU advertisesFEAT_STALLS. PRI support is being added on top of the IOPF infrastructure, so extend the allocation condition to cover SMMUs that advertiseFEAT_PRIwithoutFEAT_STALLS.Reviewed-by Jonathan Cameron <[email protected]>Assisted-by LLMSigned-off-by Nicolin Chen <[email protected]>
Sashiko has reviewed this patch and found no issues. It looks great!
Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=12
Re: [PATCH v5 12/15] iommu/arm-smmu-v3: Allocate IOPF queue for ARM_SMMU_FEAT_PRI
In reply to Nicolin ChenQuoted 4 lines · Nicolin Chen
After the previous change drops the
FEAT_SVAgate,arm_smmu_init_queues()allocatessmmu->evtq.iopfwhen the SMMU advertisesFEAT_STALLS. PRI support is being added on top of the IOPF infrastructure, so extend the allocation condition to cover SMMUs that advertiseFEAT_PRIwithoutFEAT_STALLS.
Signature · 1 line
Jason
PATCH v5 13/15 PCI/ATS: Add PRI stubs
In reply to Nicolin ChenFrom: Jean-Philippe Brucker <[email protected]>
The SMMUv3 driver, which may be built without CONFIG_PCI, will shortly gain PRI support. Partially revert commit c6e9aefbf9db ("PCI/ATS: Remove unused PRI and PASID stubs") to re-introduce the PRI stubs, avoiding more #ifdefs in the SMMU driver.
--- include/linux/pci-ats.h | 5 +++++ 1 file changed, 5 insertions(+)
include/linux/pci-ats.h +5 -0
@@ -36,6 +36,11 @@ int pci_reset_pri(struct pci_dev *pdev);int pci_prg_resp_pasid_required (struct pci_dev *pdev);bool pci_pri_supported (struct pci_dev *pdev);#else +static inline int pci_enable_pri (struct pci_dev *pdev,u32 reqs) +{return -ENODEV ; } +static inline void pci_disable_pri (struct pci_dev *pdev) { } +static inline int pci_reset_pri (struct pci_dev *pdev) +{return -ENODEV ; }static inline bool pci_pri_supported (struct pci_dev *pdev) {return false; }#endif /* CONFIG_PCI_PRI */
Signature · 1 line
2.43.0
[email protected]
AI reviewRe: [PATCH v5 13/15] PCI/ATS: Add PRI stubs
Sashiko has reviewed this patch and found no issues. It looks great!
Quoted 7 lines
From: Jean-Philippe Brucker <[email protected]>
The SMMUv3 driver, which may be built without
CONFIG_PCI, will shortly gain PRI support. Partially revert commitc6e9aefbf9db("PCI/ATS: Remove unused PRI and PASID stubs") to re-introduce the PRI stubs, avoiding more #ifdefs in the SMMU driver.
Quoted 6 lines
Acked-by Bjorn Helgaas <[email protected]>Reviewed-by Kuppuswamy Sathyanarayanan <[email protected]>Signed-off-by Jean-Philippe Brucker <[email protected]>[nicolinc: drop stale APIs]Signed-off-by Nicolin Chen <[email protected]>
Sashiko has reviewed this patch and found no issues. It looks great!
Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=13
Re: [PATCH v5 13/15] PCI/ATS: Add PRI stubs
In reply to Nicolin ChenQuoted 4 lines · Nicolin Chen
The SMMUv3 driver, which may be built without
CONFIG_PCI, will shortly gain PRI support. Partially revert commitc6e9aefbf9db("PCI/ATS: Remove unused PRI and PASID stubs") to re-introduce the PRI stubs, avoiding more #ifdefs in the SMMU driver.
Signature · 1 line
Jason
PATCH v5 14/15 PCI/ATS: Export pci_enable_pri() and pci_reset_pri()
In reply to Nicolin ChenFrom: Jean-Philippe Brucker <[email protected]>
Export pci_enable_pri() and pci_reset_pri() so the SMMUv3 driver, which is tristate, can use them from its probe path when built as a module.
--- drivers/pci/ats.c | 2 ++ 1 file changed, 2 insertions(+)
drivers/pci/ats.c +2 -0
@@ -320,6 +320,7 @@ int pci_enable_pri(struct pci_dev *pdev, u32 reqs)return 0 ; } +EXPORT_SYMBOL_GPL (pci_enable_pri);/** * pci_disable_pri - Disable PRI capability
@@ -399,6 +400,7 @@ int pci_reset_pri(struct pci_dev *pdev)return 0 ; } +EXPORT_SYMBOL_GPL (pci_reset_pri);/** * pci_prg_resp_pasid_required - Return PRG Response PASID Required bit
Signature · 1 line
2.43.0
[email protected]
AI reviewRe: [PATCH v5 14/15] PCI/ATS: Export pci_enable_pri() and pci_reset_pri()
Sashiko has reviewed this patch and found no issues. It looks great!
Quoted 11 lines
From: Jean-Philippe Brucker <[email protected]>
Export
pci_enable_pri()andpci_reset_pri()so the SMMUv3 driver, which is tristate, can use them from its probe path when built as a module.Acked-by Bjorn Helgaas <[email protected]>Reviewed-by Kuppuswamy Sathyanarayanan <[email protected]>Signed-off-by Jean-Philippe Brucker <[email protected]>[nicolinc: drop stale or already-exported APIs]Signed-off-by Nicolin Chen <[email protected]>
Sashiko has reviewed this patch and found no issues. It looks great!
Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=14
Re: [PATCH v5 14/15] PCI/ATS: Export pci_enable_pri() and pci_reset_pri()
In reply to Nicolin ChenQuoted 2 lines · Nicolin Chen
Export
pci_enable_pri()andpci_reset_pri()so the SMMUv3 driver, which is tristate, can use them from its probe path when built as a module.
Signature · 1 line
Jason
PATCH v5 15/15 iommu/arm-smmu-v3: Enable PRI for PCI device in arm_smmu_probe_device()
In reply to Nicolin ChenPRI handling is now ready. Enable PRI when probing a PCI device, and drain the PRIQ with the EVTQ when an IOPF-enabled attachment ends. Queued fault work may still refer to the old domain when the detach call returns.
Note that PRI is enabled at the probe time, while ATS gets toggled by the attach/detach routines, so a master could have PRI enabled when its ATS is disabled. PCIe (Base 6.3, Table 10-14) sets no ATS precondition on the PRI Enable bit. Its only ordering rule is that the interface must have gotten successfully Stopped prior to an enabling, which pci_enable_pri() already checks using PCI_PRI_STATUS_STOPPED. Also, a PRI-enabled device would not issue a page request until it starts to use ATS.
Set the per-device outstanding request budget to the full priq depth, same as intel-iommu's per-device PRQ_DEPTH choice. A fixed per-device cap won't prevent multiple PRI-capable devices from potentially exceeding the priq's capacity; priq overflow is recoverable per the SMMUv3 spec, and it is rare in practice.
Select PCI_PRI in Kconfig like other IOMMUs, gated on PCI so the build can stay clean for non-PCI ARM SMMUv3 configurations.
A stalling master resolves its faults synchronously, so it has no reason at all to issue a page request. Refuse to enable PRI on such a master, so that page_response() can keep dispatching on the master state unambiguously.
Refuse to enable PRI as well on any master reporting num_streams != 1, as arm_smmu_enable_iopf() rejects multi-stream masters, so IOPF cannot be set up for them; keeping PRI enabled would let a PRI request arrive on an alias StreamID and get a PRI_RESP_DENY issued against streams[0] by the driver's error-response path.
--- drivers/iommu/arm/Kconfig | 1 + drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c | 52 +++++++++++++++++++++ 2 files changed, 53 insertions(+)
drivers/iommu/arm/Kconfig +1 -0
@@ -80,6 +80,7 @@ config ARM_SMMU_V3 select IOMMU_IO_PGTABLE_LPAE select IOMMU_IOPF select GENERIC_MSI_IRQ + select PCI_PRI if PCI select IOMMUFD_DRIVER if IOMMUFD help Support for implementations of the ARM System MMU architecture
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c +52 -0
76 lines not on this page · raw message
Signature · 1 line
2.43.0
[email protected]
AI reviewRe: [PATCH v5 15/15] iommu/arm-smmu-v3: Enable PRI for PCI device in arm_smmu_probe_device()
Sashiko has reviewed this patch and found no issues. It looks great!
Quoted 19 lines
PRI handling is now ready. Enable PRI when probing a PCI device, and drain the PRIQ with the EVTQ when an IOPF-enabled attachment ends. Queued fault work may still refer to the old domain when the detach call returns.
Note that PRI is enabled at the probe time, while ATS gets toggled by the attach/detach routines, so a master could have PRI enabled when its ATS is disabled. PCIe (Base 6.3, Table 10-14) sets no ATS precondition on the PRI Enable bit. Its only ordering rule is that the interface must have gotten successfully Stopped prior to an enabling, which
pci_enable_pri()already checks usingPCI_PRI_STATUS_STOPPED. Also, a PRI-enabled device would not issue a page request until it starts to use ATS.Set the per-device outstanding request budget to the full priq depth, same as intel-iommu's per-device
PRQ_DEPTHchoice. A fixed per-device cap won't prevent multiple PRI-capable devices from potentially exceeding the priq's [ ... ]
StreamID and get aPRI_RESP_DENYissued against streams[0] by the driver's error-response path.
Quoted 2 lines
Reviewed-by Jonathan Cameron <[email protected]>Signed-off-by Nicolin Chen <[email protected]>
Sashiko has reviewed this patch and found no issues. It looks great!
Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=15
Will Deacon
maintainerQuoted 12 lines · Nicolin Chen
The SMMUv3 driver doesn't handle events on the PRI queue or respond to IOPF faults. This series adds the missing pieces, using the IOPF infrastructure, to convert PRI page requests into
iopf_faultsand issueCMDQ_OP_PRI_RESP.The
iopf_queue_flush_dev()contract requires the driver to first drain the hardware PRI queue and synchronize using a threaded IRQ handler before the IOPF software flush. This drove the additional commits compared to v1:
-arm_smmu_wait_for_queue_drained()drains the hardware queue by counting the entries that the threaded IRQ handler consumes
-arm_smmu_attach_release()moves the teardown outside the global lock
-synchronize_irq()closes the gap before the final flush
Quoted 1 line
[...]
Applied first three to iommu (arm/smmu/updates), thanks!
[01/15] iommu/arm-smmu-v3: Disable the impl before disabling the SMMU on shutdown
https://git.kernel.org/iommu/c/ab0dbf411ace [02/15] iommu/arm-smmu-v3: Add arm_smmu_attach_release() https://git.kernel.org/iommu/c/580bbfcdcd2e [03/15] iommu/arm-smmu-v3: Add Q_POS() macro https://git.kernel.org/iommu/c/02b5c4639bcd
Cheers,
Signature · 5 lines
Will https://fixes.arm64.dev https://next.arm64.dev https://will.arm64.dev