iommu/arm-smmu: Rationalize TLB sync timeout handling

The current TLB sync timeout handling doesn't make sense
in the context of non QSMMU-V500 SMMU V2 implementations, so
introduce the notion of architecture specific handling
for TLB sync timeouts.

Change-Id: Ibceb5422d6488f9e67cc4c4ed968348ad92aa834
Signed-off-by: Isaac J. Manjarres <isaacm@codeaurora.org>
This commit is contained in:
Isaac J. Manjarres 2020-02-20 16:14:39 -08:00
commit 90c4319e85
2 changed files with 59 additions and 14 deletions

View file

@ -199,8 +199,6 @@ 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))
@ -405,6 +403,14 @@ static void arm_smmu_interrupt_selftest(struct arm_smmu_device *smmu)
*
* device_group()
* Hook for checking whether a device is compatible with a said group.
*
* tlb_sync_timeout()
* Hook for performing architecture-specific procedures to collect additional
* debugging information on a TLB sync timeout.
*
* device_remove()
* Hook for performing architecture-specific procedures prior to powering off
* the SMMU.
*/
struct arm_smmu_arch_ops {
int (*init)(struct arm_smmu_device *smmu);
@ -414,6 +420,8 @@ struct arm_smmu_arch_ops {
void (*init_context_bank)(struct arm_smmu_domain *smmu_domain,
struct device *dev);
int (*device_group)(struct device *dev, struct iommu_group *group);
void (*tlb_sync_timeout)(struct arm_smmu_device *smmu);
void (*device_remove)(struct arm_smmu_device *smmu);
};
static int arm_smmu_arch_init(struct arm_smmu_device *smmu)
@ -459,6 +467,24 @@ static int arm_smmu_arch_device_group(struct device *dev,
return smmu->arch_ops->device_group(dev, group);
}
static void arm_smmu_arch_tlb_sync_timeout(struct arm_smmu_device *smmu)
{
if (!smmu->arch_ops || !smmu->arch_ops->tlb_sync_timeout)
return;
smmu->arch_ops->tlb_sync_timeout(smmu);
}
static void arm_smmu_arch_device_remove(struct arm_smmu_device *smmu)
{
if (!smmu->arch_ops)
return;
if (!smmu->arch_ops->device_remove)
return;
return smmu->arch_ops->device_remove(smmu);
}
static void arm_smmu_arch_write_sync(struct arm_smmu_device *smmu)
{
u32 id;
@ -894,6 +920,13 @@ static int __arm_smmu_tlb_sync(struct arm_smmu_device *smmu, int page,
unsigned int inc, delay;
u32 reg;
/*
* Allowing an unbounded number of sync requests to be submitted when a
* TBU is not processing sync requests can cause a TBU's command queue
* to fill up. Once the queue is full, subsequent sync requests can
* stall the CPU indefinitely. Avoid this by gating subsequent sync
* requests after the first sync timeout on an SMMU.
*/
if (IS_ENABLED(CONFIG_IOMMU_TLBSYNC_DEBUG) &&
test_bit(0, &smmu->sync_timed_out))
return -EINVAL;
@ -915,7 +948,7 @@ static int __arm_smmu_tlb_sync(struct arm_smmu_device *smmu, int page,
goto out;
trace_tlbsync_timeout(smmu->dev, 0);
__arm_smmu_tlb_sync_timeout(smmu);
arm_smmu_arch_tlb_sync_timeout(smmu);
out:
return -EINVAL;
@ -4853,7 +4886,7 @@ static int arm_smmu_device_remove(struct platform_device *pdev)
arm_smmu_bus_init(NULL);
iommu_device_unregister(&smmu->iommu);
cancel_work_sync(&smmu->outstanding_tnx_work);
arm_smmu_arch_device_remove(smmu);
idr_destroy(&smmu->asid_idr);
@ -5009,6 +5042,8 @@ struct qsmmuv500_archdata {
u32 version;
struct actlr_setting *actlrs;
u32 actlr_tbl_size;
struct work_struct outstanding_tnx_work;
struct arm_smmu_device *smmu;
};
#define get_qsmmuv500_archdata(smmu) \
((struct qsmmuv500_archdata *)(smmu->archdata))
@ -5047,15 +5082,15 @@ static struct qsmmuv500_tbu_device *qsmmuv500_find_tbu(
*/
static DEFINE_MUTEX(capture_reg_lock);
static void arm_smmu_log_outstanding_transactions(struct work_struct *work)
static void qsmmuv500_log_outstanding_transactions(struct work_struct *work)
{
struct qsmmuv500_tbu_device *tbu = NULL;
u64 outstanding_tnxs;
u64 tcr_cntl_val, res;
struct arm_smmu_device *smmu = container_of(work,
struct arm_smmu_device,
outstanding_tnx_work);
struct qsmmuv500_archdata *data = get_qsmmuv500_archdata(smmu);
struct qsmmuv500_archdata *data = container_of(work,
struct qsmmuv500_archdata,
outstanding_tnx_work);
struct arm_smmu_device *smmu = data->smmu;
void __iomem *base;
if (!mutex_trylock(&capture_reg_lock)) {
@ -5126,13 +5161,14 @@ bug:
BUG_ON(IS_ENABLED(CONFIG_IOMMU_TLBSYNC_DEBUG));
}
static void __arm_smmu_tlb_sync_timeout(struct arm_smmu_device *smmu)
static void qsmmuv500_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;
struct qsmmuv500_archdata *data = get_qsmmuv500_archdata(smmu);
int ret;
static DEFINE_RATELIMIT_STATE(_rs,
@ -5212,12 +5248,19 @@ static void __arm_smmu_tlb_sync_timeout(struct arm_smmu_device *smmu)
}
if (tcu_sync_pending)
schedule_work(&smmu->outstanding_tnx_work);
schedule_work(&data->outstanding_tnx_work);
else
out:
BUG_ON(IS_ENABLED(CONFIG_IOMMU_TLBSYNC_DEBUG));
}
static void qsmmuv500_device_remove(struct arm_smmu_device *smmu)
{
struct qsmmuv500_archdata *data = get_qsmmuv500_archdata(smmu);
cancel_work_sync(&data->outstanding_tnx_work);
}
static bool arm_smmu_fwspec_match_smr(struct iommu_fwspec *fwspec,
struct arm_smmu_smr *smr)
{
@ -5726,6 +5769,7 @@ static int qsmmuv500_arch_init(struct arm_smmu_device *smmu)
data->version = readl_relaxed(data->tcu_base + TCU_HW_VERSION_HLOS1);
smmu->archdata = data;
data->smmu = smmu;
ret = qsmmuv500_read_actlr_tbl(smmu);
if (ret)
@ -5746,8 +5790,8 @@ static int qsmmuv500_arch_init(struct arm_smmu_device *smmu)
if (ret)
return ret;
INIT_WORK(&smmu->outstanding_tnx_work,
arm_smmu_log_outstanding_transactions);
INIT_WORK(&data->outstanding_tnx_work,
qsmmuv500_log_outstanding_transactions);
/* Attempt to register child devices */
ret = device_for_each_child(dev, smmu, qsmmuv500_tbu_register);
@ -5762,6 +5806,8 @@ static struct arm_smmu_arch_ops qsmmuv500_arch_ops = {
.iova_to_phys_hard = qsmmuv500_iova_to_phys_hard,
.init_context_bank = qsmmuv500_init_cb,
.device_group = qsmmuv500_device_group,
.tlb_sync_timeout = qsmmuv500_tlb_sync_timeout,
.device_remove = qsmmuv500_device_remove,
};
static const struct of_device_id qsmmuv500_tbu_of_match[] = {

View file

@ -375,7 +375,6 @@ struct arm_smmu_device {
struct arm_smmu_arch_ops *arch_ops;
void *archdata;
struct work_struct outstanding_tnx_work;
unsigned long sync_timed_out;
};