Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit b1f377f8 authored by qctecmdr's avatar qctecmdr Committed by Gerrit - the friendly Code Review server
Browse files

Merge "iommu/arm-smmu: log outstanding transactions on sync timeout"

parents 154900b5 b4a6f88e
Loading
Loading
Loading
Loading
+4 −3
Original line number Diff line number Diff line
@@ -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.

+119 −1
Original line number Diff line number Diff line
@@ -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,6 +5192,10 @@ 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));
}
@@ -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)
+2 −0
Original line number Diff line number Diff line
@@ -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 {