[PATCH v3 06/13] iommu/arm-smmu-v3: Submit CMDQ_OP_PRI_RESP for IOPF event
Nicolin Chen
nicolinc at nvidia.com
Mon Aug 31 17:33:31 PDT 2026
From: Malak Marrid <mmarrid at 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.
A stall event in the EVTQ and a PRI request in the PRIQ both surface to the
IOPF infrastructure with fault.type == IOMMU_FAULT_PAGE_REQ. SMMUv3 forbids
the Stall model on PCIe streams (PCIe must use Terminate), and PRI is only
enabled on PCIe masters, so stall_enabled and pri_enabled never co-occur on
a single master. arm_smmu_page_response() can therefore key on the master
state: CMDQ_OP_RESUME for stall_enabled, CMDQ_OP_PRI_RESP for pri_enabled,
mapping IOMMU_PAGE_RESP_* to the PRI response codes.
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.
Co-developed-by: Barak Biber <bbiber at nvidia.com>
Signed-off-by: Barak Biber <bbiber at nvidia.com>
Co-developed-by: Stefan Kaestle <skaestle at nvidia.com>
Signed-off-by: Stefan Kaestle <skaestle at nvidia.com>
Signed-off-by: Malak Marrid <mmarrid at nvidia.com>
Signed-off-by: Nicolin Chen <nicolinc at nvidia.com>
---
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h | 1 +
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c | 75 +++++++++++++++------
2 files changed, 57 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 de7e4284658a1..92d21ded07ae9 100644
--- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h
+++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h
@@ -1015,6 +1015,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/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
index 352c916b2a572..64540cfb73244 100644
--- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
+++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
@@ -1028,32 +1028,69 @@ static int arm_smmu_drain_queue(struct arm_smmu_device *smmu,
return -ETIMEDOUT;
}
-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 (master->stall_enabled) {
+ u8 resume_resp;
+
+ 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 if (master->pri_enabled) {
+ enum pri_resp pri_resp;
+ bool ssv;
+
+ /* 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:
+ /* 0b00 ResponseFailure: a permanent non-paging error */
+ pri_resp = PRI_RESP_DENY;
+ break;
+ case IOMMU_PAGE_RESP_INVALID:
+ /* 0b01 InvalidRequest: page-in unsuccessful */
+ pri_resp = PRI_RESP_FAIL;
+ 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);
+ } else {
+ WARN_ON_ONCE(1);
+ return;
}
- 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
@@ -3209,7 +3246,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 */
--
2.43.0
More information about the linux-arm-kernel
mailing list