From aa4926dd8dda3bd005514ade0622362e34c814fc Mon Sep 17 00:00:00 2001 From: Prakash Gupta Date: Thu, 24 Oct 2019 18:10:23 +0530 Subject: [PATCH 1/2] iommu/arm-smmu: enhance tlb sync timeout handler Enhance TLB sync timeout handler. Parse debug registers to identify TBU/TCU invalidation or SYNC pending state and dump appropriate TBU testbus data accordingly. Example output: arm-smmu 15000000.apps-smmu: TLB sync timed out -- SMMU may be deadlocked TBU ACK 0x3f0000 TBU PWR 0x3f TCU sync_inv 0x1000d5 arm-smmu 15000000.apps-smmu: TCU invalidation completed, TCU sync pending arm-smmu 15000000.apps-smmu: TBU PWR status 0x3f arm-smmu 15000000.apps-smmu: TBU sync ack pending for TBU 150d9000.adsp_tbu, check pending transactions on TBU. Change-Id: If9ca9dcaf26f06ee9b4204771631425cec52f95b Signed-off-by: Prakash Gupta Signed-off-by: Chris Goldsworthy --- drivers/iommu/arm-smmu.c | 106 ++++++++++++++++++++++++++++++++++----- drivers/iommu/arm-smmu.h | 11 ++++ 2 files changed, 104 insertions(+), 13 deletions(-) diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c index 08016ea62bee..d8bfafa99243 100644 --- a/drivers/iommu/arm-smmu.c +++ b/drivers/iommu/arm-smmu.c @@ -196,6 +196,8 @@ static int __arm_smmu_domain_set_attr(struct iommu_domain *domain, static int arm_smmu_domain_get_attr(struct iommu_domain *domain, enum iommu_attr attr, void *data); +static void __arm_smmu_tlb_sync_timeout(struct arm_smmu_device *smmu); + static inline int arm_smmu_rpm_get(struct arm_smmu_device *smmu) { if (pm_runtime_enabled(smmu->dev)) @@ -887,7 +889,6 @@ static int __arm_smmu_tlb_sync(struct arm_smmu_device *smmu, int page, int sync, int status) { unsigned int inc, delay; - u32 sync_inv_ack, tbu_pwr_status = -EINVAL, sync_inv_progress = -EINVAL; u32 reg; arm_smmu_writel(smmu, page, sync, QCOM_DUMMY_VAL); @@ -902,19 +903,8 @@ static int __arm_smmu_tlb_sync(struct arm_smmu_device *smmu, int page, inc *= 2; } - sync_inv_ack = arm_smmu_readl(smmu, - ARM_SMMU_IMPL_DEF0, - ARM_SMMU_STATS_SYNC_INV_TBU_ACK); - qcom_scm_io_readl((unsigned long)(smmu->phys_addr + - ARM_SMMU_TBU_PWR_STATUS), &tbu_pwr_status); - qcom_scm_io_readl((unsigned long)(smmu->phys_addr + - ARM_SMMU_MMU2QSS_AND_SAFE_WAIT_CNTR), - &sync_inv_progress); trace_tlbsync_timeout(smmu->dev, 0); - dev_err_ratelimited(smmu->dev, - "TLB sync timed out -- SMMU may be deadlocked ack 0x%x pwr 0x%x sync and invalidation progress 0x%x\n", - sync_inv_ack, tbu_pwr_status, sync_inv_progress); - BUG_ON(IS_ENABLED(CONFIG_IOMMU_TLBSYNC_DEBUG)); + __arm_smmu_tlb_sync_timeout(smmu); return -EINVAL; } @@ -5004,6 +4994,96 @@ struct qsmmuv500_group_iommudata { ((struct qsmmuv500_group_iommudata *) \ (iommu_group_get_iommudata(group))) +static struct qsmmuv500_tbu_device *qsmmuv500_find_tbu( + struct arm_smmu_device *smmu, u32 sid); + +static void __arm_smmu_tlb_sync_timeout(struct arm_smmu_device *smmu) +{ + u32 sync_inv_ack, tbu_pwr_status, sync_inv_progress; + u32 tbu_inv_pending = 0, tbu_sync_pending = 0; + u32 tbu_inv_acked = 0, tbu_sync_acked = 0; + u32 tcu_inv_pending = 0, tcu_sync_pending = 0; + u32 tbu_ids = 0; + int ret; + + static DEFINE_RATELIMIT_STATE(_rs, + DEFAULT_RATELIMIT_INTERVAL, + DEFAULT_RATELIMIT_BURST); + + dev_err_ratelimited(smmu->dev, + "TLB sync timed out -- SMMU may be deadlocked\n"); + + sync_inv_ack = arm_smmu_readl(smmu, + ARM_SMMU_IMPL_DEF0, + ARM_SMMU_STATS_SYNC_INV_TBU_ACK); + ret = qcom_scm_io_readl((unsigned long)(smmu->phys_addr + + ARM_SMMU_TBU_PWR_STATUS), &tbu_pwr_status); + if (ret) { + dev_err_ratelimited(smmu->dev, + "SCM read of TBU power status fails: %d\n", + ret); + goto out; + } + + ret = qcom_scm_io_readl((unsigned long)(smmu->phys_addr + + ARM_SMMU_MMU2QSS_AND_SAFE_WAIT_CNTR), + &sync_inv_progress); + if (ret) { + dev_err_ratelimited(smmu->dev, + "SCM read of TBU sync/inv prog fails: %d\n", + ret); + goto out; + } + + if (sync_inv_ack) { + tbu_inv_pending = FIELD_GET(TBU_INV_REQ, sync_inv_ack); + tbu_inv_acked = FIELD_GET(TBU_INV_ACK, sync_inv_ack); + tbu_sync_pending = FIELD_GET(TBU_SYNC_REQ, sync_inv_ack); + tbu_sync_acked = FIELD_GET(TBU_SYNC_ACK, sync_inv_ack); + } + + if (tbu_pwr_status) { + if (tbu_sync_pending) + tbu_ids = tbu_pwr_status & ~tbu_sync_acked; + else if (tbu_inv_pending) + tbu_ids = tbu_pwr_status & ~tbu_inv_acked; + } + + tcu_inv_pending = FIELD_GET(TCU_INV_IN_PRGSS, sync_inv_progress); + tcu_sync_pending = FIELD_GET(TCU_SYNC_IN_PRGSS, sync_inv_progress); + + if (__ratelimit(&_rs)) { + unsigned long tbu_id, tbus_t = tbu_ids; + + dev_err(smmu->dev, + "TBU ACK 0x%x TBU PWR 0x%x TCU sync_inv 0x%x\n", + sync_inv_ack, tbu_pwr_status, sync_inv_progress); + dev_err(smmu->dev, + "TCU invalidation %s, TCU sync %s\n", + tcu_inv_pending?"pending":"completed", + tcu_sync_pending?"pending":"completed"); + + while (tbus_t) { + struct qsmmuv500_tbu_device *tbu; + + tbu_id = __ffs(tbus_t); + tbus_t = tbus_t & ~(1 << tbu_id); + tbu = qsmmuv500_find_tbu(smmu, + (u16)(tbu_id << TBUID_SHIFT)); + if (tbu) { + dev_err(smmu->dev, + "TBU %s ack pending for TBU %s, %s\n", + tbu_sync_pending?"sync" : "inv", + dev_name(tbu->dev), + tbu_sync_pending ? + "check pending transactions on TBU" + : "check for TBU power status"); + } + } + } +out: + BUG_ON(IS_ENABLED(CONFIG_IOMMU_TLBSYNC_DEBUG)); +} static bool arm_smmu_fwspec_match_smr(struct iommu_fwspec *fwspec, struct arm_smmu_smr *smr) diff --git a/drivers/iommu/arm-smmu.h b/drivers/iommu/arm-smmu.h index 02ca4e9964d3..755cdc40ce99 100644 --- a/drivers/iommu/arm-smmu.h +++ b/drivers/iommu/arm-smmu.h @@ -229,10 +229,18 @@ enum arm_smmu_cbar_type { /* Implementation Defined Register Space 0 registers*/ /* Relative to IMPL_DEF_0 page */ #define ARM_SMMU_STATS_SYNC_INV_TBU_ACK 0x5dc +#define TBU_SYNC_ACK GENMASK(25, 17) +#define TBU_SYNC_REQ BIT(16) +#define TBU_INV_ACK GENMASK(9, 1) +#define TBU_INV_REQ BIT(0) + /* Relative to SMMU_BASE */ #define ARM_SMMU_TBU_PWR_STATUS 0x2204 + /* Relative SMMU_BASE */ #define ARM_SMMU_MMU2QSS_AND_SAFE_WAIT_CNTR 0x2670 +#define TCU_SYNC_IN_PRGSS BIT(20) +#define TCU_INV_IN_PRGSS BIT(16) #define ARM_SMMU_CB_ATSR 0x8f0 #define ATSR_ACTIVE BIT(0) @@ -503,4 +511,7 @@ static inline void arm_smmu_writeq(struct arm_smmu_device *smmu, int page, struct arm_smmu_device *arm_smmu_impl_init(struct arm_smmu_device *smmu); +/* Misc. constants */ +#define TBUID_SHIFT 10 + #endif /* _ARM_SMMU_H */ From 240d6f5367dd3a7e931408a4bc1eb562c52d85d3 Mon Sep 17 00:00:00 2001 From: Chris Goldsworthy Date: Wed, 15 Jan 2020 02:00:55 -0800 Subject: [PATCH 2/2] iommu/arm-smmu: Replace while loop with for_each_set_bit Switch out a while loop that loops over the set bits in a bitmap with for_each_set_bit. Change-Id: I8843a0b36906979a09122a8ccb07c8081278bf3b Signed-off-by: Chris Goldsworthy --- drivers/iommu/arm-smmu.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c index d8bfafa99243..5424d7a277e0 100644 --- a/drivers/iommu/arm-smmu.c +++ b/drivers/iommu/arm-smmu.c @@ -5053,7 +5053,7 @@ static void __arm_smmu_tlb_sync_timeout(struct arm_smmu_device *smmu) tcu_sync_pending = FIELD_GET(TCU_SYNC_IN_PRGSS, sync_inv_progress); if (__ratelimit(&_rs)) { - unsigned long tbu_id, tbus_t = tbu_ids; + unsigned long tbu_id; dev_err(smmu->dev, "TBU ACK 0x%x TBU PWR 0x%x TCU sync_inv 0x%x\n", @@ -5063,11 +5063,11 @@ static void __arm_smmu_tlb_sync_timeout(struct arm_smmu_device *smmu) tcu_inv_pending?"pending":"completed", tcu_sync_pending?"pending":"completed"); - while (tbus_t) { + for_each_set_bit(tbu_id, (unsigned long *) &tbu_ids, + sizeof(tbu_ids) * BITS_PER_BYTE) { + struct qsmmuv500_tbu_device *tbu; - tbu_id = __ffs(tbus_t); - tbus_t = tbus_t & ~(1 << tbu_id); tbu = qsmmuv500_find_tbu(smmu, (u16)(tbu_id << TBUID_SHIFT)); if (tbu) {