* [PATCH v2 00/11] iommu/arm-smmu-v3: Add PRI support
@ 2026-05-28 7:59 Nicolin Chen
2026-05-28 7:59 ` [PATCH v2 01/11] iommu/arm-smmu-v3: Add arm_smmu_attach_release() Nicolin Chen
` (10 more replies)
0 siblings, 11 replies; 12+ messages in thread
From: Nicolin Chen @ 2026-05-28 7:59 UTC (permalink / raw)
To: will, robin.murphy, jgg
Cc: joro, bhelgaas, praan, kevin.tian, kees, smostafa, baolu.lu,
linux-arm-kernel, iommu, linux-kernel, linux-pci, skaestle,
mmarrid, skolothumtho, bbiber
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_drain_queue_for_iopf() drains the hardware queue to the PROD
snapshot
- arm_smmu_attach_release() moves the teardown outside the global lock
- synchronize_irq() closes the gap before the final flush
This is on Github:
https://github.com/nicolinc/iommufd/commits/smmuv3_pri-v2
FWIW, engineers on the NVIDIA side have managed to verify the PRI feature.
Changelog
v2:
* 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/cover.1772568590.git.nicolinc@nvidia.com/
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 (8):
iommu/arm-smmu-v3: Add arm_smmu_attach_release()
iommu/arm-smmu-v3: Factor out __queue_empty() and __queue_consumed()
iommu/arm-smmu-v3: Add arm_smmu_drain_queue_for_iopf() helper
iommu/arm-smmu-v3: Drain in-flight fault handlers
iommu/arm-smmu-v3: Support PRI Page Request in arm_smmu_handle_ppr()
iommu/arm-smmu-v3: Disable PRI when no IRQ handler is registered
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 | 1 +
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h | 3 +
include/linux/iommu.h | 1 +
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 | 244 +++++++++++++++---
drivers/pci/ats.c | 2 +
7 files changed, 221 insertions(+), 36 deletions(-)
base-commit: 74fa4c177ad09800b007cba043370c887bb1b4e3
--
2.43.0
^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH v2 01/11] iommu/arm-smmu-v3: Add arm_smmu_attach_release()
2026-05-28 7:59 [PATCH v2 00/11] iommu/arm-smmu-v3: Add PRI support Nicolin Chen
@ 2026-05-28 7:59 ` Nicolin Chen
2026-05-28 7:59 ` [PATCH v2 02/11] iommu/arm-smmu-v3: Factor out __queue_empty() and __queue_consumed() Nicolin Chen
` (9 subsequent siblings)
10 siblings, 0 replies; 12+ messages in thread
From: Nicolin Chen @ 2026-05-28 7:59 UTC (permalink / raw)
To: will, robin.murphy, jgg
Cc: joro, bhelgaas, praan, kevin.tian, kees, smostafa, baolu.lu,
linux-arm-kernel, iommu, linux-kernel, linux-pci, skaestle,
mmarrid, skolothumtho, bbiber
The 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. But
the teardown must drain any in-flight IOPF (for the old domain), before the
master_domain is freed via iopf_queue_flush_dev() calling flush_workqueue()
that can block on user-faulting page-fault handlers. Doing so when holding
the arm_smmu_asid_lock would stall any 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.
Since no other device would use the old master_domain that is being freed,
it's safe to move out of arm_smmu_asid_lock (still under the protection of
iommu_group->mutex).
Note: this is a pure refactor; no functional change; it is a prerequisite
to apply bug fix calling iopf_queue_flush_dev().
Fixes: cfea71aea921 ("iommu/arm-smmu-v3: Put iopf enablement in the domain attach path")
Cc: stable@vger.kernel.org # v6.16
Signed-off-by: Nicolin Chen <nicolinc@nvidia.com>
---
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 | 33 +++++++++++++++----
3 files changed, 30 insertions(+), 6 deletions(-)
diff --git a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h
index 16353596e08ad..2bb810e4d5fce 100644
--- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h
+++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h
@@ -1197,12 +1197,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);
diff --git a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-iommufd.c b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-iommufd.c
index 1e9f7d2de3441..e53c8e97ba190 100644
--- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-iommufd.c
+++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-iommufd.c
@@ -191,6 +191,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;
}
diff --git a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
index 8ce3e801eda3b..620c67811df48 100644
--- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
+++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
@@ -3195,9 +3195,9 @@ arm_smmu_master_build_invs(struct arm_smmu_master *master, bool ats_enabled,
return master->build_invs;
}
-static void arm_smmu_remove_master_domain(struct arm_smmu_master *master,
- struct iommu_domain *domain,
- ioasid_t ssid)
+static struct arm_smmu_master_domain *
+arm_smmu_remove_master_domain(struct arm_smmu_master *master,
+ struct iommu_domain *domain, ioasid_t ssid)
{
struct arm_smmu_domain *smmu_domain = to_smmu_domain_devices(domain);
struct arm_smmu_master_domain *master_domain;
@@ -3205,7 +3205,7 @@ static void arm_smmu_remove_master_domain(struct arm_smmu_master *master,
unsigned long flags;
if (!smmu_domain)
- return;
+ return NULL;
if (domain->type == IOMMU_DOMAIN_NESTED)
nested_ats_flush = to_smmu_nested_domain(domain)->enable_ats;
@@ -3220,8 +3220,23 @@ static void arm_smmu_remove_master_domain(struct arm_smmu_master *master,
}
spin_unlock_irqrestore(&smmu_domain->devices_lock, flags);
+ /* arm_smmu_attach_release() will free it */
+ return master_domain;
+}
+
+/* Release the old master_domain detached by arm_smmu_remove_master_domain() */
+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;
+
+ iommu_group_mutex_assert(master->dev);
+
+ if (!master_domain)
+ return;
arm_smmu_disable_iopf(master, master_domain);
kfree(master_domain);
+ state->old_master_domain = NULL;
}
/*
@@ -3519,7 +3534,8 @@ void arm_smmu_attach_commit(struct arm_smmu_attach_state *state)
arm_smmu_atc_inv_master(master, IOMMU_NO_PASID);
}
- arm_smmu_remove_master_domain(master, state->old_domain, state->ssid);
+ state->old_master_domain = arm_smmu_remove_master_domain(
+ master, state->old_domain, state->ssid);
arm_smmu_install_old_domain_invs(state);
master->ats_enabled = state->ats_enabled;
}
@@ -3594,6 +3610,7 @@ static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev,
arm_smmu_attach_commit(&state);
mutex_unlock(&arm_smmu_asid_lock);
+ arm_smmu_attach_release(&state);
return 0;
}
@@ -3694,6 +3711,7 @@ int arm_smmu_set_pasid(struct arm_smmu_master *master,
out_unlock:
mutex_unlock(&arm_smmu_asid_lock);
+ arm_smmu_attach_release(&state);
return ret;
}
@@ -3714,9 +3732,11 @@ static int arm_smmu_blocking_set_dev_pasid(struct iommu_domain *new_domain,
arm_smmu_clear_cd(master, pasid);
if (master->ats_enabled)
arm_smmu_atc_inv_master(master, pasid);
- arm_smmu_remove_master_domain(master, &smmu_domain->domain, pasid);
+ state.old_master_domain = arm_smmu_remove_master_domain(
+ master, &smmu_domain->domain, pasid);
arm_smmu_install_old_domain_invs(&state);
mutex_unlock(&arm_smmu_asid_lock);
+ arm_smmu_attach_release(&state);
/*
* When the last user of the CD table goes away downgrade the STE back
@@ -3774,6 +3794,7 @@ static void arm_smmu_attach_dev_ste(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);
/*
* This has to be done after removing the master from the
--
2.43.0
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH v2 02/11] iommu/arm-smmu-v3: Factor out __queue_empty() and __queue_consumed()
2026-05-28 7:59 [PATCH v2 00/11] iommu/arm-smmu-v3: Add PRI support Nicolin Chen
2026-05-28 7:59 ` [PATCH v2 01/11] iommu/arm-smmu-v3: Add arm_smmu_attach_release() Nicolin Chen
@ 2026-05-28 7:59 ` Nicolin Chen
2026-05-28 7:59 ` [PATCH v2 03/11] iommu/arm-smmu-v3: Add arm_smmu_drain_queue_for_iopf() helper Nicolin Chen
` (8 subsequent siblings)
10 siblings, 0 replies; 12+ messages in thread
From: Nicolin Chen @ 2026-05-28 7:59 UTC (permalink / raw)
To: will, robin.murphy, jgg
Cc: joro, bhelgaas, praan, kevin.tian, kees, smostafa, baolu.lu,
linux-arm-kernel, iommu, linux-kernel, linux-pci, skaestle,
mmarrid, skolothumtho, bbiber
queue_empty() and queue_consumed() each compare a ring-position pair where
one operand is fixed to a cached llq field (q->prod, q->cons).
A subsequent change will need the same ring-position comparisons against a
CONS value read live from MMIO into a local, which does not live in q->llq.
Factor the ring-position checks in queue_empty() and queue_consumed() into
__queue_empty() and __queue_consumed() primitives that accept both operands
explicitly. queue_empty() and queue_consumed() become pass-through wrappers
that pass the cached fields.
No functional change intended; it's a prerequisite to apply bug fix calling
iopf_queue_flush_dev().
Fixes: cfea71aea921 ("iommu/arm-smmu-v3: Put iopf enablement in the domain attach path")
Cc: stable@vger.kernel.org # v6.16
Assisted-by: Claude:claude-opus-4-7
Signed-off-by: Nicolin Chen <nicolinc@nvidia.com>
---
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c | 22 +++++++++++++++------
1 file changed, 16 insertions(+), 6 deletions(-)
diff --git a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
index 620c67811df48..cf41b3cf5985f 100644
--- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
+++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
@@ -146,18 +146,28 @@ static bool queue_full(struct arm_smmu_ll_queue *q)
Q_WRP(q, q->prod) != Q_WRP(q, q->cons);
}
+static bool __queue_empty(struct arm_smmu_ll_queue *q, u32 cons, u32 prod)
+{
+ return Q_IDX(q, prod) == Q_IDX(q, cons) &&
+ Q_WRP(q, prod) == Q_WRP(q, cons);
+}
+
static bool queue_empty(struct arm_smmu_ll_queue *q)
{
- return Q_IDX(q, q->prod) == Q_IDX(q, q->cons) &&
- Q_WRP(q, q->prod) == Q_WRP(q, q->cons);
+ return __queue_empty(q, q->cons, q->prod);
+}
+
+static bool __queue_consumed(struct arm_smmu_ll_queue *q, u32 cons, u32 prod)
+{
+ return ((Q_WRP(q, cons) == Q_WRP(q, prod)) &&
+ (Q_IDX(q, cons) > Q_IDX(q, prod))) ||
+ ((Q_WRP(q, cons) != Q_WRP(q, prod)) &&
+ (Q_IDX(q, cons) <= Q_IDX(q, prod)));
}
static bool queue_consumed(struct arm_smmu_ll_queue *q, u32 prod)
{
- return ((Q_WRP(q, q->cons) == Q_WRP(q, prod)) &&
- (Q_IDX(q, q->cons) > Q_IDX(q, prod))) ||
- ((Q_WRP(q, q->cons) != Q_WRP(q, prod)) &&
- (Q_IDX(q, q->cons) <= Q_IDX(q, prod)));
+ return __queue_consumed(q, q->cons, prod);
}
static void queue_sync_cons_out(struct arm_smmu_queue *q)
--
2.43.0
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH v2 03/11] iommu/arm-smmu-v3: Add arm_smmu_drain_queue_for_iopf() helper
2026-05-28 7:59 [PATCH v2 00/11] iommu/arm-smmu-v3: Add PRI support Nicolin Chen
2026-05-28 7:59 ` [PATCH v2 01/11] iommu/arm-smmu-v3: Add arm_smmu_attach_release() Nicolin Chen
2026-05-28 7:59 ` [PATCH v2 02/11] iommu/arm-smmu-v3: Factor out __queue_empty() and __queue_consumed() Nicolin Chen
@ 2026-05-28 7:59 ` Nicolin Chen
2026-05-28 7:59 ` [PATCH v2 04/11] iommu/arm-smmu-v3: Drain in-flight fault handlers Nicolin Chen
` (7 subsequent siblings)
10 siblings, 0 replies; 12+ messages in thread
From: Nicolin Chen @ 2026-05-28 7:59 UTC (permalink / raw)
To: will, robin.murphy, jgg
Cc: joro, bhelgaas, praan, kevin.tian, kees, smostafa, baolu.lu,
linux-arm-kernel, iommu, linux-kernel, linux-pci, skaestle,
mmarrid, skolothumtho, bbiber
A poll-until-empty form does not converge on the shared EVTQ or PRIQ since
other masters keep advancing PROD with unrelated traffic. A fixed snapshot
bounds the wait even under sustained unrelated load, since the target does
not move with subsequent enqueues.
Add a helper that drains an SMMU EVTQ or PRIQ up to a PROD snapshot taken
on entry, waiting until CONS reaches the snapshot. Every entry already in
the queue is consumed by the threaded IRQ handler before this returns. A
subsequent change uses it from the IOPF attach-release path.
SMMUv3 guarantees that after a CMD_SYNC following STE/CD invalidation and
any CMD_ATC_INV, no further entries tied to the pre-SYNC configuration will
appear in either queue. A caller that has completed that sequence captures
its entire in-flight cohort in the snapshot.
Read CONS from MMIO each iteration so the comparison does not rely on the
cached llq->cons, which the threaded handler owns. Disable WFE since SMMU
does not signal SEV on EVTQ/PRIQ PROD advance. Call cond_resched() inside
the loop so this caller does not starve the threaded IRQ handler it waits
on when both share a CPU on a non-preemptible kernel.
This is a prerequisite to apply bug fix calling iopf_queue_flush_dev().
Fixes: cfea71aea921 ("iommu/arm-smmu-v3: Put iopf enablement in the domain attach path")
Cc: stable@vger.kernel.org # v6.16
Assisted-by: Claude:claude-opus-4-7
Signed-off-by: Nicolin Chen <nicolinc@nvidia.com>
---
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c | 27 +++++++++++++++++++++
1 file changed, 27 insertions(+)
diff --git a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
index cf41b3cf5985f..4794a15f351c4 100644
--- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
+++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
@@ -894,6 +894,33 @@ static int arm_smmu_cmdq_batch_submit(struct arm_smmu_device *smmu,
cmds->num, true);
}
+/* Drain an SMMU EVTQ or PRIQ to a PROD snapshot taken on entry */
+static int arm_smmu_drain_queue_for_iopf(struct arm_smmu_device *smmu,
+ struct arm_smmu_queue *q)
+{
+ struct arm_smmu_queue_poll qp;
+ u32 prod, cons;
+ int ret = 0;
+
+ /* Snapshot PROD; entries [old_cons, prod) are the cohort to drain */
+ prod = readl_relaxed(q->prod_reg);
+ queue_poll_init(smmu, &qp);
+ qp.wfe = false; /* No SEV on EVTQ/PRIQ PROD advance */
+ /* Read MMIO each iteration; llq->cons is the threaded handler's */
+ do {
+ cons = readl_relaxed(q->cons_reg);
+ if (__queue_empty(&q->llq, cons, prod) ||
+ __queue_consumed(&q->llq, cons, prod))
+ return 0;
+ cond_resched();
+ } while (!(ret = queue_poll(&qp)));
+
+ dev_warn_ratelimited(smmu->dev,
+ "queue drain timed out at prod=0x%x cons=0x%x\n",
+ prod, cons);
+ return ret;
+}
+
static void arm_smmu_page_response(struct device *dev, struct iopf_fault *unused,
struct iommu_page_response *resp)
{
--
2.43.0
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH v2 04/11] iommu/arm-smmu-v3: Drain in-flight fault handlers
2026-05-28 7:59 [PATCH v2 00/11] iommu/arm-smmu-v3: Add PRI support Nicolin Chen
` (2 preceding siblings ...)
2026-05-28 7:59 ` [PATCH v2 03/11] iommu/arm-smmu-v3: Add arm_smmu_drain_queue_for_iopf() helper Nicolin Chen
@ 2026-05-28 7:59 ` Nicolin Chen
2026-05-28 7:59 ` [PATCH v2 05/11] iommu/arm-smmu-v3: Submit CMDQ_OP_PRI_RESP for IOPF event Nicolin Chen
` (6 subsequent siblings)
10 siblings, 0 replies; 12+ messages in thread
From: Nicolin Chen @ 2026-05-28 7:59 UTC (permalink / raw)
To: will, robin.murphy, jgg
Cc: joro, bhelgaas, praan, kevin.tian, kees, smostafa, baolu.lu,
linux-arm-kernel, iommu, linux-kernel, linux-pci, skaestle,
mmarrid, skolothumtho, bbiber
When a device is switching away from a domain, either through a detach or a
replace operation, in-flight stall events for the old domain might still be
on the SMMU's hardware event queue or on the IOMMU core's IOPF queue. Thus,
if the IOMMU core swaps the device's attach_handle and frees the old domain
before those handlers complete, the IOPF work might hit use-after-free.
Two queues need to be drained: the SMMU hardware event queue and the IOMMU
core IOPF software workqueue.
Poll the evtq so that pending IRQs won't let the threaded handler run after
the drain and queue a fault against the freed master_domain.
Then, synchronize_irq() on the evtq. queue_remove_raw() advances MMIO CONS
before the caller pushes an event to the IOPF queue, so the first step does
not on its own guarantee every event has been queued. It must wait for the
IRQ handler to finish to close that gap.
Lastly, invoke iopf_queue_flush_dev() to drain the IOPF workqueue.
Fixes: cfea71aea921 ("iommu/arm-smmu-v3: Put iopf enablement in the domain attach path")
Cc: stable@vger.kernel.org # v6.16
Co-developed-by: Barak Biber <bbiber@nvidia.com>
Signed-off-by: Barak Biber <bbiber@nvidia.com>
Co-developed-by: Stefan Kaestle <skaestle@nvidia.com>
Signed-off-by: Stefan Kaestle <skaestle@nvidia.com>
Signed-off-by: Malak Marrid <mmarrid@nvidia.com>
Signed-off-by: Nicolin Chen <nicolinc@nvidia.com>
---
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c | 19 +++++++++++++++++++
1 file changed, 19 insertions(+)
diff --git a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
index 4794a15f351c4..ffc9621cd2288 100644
--- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
+++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
@@ -3267,10 +3267,29 @@ 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;
+ lockdep_assert_not_held(&arm_smmu_asid_lock);
iommu_group_mutex_assert(master->dev);
if (!master_domain)
return;
+
+ if (master_domain->using_iopf) {
+ struct arm_smmu_device *smmu = master->smmu;
+
+ /* Drain the hardware eventq */
+ if (master->stall_enabled) {
+ arm_smmu_drain_queue_for_iopf(smmu, &smmu->evtq.q);
+ /* Ensure pending events have reached the IOPF queue */
+ 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, drain the IOPF queue */
+ iopf_queue_flush_dev(master->dev);
+ }
+
arm_smmu_disable_iopf(master, master_domain);
kfree(master_domain);
state->old_master_domain = NULL;
--
2.43.0
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH v2 05/11] iommu/arm-smmu-v3: Submit CMDQ_OP_PRI_RESP for IOPF event
2026-05-28 7:59 [PATCH v2 00/11] iommu/arm-smmu-v3: Add PRI support Nicolin Chen
` (3 preceding siblings ...)
2026-05-28 7:59 ` [PATCH v2 04/11] iommu/arm-smmu-v3: Drain in-flight fault handlers Nicolin Chen
@ 2026-05-28 7:59 ` Nicolin Chen
2026-05-28 7:59 ` [PATCH v2 06/11] iommu/arm-smmu-v3: Support PRI Page Request in arm_smmu_handle_ppr() Nicolin Chen
` (5 subsequent siblings)
10 siblings, 0 replies; 12+ messages in thread
From: Nicolin Chen @ 2026-05-28 7:59 UTC (permalink / raw)
To: will, robin.murphy, jgg
Cc: joro, bhelgaas, praan, kevin.tian, kees, smostafa, baolu.lu,
linux-arm-kernel, iommu, linux-kernel, linux-pci, skaestle,
mmarrid, skolothumtho, bbiber
From: Malak Marrid <mmarrid@nvidia.com>
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.
However, either a stall event in the EVTQ or a PRI request in the PRIQ can
surface to the IOPF infrastructure with fault.type == IOMMU_FAULT_PAGE_REQ,
and a single master can in principle be both stall-capable and PRI-capable
(e.g. FEAT_STALL_FORCE on a PCIe device with PRI), so master state is not a
reliable discriminator.
Add IOMMU_FAULT_PAGE_REQUEST_STALLS_TRANS to the generic flags so the fault
reporter can mark a page request that is holding the device's transaction:
arm_smmu_handle_event() sets it on STALL events
arm_smmu_handle_ppr() leaves it clear for PRI events
Note: streams[0].id remains the RID because arm_smmu_enable_iopf() rejects
num_streams != 1.
Co-developed-by: Barak Biber <bbiber@nvidia.com>
Signed-off-by: Barak Biber <bbiber@nvidia.com>
Co-developed-by: Stefan Kaestle <skaestle@nvidia.com>
Signed-off-by: Stefan Kaestle <skaestle@nvidia.com>
Signed-off-by: Malak Marrid <mmarrid@nvidia.com>
Signed-off-by: Nicolin Chen <nicolinc@nvidia.com>
---
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h | 1 +
include/linux/iommu.h | 1 +
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c | 75 +++++++++++++++------
3 files changed, 58 insertions(+), 19 deletions(-)
diff --git a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h
index 2bb810e4d5fce..1083621705f16 100644
--- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h
+++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h
@@ -1007,6 +1007,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;
diff --git a/include/linux/iommu.h b/include/linux/iommu.h
index e587d4ac4d331..83c4dfcf20637 100644
--- a/include/linux/iommu.h
+++ b/include/linux/iommu.h
@@ -76,6 +76,7 @@ struct iommu_fault_page_request {
#define IOMMU_FAULT_PAGE_REQUEST_PASID_VALID (1 << 0)
#define IOMMU_FAULT_PAGE_REQUEST_LAST_PAGE (1 << 1)
#define IOMMU_FAULT_PAGE_RESPONSE_NEEDS_PASID (1 << 2)
+#define IOMMU_FAULT_PAGE_REQUEST_STALLS_TRANS (1 << 3)
u32 flags;
u32 pasid;
u32 grpid;
diff --git a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
index ffc9621cd2288..061f1d46fda0d 100644
--- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
+++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
@@ -921,32 +921,68 @@ static int arm_smmu_drain_queue_for_iopf(struct arm_smmu_device *smmu,
return ret;
}
-static void arm_smmu_page_response(struct device *dev, struct iopf_fault *unused,
+static void arm_smmu_page_response(struct device *dev, struct iopf_fault *evt,
struct iommu_page_response *resp)
{
struct arm_smmu_master *master = dev_iommu_priv_get(dev);
- u8 resume_resp;
+ struct arm_smmu_cmd cmd;
+ int sid;
- if (WARN_ON(!master->stall_enabled))
+ if (WARN_ON_ONCE(evt->fault.type != IOMMU_FAULT_PAGE_REQ))
return;
- switch (resp->code) {
- case IOMMU_PAGE_RESP_INVALID:
- case IOMMU_PAGE_RESP_FAILURE:
- 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;
+ /* IOPF is gated to num_streams == 1 in arm_smmu_enable_iopf() */
+ sid = master->streams[0].id;
+
+ if (evt->fault.prm.flags & IOMMU_FAULT_PAGE_REQUEST_STALLS_TRANS) {
+ u8 resume_resp;
+
+ if (WARN_ON_ONCE(!master->stall_enabled))
+ return;
+ switch (resp->code) {
+ case IOMMU_PAGE_RESP_INVALID:
+ case IOMMU_PAGE_RESP_FAILURE:
+ 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);
+ } else {
+ enum pri_resp pri_resp;
+ bool ssv;
+
+ if (WARN_ON_ONCE(!master->pri_enabled))
+ return;
+ /* PCIe allows only one PRG Response per group */
+ if (!(evt->fault.prm.flags &
+ IOMMU_FAULT_PAGE_REQUEST_LAST_PAGE))
+ return;
+ switch (resp->code) {
+ case IOMMU_PAGE_RESP_SUCCESS:
+ pri_resp = PRI_RESP_SUCC;
+ break;
+ case IOMMU_PAGE_RESP_FAILURE:
+ pri_resp = PRI_RESP_FAIL;
+ break;
+ case IOMMU_PAGE_RESP_INVALID:
+ pri_resp = PRI_RESP_DENY;
+ break;
+ default:
+ WARN_ON(true);
+ return;
+ }
+ ssv = !!(evt->fault.prm.flags &
+ IOMMU_FAULT_PAGE_REQUEST_PASID_VALID);
+ cmd = arm_smmu_make_cmd_pri_resp(sid, resp->pasid, ssv,
+ resp->grpid, pri_resp);
}
- arm_smmu_cmdq_issue_cmd(master->smmu,
- arm_smmu_make_cmd_resume(master->streams[0].id,
- resp->grpid,
- resume_resp));
+ arm_smmu_cmdq_issue_cmd(master->smmu, cmd);
/*
* Don't send a SYNC, it doesn't do anything for RESUME or PRI_RESP.
* RESUME consumption guarantees that the stalled transaction will be
@@ -2081,7 +2117,8 @@ static int arm_smmu_handle_event(struct arm_smmu_device *smmu, u64 *evt,
flt->type = IOMMU_FAULT_PAGE_REQ;
flt->prm = (struct iommu_fault_page_request){
- .flags = IOMMU_FAULT_PAGE_REQUEST_LAST_PAGE,
+ .flags = IOMMU_FAULT_PAGE_REQUEST_LAST_PAGE |
+ IOMMU_FAULT_PAGE_REQUEST_STALLS_TRANS,
.grpid = event->stag,
.perm = perm,
.addr = event->iova,
--
2.43.0
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH v2 06/11] iommu/arm-smmu-v3: Support PRI Page Request in arm_smmu_handle_ppr()
2026-05-28 7:59 [PATCH v2 00/11] iommu/arm-smmu-v3: Add PRI support Nicolin Chen
` (4 preceding siblings ...)
2026-05-28 7:59 ` [PATCH v2 05/11] iommu/arm-smmu-v3: Submit CMDQ_OP_PRI_RESP for IOPF event Nicolin Chen
@ 2026-05-28 7:59 ` Nicolin Chen
2026-05-28 7:59 ` [PATCH v2 07/11] iommu/arm-smmu-v3: Disable PRI when no IRQ handler is registered Nicolin Chen
` (4 subsequent siblings)
10 siblings, 0 replies; 12+ messages in thread
From: Nicolin Chen @ 2026-05-28 7:59 UTC (permalink / raw)
To: will, robin.murphy, jgg
Cc: joro, bhelgaas, praan, kevin.tian, kees, smostafa, baolu.lu,
linux-arm-kernel, iommu, linux-kernel, linux-pci, skaestle,
mmarrid, skolothumtho, bbiber
Now, arm_smmu_page_response() can issue CMDQ_OP_PRI_RESP for page requests
from the IOPF infrastructure. Forward PRI requests from the priq into the
IOPF infrastructure for PRI-enabled masters by building an iopf_fault from
the priq entry and calling iommu_report_device_fault().
For an unrecognised StreamID or a master without master->pri_enabled, fall
through to the existing "unexpected PRI request" log + LAST-page DENY path
to release the credit per the PCIe PRI spec.
Co-developed-by: Barak Biber <bbiber@nvidia.com>
Signed-off-by: Barak Biber <bbiber@nvidia.com>
Co-developed-by: Stefan Kaestle <skaestle@nvidia.com>
Signed-off-by: Stefan Kaestle <skaestle@nvidia.com>
Signed-off-by: Malak Marrid <mmarrid@nvidia.com>
Signed-off-by: Nicolin Chen <nicolinc@nvidia.com>
---
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c | 32 +++++++++++++++++++++
1 file changed, 32 insertions(+)
diff --git a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
index 061f1d46fda0d..371a8bbdf6756 100644
--- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
+++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
@@ -2242,6 +2242,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;
@@ -2252,6 +2253,37 @@ 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]);
+ mutex_lock(&smmu->streams_mutex);
+ master = arm_smmu_find_master(smmu, sid);
+ if (master && master->pri_enabled) {
+ struct iopf_fault iopf_fault = {0};
+ 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(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",
--
2.43.0
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH v2 07/11] iommu/arm-smmu-v3: Disable PRI when no IRQ handler is registered
2026-05-28 7:59 [PATCH v2 00/11] iommu/arm-smmu-v3: Add PRI support Nicolin Chen
` (5 preceding siblings ...)
2026-05-28 7:59 ` [PATCH v2 06/11] iommu/arm-smmu-v3: Support PRI Page Request in arm_smmu_handle_ppr() Nicolin Chen
@ 2026-05-28 7:59 ` Nicolin Chen
2026-05-28 7:59 ` [PATCH v2 08/11] iommu/arm-smmu-v3: Allocate IOPF queue for ARM_SMMU_FEAT_PRI Nicolin Chen
` (3 subsequent siblings)
10 siblings, 0 replies; 12+ messages in thread
From: Nicolin Chen @ 2026-05-28 7:59 UTC (permalink / raw)
To: will, robin.murphy, jgg
Cc: joro, bhelgaas, praan, kevin.tian, kees, smostafa, baolu.lu,
linux-arm-kernel, iommu, linux-kernel, linux-pci, skaestle,
mmarrid, skolothumtho, bbiber
arm_smmu_setup_irqs() has three failure paths that leave the priq without
a handler: a missing priq IRQ line, devm_request_threaded_irq() failure on
the priq IRQ, and devm_request_threaded_irq() failure on the combined IRQ.
Each path warned but kept ARM_SMMU_FEAT_PRI set in smmu->features.
With FEAT_PRI still set, arm_smmu_probe_device() calls pci_enable_pri() on
PCIe endpoints, which then issue PRI Page Requests that pile up in a priq
with no drainer; arm_smmu_setup_irqs() also enables IRQ_CTRL_PRIQ_IRQEN
against a handler that does not exist. Separately, arm_smmu_device_reset()
has already enabled CR0_PRIQEN before calling arm_smmu_setup_irqs(), so
the hardware queue stays enabled regardless of what setup_irqs decides.
Clear ARM_SMMU_FEAT_PRI from all three failure paths so subsequent code
treats PRI as unavailable. And disable CR0_PRIQEN after the setup function
returns.
Assisted-by: Claude:claude-opus-4-7
Signed-off-by: Nicolin Chen <nicolinc@nvidia.com>
---
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c | 13 +++++++++++--
1 file changed, 11 insertions(+), 2 deletions(-)
diff --git a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
index 371a8bbdf6756..72fd5caa27368 100644
--- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
+++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
@@ -4738,11 +4738,14 @@ 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->features &= ~ARM_SMMU_FEAT_PRI;
+ }
} else {
dev_warn(smmu->dev, "no priq irq - PRI will be broken\n");
+ smmu->features &= ~ARM_SMMU_FEAT_PRI;
}
}
}
@@ -4771,8 +4774,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->features &= ~ARM_SMMU_FEAT_PRI;
+ }
} else
arm_smmu_setup_unique_irqs(smmu);
@@ -4931,6 +4936,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);
--
2.43.0
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH v2 08/11] iommu/arm-smmu-v3: Allocate IOPF queue for ARM_SMMU_FEAT_PRI
2026-05-28 7:59 [PATCH v2 00/11] iommu/arm-smmu-v3: Add PRI support Nicolin Chen
` (6 preceding siblings ...)
2026-05-28 7:59 ` [PATCH v2 07/11] iommu/arm-smmu-v3: Disable PRI when no IRQ handler is registered Nicolin Chen
@ 2026-05-28 7:59 ` Nicolin Chen
2026-05-28 7:59 ` [PATCH v2 09/11] PCI/ATS: Add PRI stubs Nicolin Chen
` (2 subsequent siblings)
10 siblings, 0 replies; 12+ messages in thread
From: Nicolin Chen @ 2026-05-28 7:59 UTC (permalink / raw)
To: will, robin.murphy, jgg
Cc: joro, bhelgaas, praan, kevin.tian, kees, smostafa, baolu.lu,
linux-arm-kernel, iommu, linux-kernel, linux-pci, skaestle,
mmarrid, skolothumtho, bbiber
arm_smmu_init_queues() allocates smmu->evtq.iopf only when FEAT_SVA and
ARM_SMMU_FEAT_STALLS are both advertised. A subsequent change will add PRI
support on top of the IOPF infrastructure, so the IOPF workqueue must also
exist on SMMUs supporting FEAT_PRI without FEAT_STALLS. Extend the
allocation condition to include FEAT_PRI.
Signed-off-by: Nicolin Chen <nicolinc@nvidia.com>
---
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
index 72fd5caa27368..87e4880a145f1 100644
--- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
+++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
@@ -4504,7 +4504,7 @@ static int arm_smmu_init_queues(struct arm_smmu_device *smmu)
return ret;
if ((smmu->features & ARM_SMMU_FEAT_SVA) &&
- (smmu->features & ARM_SMMU_FEAT_STALLS)) {
+ (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;
--
2.43.0
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH v2 09/11] PCI/ATS: Add PRI stubs
2026-05-28 7:59 [PATCH v2 00/11] iommu/arm-smmu-v3: Add PRI support Nicolin Chen
` (7 preceding siblings ...)
2026-05-28 7:59 ` [PATCH v2 08/11] iommu/arm-smmu-v3: Allocate IOPF queue for ARM_SMMU_FEAT_PRI Nicolin Chen
@ 2026-05-28 7:59 ` Nicolin Chen
2026-05-28 7:59 ` [PATCH v2 10/11] PCI/ATS: Export pci_enable_pri() and pci_reset_pri() Nicolin Chen
2026-05-28 7:59 ` [PATCH v2 11/11] iommu/arm-smmu-v3: Enable PRI for PCI device in arm_smmu_probe_device() Nicolin Chen
10 siblings, 0 replies; 12+ messages in thread
From: Nicolin Chen @ 2026-05-28 7:59 UTC (permalink / raw)
To: will, robin.murphy, jgg
Cc: joro, bhelgaas, praan, kevin.tian, kees, smostafa, baolu.lu,
linux-arm-kernel, iommu, linux-kernel, linux-pci, skaestle,
mmarrid, skolothumtho, bbiber
From: Jean-Philippe Brucker <jean-philippe@linaro.org>
The SMMUv3 driver, which can be built without CONFIG_PCI, will soon gain
support for PRI. Partially revert commit c6e9aefbf9db ("PCI/ATS: Remove
unused PRI and PASID stubs") to re-introduce the PRI stubs, and avoid
adding more #ifdefs to the SMMU driver.
Link: https://lore.kernel.org/iommu/20201112125519.3987595-8-jean-philippe@linaro.org/
Acked-by: Bjorn Helgaas <bhelgaas@google.com>
Reviewed-by: Kuppuswamy Sathyanarayanan <sathyanarayanan.kuppuswamy@linux.intel.com>
Signed-off-by: Jean-Philippe Brucker <jean-philippe@linaro.org>
[nicolinc: drop stale APIs]
Signed-off-by: Nicolin Chen <nicolinc@nvidia.com>
---
include/linux/pci-ats.h | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/include/linux/pci-ats.h b/include/linux/pci-ats.h
index 75c6c86cf09dc..a1b249fb38bb4 100644
--- a/include/linux/pci-ats.h
+++ b/include/linux/pci-ats.h
@@ -33,6 +33,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 */
--
2.43.0
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH v2 10/11] PCI/ATS: Export pci_enable_pri() and pci_reset_pri()
2026-05-28 7:59 [PATCH v2 00/11] iommu/arm-smmu-v3: Add PRI support Nicolin Chen
` (8 preceding siblings ...)
2026-05-28 7:59 ` [PATCH v2 09/11] PCI/ATS: Add PRI stubs Nicolin Chen
@ 2026-05-28 7:59 ` Nicolin Chen
2026-05-28 7:59 ` [PATCH v2 11/11] iommu/arm-smmu-v3: Enable PRI for PCI device in arm_smmu_probe_device() Nicolin Chen
10 siblings, 0 replies; 12+ messages in thread
From: Nicolin Chen @ 2026-05-28 7:59 UTC (permalink / raw)
To: will, robin.murphy, jgg
Cc: joro, bhelgaas, praan, kevin.tian, kees, smostafa, baolu.lu,
linux-arm-kernel, iommu, linux-kernel, linux-pci, skaestle,
mmarrid, skolothumtho, bbiber
From: Jean-Philippe Brucker <jean-philippe@linaro.org>
The SMMUv3 driver, which is tristate, calls pci_enable_pri() and
pci_reset_pri() from its probe path. Export them so the driver can be
built as a module.
Link: https://lore.kernel.org/iommu/20201112125519.3987595-9-jean-philippe@linaro.org/
Acked-by: Bjorn Helgaas <bhelgaas@google.com>
Reviewed-by: Kuppuswamy Sathyanarayanan <sathyanarayanan.kuppuswamy@linux.intel.com>
Signed-off-by: Jean-Philippe Brucker <jean-philippe@linaro.org>
[nicolinc: drop stale or already-exported APIs]
Signed-off-by: Nicolin Chen <nicolinc@nvidia.com>
---
drivers/pci/ats.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/drivers/pci/ats.c b/drivers/pci/ats.c
index ec6c8dbdc5e9c..c844c0cee3cb7 100644
--- a/drivers/pci/ats.c
+++ b/drivers/pci/ats.c
@@ -266,6 +266,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
@@ -345,6 +346,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
--
2.43.0
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH v2 11/11] iommu/arm-smmu-v3: Enable PRI for PCI device in arm_smmu_probe_device()
2026-05-28 7:59 [PATCH v2 00/11] iommu/arm-smmu-v3: Add PRI support Nicolin Chen
` (9 preceding siblings ...)
2026-05-28 7:59 ` [PATCH v2 10/11] PCI/ATS: Export pci_enable_pri() and pci_reset_pri() Nicolin Chen
@ 2026-05-28 7:59 ` Nicolin Chen
10 siblings, 0 replies; 12+ messages in thread
From: Nicolin Chen @ 2026-05-28 7:59 UTC (permalink / raw)
To: will, robin.murphy, jgg
Cc: joro, bhelgaas, praan, kevin.tian, kees, smostafa, baolu.lu,
linux-arm-kernel, iommu, linux-kernel, linux-pci, skaestle,
mmarrid, skolothumtho, bbiber
Now PRI requests can be correctly handled. Enable the PCI cap when probing
a PCI device. Also flush the priq in arm_smmu_attach_release().
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.
Signed-off-by: Nicolin Chen <nicolinc@nvidia.com>
---
drivers/iommu/arm/Kconfig | 1 +
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c | 21 +++++++++++++++++++--
2 files changed, 20 insertions(+), 2 deletions(-)
diff --git a/drivers/iommu/arm/Kconfig b/drivers/iommu/arm/Kconfig
index 5fac08b89deea..855934d08f866 100644
--- a/drivers/iommu/arm/Kconfig
+++ b/drivers/iommu/arm/Kconfig
@@ -79,6 +79,7 @@ config ARM_SMMU_V3
select IOMMU_API
select IOMMU_IO_PGTABLE_LPAE
select GENERIC_MSI_IRQ
+ select PCI_PRI if PCI
select IOMMUFD_DRIVER if IOMMUFD
help
Support for implementations of the ARM System MMU architecture
diff --git a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
index 87e4880a145f1..648db9a24b582 100644
--- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
+++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
@@ -3149,7 +3149,7 @@ static int arm_smmu_enable_iopf(struct arm_smmu_master *master,
* device-specific fault handlers and don't need IOPF, so this is not a
* failure.
*/
- if (!master->stall_enabled)
+ if (!master->stall_enabled && !master->pri_enabled)
return 0;
/* We're not keeping track of SIDs in fault events */
@@ -3352,6 +3352,12 @@ void arm_smmu_attach_release(struct arm_smmu_attach_state *state)
if (smmu->evtq.q.irq)
synchronize_irq(smmu->evtq.q.irq);
}
+ /* Drain the hardware priq */
+ if (master->pri_enabled) {
+ arm_smmu_drain_queue_for_iopf(smmu, &smmu->priq.q);
+ if (smmu->priq.q.irq)
+ synchronize_irq(smmu->priq.q.irq);
+ }
/* Pending events might be in the combined_irq handler */
if (smmu->combined_irq)
synchronize_irq(smmu->combined_irq);
@@ -4282,8 +4288,17 @@ static struct iommu_device *arm_smmu_probe_device(struct device *dev)
if (dev_is_pci(dev)) {
unsigned int stu = __ffs(smmu->pgsize_bitmap);
+ struct pci_dev *pdev = to_pci_dev(dev);
- pci_prepare_ats(to_pci_dev(dev), stu);
+ if (!pci_prepare_ats(pdev, stu) && pci_pri_supported(pdev) &&
+ (smmu->features & ARM_SMMU_FEAT_PRI) && smmu->evtq.iopf) {
+ unsigned int reqs = 1 << smmu->priq.q.llq.max_n_shift;
+
+ if (!pci_reset_pri(pdev) && !pci_enable_pri(pdev, reqs))
+ master->pri_enabled = true;
+ else
+ dev_warn(master->dev, "failed to enable PRI\n");
+ }
}
return &smmu->iommu;
@@ -4299,6 +4314,8 @@ static void arm_smmu_release_device(struct device *dev)
WARN_ON(master->iopf_refcount);
+ if (master->pri_enabled)
+ pci_disable_pri(to_pci_dev(master->dev));
arm_smmu_disable_pasid(master);
arm_smmu_remove_master(master);
if (arm_smmu_cdtab_allocated(&master->cd_table))
--
2.43.0
^ permalink raw reply related [flat|nested] 12+ messages in thread
end of thread, other threads:[~2026-05-28 8:01 UTC | newest]
Thread overview: 12+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-05-28 7:59 [PATCH v2 00/11] iommu/arm-smmu-v3: Add PRI support Nicolin Chen
2026-05-28 7:59 ` [PATCH v2 01/11] iommu/arm-smmu-v3: Add arm_smmu_attach_release() Nicolin Chen
2026-05-28 7:59 ` [PATCH v2 02/11] iommu/arm-smmu-v3: Factor out __queue_empty() and __queue_consumed() Nicolin Chen
2026-05-28 7:59 ` [PATCH v2 03/11] iommu/arm-smmu-v3: Add arm_smmu_drain_queue_for_iopf() helper Nicolin Chen
2026-05-28 7:59 ` [PATCH v2 04/11] iommu/arm-smmu-v3: Drain in-flight fault handlers Nicolin Chen
2026-05-28 7:59 ` [PATCH v2 05/11] iommu/arm-smmu-v3: Submit CMDQ_OP_PRI_RESP for IOPF event Nicolin Chen
2026-05-28 7:59 ` [PATCH v2 06/11] iommu/arm-smmu-v3: Support PRI Page Request in arm_smmu_handle_ppr() Nicolin Chen
2026-05-28 7:59 ` [PATCH v2 07/11] iommu/arm-smmu-v3: Disable PRI when no IRQ handler is registered Nicolin Chen
2026-05-28 7:59 ` [PATCH v2 08/11] iommu/arm-smmu-v3: Allocate IOPF queue for ARM_SMMU_FEAT_PRI Nicolin Chen
2026-05-28 7:59 ` [PATCH v2 09/11] PCI/ATS: Add PRI stubs Nicolin Chen
2026-05-28 7:59 ` [PATCH v2 10/11] PCI/ATS: Export pci_enable_pri() and pci_reset_pri() Nicolin Chen
2026-05-28 7:59 ` [PATCH v2 11/11] iommu/arm-smmu-v3: Enable PRI for PCI device in arm_smmu_probe_device() Nicolin Chen
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox