[PATCH v6 09/17] iommu/arm-smmu-v3: Introduce per-cmdq cmdq_err_handler callback
Nicolin Chen
nicolinc at nvidia.com
Wed Sep 23 13:11:28 PDT 2026
A subsequent change will need arm_smmu_cmdq_issue_cmdlist() to co-clear a
pending CMDQ_ERR after a CMD_SYNC poll timeout. And this needs to be done
for both smmu->cmdq and tegra241-cmdq.
Add a cmdq_err_handler and a paired cmdq_err_lock to struct arm_smmu_cmdq.
arm_smmu_gerror_handler() now takes the per-cmdq cmdq_err_lock when acking
CMDQ_ERR. It already covers a concurrent ack from cmdq_err_handler via its
existing early-exit on no-active-bits.
Impl functions and caller will be added in the subsequent change.
Assisted-by: LLM
Signed-off-by: Nicolin Chen <nicolinc at nvidia.com>
---
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h | 12 ++++++++++--
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c | 17 +++++++++++++----
drivers/iommu/arm/arm-smmu-v3/tegra241-cmdqv.c | 2 +-
3 files changed, 24 insertions(+), 7 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 b8c3fd7aeea45..ca8f52142b07f 100644
--- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h
+++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h
@@ -698,6 +698,10 @@ struct arm_smmu_queue_poll {
bool wfe;
};
+struct arm_smmu_cmdq;
+typedef void (*arm_smmu_cmdq_err_fn)(struct arm_smmu_device *smmu,
+ struct arm_smmu_cmdq *cmdq);
+
struct arm_smmu_cmdq {
struct arm_smmu_queue q;
atomic_long_t *valid_map;
@@ -705,6 +709,10 @@ struct arm_smmu_cmdq {
atomic_t lock;
unsigned long *atc_sync_timeouts;
bool (*supports_cmd)(struct arm_smmu_cmd *cmd);
+
+ /* Drain a pending CMDQ_ERR; will hold cmdq_err_lock with irqsave */
+ arm_smmu_cmdq_err_fn cmdq_err_handler;
+ raw_spinlock_t cmdq_err_lock;
};
static inline bool arm_smmu_cmdq_supports_cmd(struct arm_smmu_cmdq *cmdq,
@@ -1170,8 +1178,8 @@ int arm_smmu_init_one_queue(struct arm_smmu_device *smmu,
struct arm_smmu_queue *q, void __iomem *page,
unsigned long prod_off, unsigned long cons_off,
size_t dwords, const char *name);
-int arm_smmu_cmdq_init(struct arm_smmu_device *smmu,
- struct arm_smmu_cmdq *cmdq);
+int arm_smmu_cmdq_init(struct arm_smmu_device *smmu, struct arm_smmu_cmdq *cmdq,
+ arm_smmu_cmdq_err_fn cmdq_err_handler);
static inline bool arm_smmu_master_canwbs(struct arm_smmu_master *master)
{
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 24f286e994e6e..409ee1be76815 100644
--- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
+++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
@@ -2367,13 +2367,18 @@ static irqreturn_t arm_smmu_gerror_handler(int irq, void *dev)
{
u32 gerror, gerrorn, active;
struct arm_smmu_device *smmu = dev;
+ unsigned long flags;
+
+ raw_spin_lock_irqsave(&smmu->cmdq.cmdq_err_lock, flags);
gerror = readl_relaxed(smmu->base + ARM_SMMU_GERROR);
gerrorn = readl_relaxed(smmu->base + ARM_SMMU_GERRORN);
active = gerror ^ gerrorn;
- if (!(active & GERROR_ERR_MASK))
+ if (!(active & GERROR_ERR_MASK)) {
+ raw_spin_unlock_irqrestore(&smmu->cmdq.cmdq_err_lock, flags);
return IRQ_NONE; /* No errors pending */
+ }
dev_warn(smmu->dev,
"unexpected global error reported (0x%08x), this could be serious\n",
@@ -2382,6 +2387,7 @@ static irqreturn_t arm_smmu_gerror_handler(int irq, void *dev)
if (active & GERROR_SFM_ERR) {
/* SMMU is being disabled, so other errors don't matter */
writel(gerror, smmu->base + ARM_SMMU_GERRORN);
+ raw_spin_unlock_irqrestore(&smmu->cmdq.cmdq_err_lock, flags);
dev_err(smmu->dev, "device has entered Service Failure Mode!\n");
arm_smmu_device_disable(smmu);
return IRQ_HANDLED;
@@ -2409,6 +2415,7 @@ static irqreturn_t arm_smmu_gerror_handler(int irq, void *dev)
arm_smmu_cmdq_skip_err(smmu);
writel(gerror, smmu->base + ARM_SMMU_GERRORN);
+ raw_spin_unlock_irqrestore(&smmu->cmdq.cmdq_err_lock, flags);
return IRQ_HANDLED;
}
@@ -4529,13 +4536,15 @@ int arm_smmu_init_one_queue(struct arm_smmu_device *smmu,
return 0;
}
-int arm_smmu_cmdq_init(struct arm_smmu_device *smmu,
- struct arm_smmu_cmdq *cmdq)
+int arm_smmu_cmdq_init(struct arm_smmu_device *smmu, struct arm_smmu_cmdq *cmdq,
+ arm_smmu_cmdq_err_fn cmdq_err_handler)
{
unsigned int nents = 1 << cmdq->q.llq.max_n_shift;
atomic_set(&cmdq->owner_prod, 0);
atomic_set(&cmdq->lock, 0);
+ raw_spin_lock_init(&cmdq->cmdq_err_lock);
+ cmdq->cmdq_err_handler = cmdq_err_handler;
cmdq->valid_map = (atomic_long_t *)devm_bitmap_zalloc(smmu->dev, nents,
GFP_KERNEL);
@@ -4575,7 +4584,7 @@ static int arm_smmu_init_queues(struct arm_smmu_device *smmu)
if (ret)
return ret;
- ret = arm_smmu_cmdq_init(smmu, &smmu->cmdq);
+ ret = arm_smmu_cmdq_init(smmu, &smmu->cmdq, NULL);
if (ret)
return ret;
diff --git a/drivers/iommu/arm/arm-smmu-v3/tegra241-cmdqv.c b/drivers/iommu/arm/arm-smmu-v3/tegra241-cmdqv.c
index 6644075c1431e..a595769e7ecd6 100644
--- a/drivers/iommu/arm/arm-smmu-v3/tegra241-cmdqv.c
+++ b/drivers/iommu/arm/arm-smmu-v3/tegra241-cmdqv.c
@@ -676,7 +676,7 @@ static int tegra241_vcmdq_alloc_smmu_cmdq(struct tegra241_vcmdq *vcmdq)
q->q_base = q->base_dma & VCMDQ_ADDR;
q->q_base |= FIELD_PREP(VCMDQ_LOG2SIZE, q->llq.max_n_shift);
- return arm_smmu_cmdq_init(smmu, cmdq);
+ return arm_smmu_cmdq_init(smmu, cmdq, NULL);
}
/* VINTF Logical VCMDQ Resource Helpers */
--
2.43.0
More information about the linux-arm-kernel
mailing list