mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-11 07:03:09 -04:00
iommu/arm-smmu: log outstanding transactions on sync timeout
On a TLB sync timeout for a given SMMU, dump the outstanding transaction bitmap for each of its TBUs. Additionally, introduce a sync_timed_out flag for each SMMU to be present with the enabling of CONFIG_IOMMU_DEBUG to (1) ensure that only one thread performs the relevant logging and BUG_ON() during a sync timeout, and (2) prevent other incoming threads from triggering further timeouts, which can overwrite the SMMU state data that would contain information on the initial sync timeout. Change-Id: I43855ecc51ddd2f3a1a287a39c123784c54ad538 Signed-off-by: Chris Goldsworthy <cgoldswo@codeaurora.org>
This commit is contained in:
parent
187d2284fd
commit
b4a6f88e58
3 changed files with 125 additions and 4 deletions
|
|
@ -477,9 +477,10 @@ config IOMMU_TLBSYNC_DEBUG
|
|||
bool "TLB sync timeout debug"
|
||||
depends on ARM_SMMU
|
||||
help
|
||||
Enables to collect the SMMU system state information right
|
||||
after the first TLB sync timeout failure by calling BUG().
|
||||
Note to use this only on debug builds.
|
||||
Collects the SMMU system state information after the
|
||||
first TLB sync timeout failure by calling BUG() from
|
||||
a workqueue thread. Note to use this only on debug
|
||||
builds.
|
||||
|
||||
If unsure, say N here.
|
||||
|
||||
|
|
|
|||
|
|
@ -44,6 +44,7 @@
|
|||
#include <linux/of_platform.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/wait.h>
|
||||
#include <linux/workqueue.h>
|
||||
|
||||
#include <linux/amba/bus.h>
|
||||
#include <linux/fsl/mc.h>
|
||||
|
|
@ -891,6 +892,10 @@ static int __arm_smmu_tlb_sync(struct arm_smmu_device *smmu, int page,
|
|||
unsigned int inc, delay;
|
||||
u32 reg;
|
||||
|
||||
if (IS_ENABLED(CONFIG_IOMMU_TLBSYNC_DEBUG) &&
|
||||
test_bit(0, &smmu->sync_timed_out))
|
||||
return -EINVAL;
|
||||
|
||||
arm_smmu_writel(smmu, page, sync, QCOM_DUMMY_VAL);
|
||||
for (delay = 1, inc = 1; delay < TLB_LOOP_TIMEOUT; delay += inc) {
|
||||
reg = arm_smmu_readl(smmu, page, status);
|
||||
|
|
@ -903,8 +908,14 @@ static int __arm_smmu_tlb_sync(struct arm_smmu_device *smmu, int page,
|
|||
inc *= 2;
|
||||
}
|
||||
|
||||
if (IS_ENABLED(CONFIG_IOMMU_TLBSYNC_DEBUG) &&
|
||||
test_and_set_bit_lock(0, &smmu->sync_timed_out))
|
||||
goto out;
|
||||
|
||||
trace_tlbsync_timeout(smmu->dev, 0);
|
||||
__arm_smmu_tlb_sync_timeout(smmu);
|
||||
|
||||
out:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
|
|
@ -4823,6 +4834,9 @@ 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);
|
||||
|
||||
idr_destroy(&smmu->asid_idr);
|
||||
|
||||
/* Turn the thing off */
|
||||
|
|
@ -4955,6 +4969,17 @@ module_exit(arm_smmu_exit);
|
|||
|
||||
#define TBU_DBG_TIMEOUT_US 100
|
||||
|
||||
#define TNX_TCR_CNTL 0x130
|
||||
#define TNX_TCR_CNTL_TBU_OT_CAPTURE_EN BIT(18)
|
||||
#define TNX_TCR_CNTL_ALWAYS_CAPTURE BIT(15)
|
||||
#define TNX_TCR_CNTL_MATCH_MASK_UPD BIT(7)
|
||||
#define TNX_TCR_CNTL_MATCH_MASK_VALID BIT(6)
|
||||
|
||||
#define CAPTURE1_SNAPSHOT_1 0x138
|
||||
|
||||
#define TNX_TCR_CNTL_2 0x178
|
||||
#define TNX_TCR_CNTL_2_CAP1_VALID BIT(0)
|
||||
|
||||
struct actlr_setting {
|
||||
struct arm_smmu_smr smr;
|
||||
u32 actlr;
|
||||
|
|
@ -4997,6 +5022,92 @@ struct qsmmuv500_group_iommudata {
|
|||
static struct qsmmuv500_tbu_device *qsmmuv500_find_tbu(
|
||||
struct arm_smmu_device *smmu, u32 sid);
|
||||
|
||||
/*
|
||||
* Provides mutually exclusive access to the registers used by the
|
||||
* outstanding transaction snapshot feature and the transaction
|
||||
* snapshot capture feature.
|
||||
*/
|
||||
static DEFINE_MUTEX(capture_reg_lock);
|
||||
|
||||
static void arm_smmu_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);
|
||||
void __iomem *base;
|
||||
|
||||
if (!mutex_trylock(&capture_reg_lock)) {
|
||||
dev_warn_ratelimited(smmu->dev,
|
||||
"Tnx snapshot regs in use, not dumping OT tnxs.\n");
|
||||
goto bug;
|
||||
}
|
||||
|
||||
if (arm_smmu_power_on(smmu->pwr)) {
|
||||
dev_err_ratelimited(smmu->dev,
|
||||
"%s: Failed to power on SMMU.\n",
|
||||
__func__);
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
list_for_each_entry(tbu, &data->tbus, list) {
|
||||
if (arm_smmu_power_on(tbu->pwr)) {
|
||||
dev_err_ratelimited(tbu->dev,
|
||||
"%s: Failed to power on TBU.\n",
|
||||
__func__);
|
||||
continue;
|
||||
}
|
||||
base = tbu->base;
|
||||
|
||||
tcr_cntl_val = readq_relaxed(base + TNX_TCR_CNTL);
|
||||
|
||||
/* Write 1 into MATCH_MASK_UPD of TNX_TCR_CNTL */
|
||||
writeq_relaxed(tcr_cntl_val | TNX_TCR_CNTL_MATCH_MASK_UPD,
|
||||
base + TNX_TCR_CNTL);
|
||||
|
||||
/*
|
||||
* Simultaneously write 0 into MATCH_MASK_UPD, 0 into
|
||||
* ALWAYS_CAPTURE, 0 into MATCH_MASK_VALID, and 1 into
|
||||
* TBU_OT_CAPTURE_EN of TNX_TCR_CNTL
|
||||
*/
|
||||
tcr_cntl_val &= ~(TNX_TCR_CNTL_MATCH_MASK_UPD |
|
||||
TNX_TCR_CNTL_ALWAYS_CAPTURE |
|
||||
TNX_TCR_CNTL_MATCH_MASK_VALID);
|
||||
writeq_relaxed(tcr_cntl_val | TNX_TCR_CNTL_TBU_OT_CAPTURE_EN,
|
||||
base + TNX_TCR_CNTL);
|
||||
|
||||
/* Poll for CAPTURE1_VALID to become 1 on TNX_TCR_CNTL_2 */
|
||||
if (readq_poll_timeout_atomic(base + TNX_TCR_CNTL_2, res,
|
||||
res & TNX_TCR_CNTL_2_CAP1_VALID,
|
||||
0, TBU_DBG_TIMEOUT_US)) {
|
||||
dev_err_ratelimited(tbu->dev,
|
||||
"Timeout on TNX snapshot poll\n");
|
||||
goto poll_timeout;
|
||||
}
|
||||
|
||||
/* Read Register CAPTURE1_SNAPSHOT_1 */
|
||||
outstanding_tnxs = readq_relaxed(base + CAPTURE1_SNAPSHOT_1);
|
||||
dev_err_ratelimited(tbu->dev,
|
||||
"Outstanding Transaction Bitmap: 0x%llx\n",
|
||||
outstanding_tnxs);
|
||||
poll_timeout:
|
||||
/* Write TBU_OT_CAPTURE_EN to 0 of TNX_TCR_CNTL */
|
||||
writeq_relaxed(tcr_cntl_val & ~TNX_TCR_CNTL_TBU_OT_CAPTURE_EN,
|
||||
tbu->base + TNX_TCR_CNTL);
|
||||
|
||||
arm_smmu_power_off(smmu, tbu->pwr);
|
||||
}
|
||||
|
||||
arm_smmu_power_off(smmu, smmu->pwr);
|
||||
unlock:
|
||||
mutex_unlock(&capture_reg_lock);
|
||||
bug:
|
||||
BUG_ON(IS_ENABLED(CONFIG_IOMMU_TLBSYNC_DEBUG));
|
||||
}
|
||||
|
||||
static void __arm_smmu_tlb_sync_timeout(struct arm_smmu_device *smmu)
|
||||
{
|
||||
u32 sync_inv_ack, tbu_pwr_status, sync_inv_progress;
|
||||
|
|
@ -5081,8 +5192,12 @@ static void __arm_smmu_tlb_sync_timeout(struct arm_smmu_device *smmu)
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (tcu_sync_pending)
|
||||
schedule_work(&smmu->outstanding_tnx_work);
|
||||
else
|
||||
out:
|
||||
BUG_ON(IS_ENABLED(CONFIG_IOMMU_TLBSYNC_DEBUG));
|
||||
BUG_ON(IS_ENABLED(CONFIG_IOMMU_TLBSYNC_DEBUG));
|
||||
}
|
||||
|
||||
static bool arm_smmu_fwspec_match_smr(struct iommu_fwspec *fwspec,
|
||||
|
|
@ -5613,6 +5728,9 @@ 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);
|
||||
|
||||
/* Attempt to register child devices */
|
||||
ret = device_for_each_child(dev, smmu, qsmmuv500_tbu_register);
|
||||
if (ret)
|
||||
|
|
|
|||
|
|
@ -375,6 +375,8 @@ struct arm_smmu_device {
|
|||
|
||||
struct arm_smmu_arch_ops *arch_ops;
|
||||
void *archdata;
|
||||
struct work_struct outstanding_tnx_work;
|
||||
unsigned long sync_timed_out;
|
||||
};
|
||||
|
||||
enum arm_smmu_context_fmt {
|
||||
|
|
|
|||
Loading…
Reference in a new issue