[PATCH v9 02/12] iommu/arm-smmu-v3: Add a helper to drain cmd queues
Pranjal Shrivastava <[email protected]> Tue, 28 Jul 2026 21:09:18 +0000
| Newsgroups | dev.linux.lists.iommu,org.infradead.lists.linux-arm-kernel |
|---|---|
| Message-ID | <[email protected]> |
Before we suspend SMMU, we want to ensure that all commands (especially ATC_INV) have been flushed by the CMDQ, i.e. the CMDQs are empty. Add a helper function that polls till the queues are dained. Reviewed-by: Nicolin Chen <[email protected]> Signed-off-by: Pranjal Shrivastava <[email protected]> --- drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c | 45 +++++++++++++++++++++ drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h | 3 ++ 2 files changed, 48 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 a11f5bcbe77d..08b83f211ab3 100644 --- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c +++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c @@ -949,6 +949,51 @@ static int arm_smmu_cmdq_batch_submit(struct arm_smmu_device *smmu, cmds->num, true); } +/* + * Currently, only used for CMDQ. + * TODO: Update to handle PRIQ & EVTQ when PRI support is added + */ +int arm_smmu_queue_poll_until_empty(struct arm_smmu_device *smmu, + struct arm_smmu_queue *q) +{ + struct arm_smmu_queue_poll qp; + struct arm_smmu_ll_queue *llq = &q->llq; + int ret = 0; + + queue_poll_init(smmu, &qp); + + /* + * The events are only generated when CMDQ becomes non-full. + * Thus, wfe can't be used here, mark it false explicitly. + */ + qp.wfe = false; + + do { + WRITE_ONCE(llq->cons, readl_relaxed(q->cons_reg)); + if (queue_empty(llq)) + break; + + ret = queue_poll(&qp); + + } while (!ret); + + return ret; +} + +static int arm_smmu_drain_queues(struct arm_smmu_device *smmu) +{ + int ret; + + /* + * We skip the cmdq locking here since this helper is only called + * from contexts (like suspend) where the caller has already + * ensured that new command submissions are fully closed. + */ + ret = arm_smmu_queue_poll_until_empty(smmu, &smmu->cmdq.q); + + return ret; +} + static void arm_smmu_page_response(struct device *dev, struct iopf_fault *unused, struct iommu_page_response *resp) { 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 43f4d24e7847..00ad2e0c257b 100644 --- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h +++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h @@ -1167,6 +1167,9 @@ int arm_smmu_init_one_queue(struct arm_smmu_device *smmu, int arm_smmu_cmdq_init(struct arm_smmu_device *smmu, struct arm_smmu_cmdq *cmdq); +int arm_smmu_queue_poll_until_empty(struct arm_smmu_device *smmu, + struct arm_smmu_queue *q); + static inline bool arm_smmu_master_canwbs(struct arm_smmu_master *master) { return dev_iommu_fwspec_get(master->dev)->flags & -- 2.55.0.487.gaf234c4eb3-goog