Commit 3b5302cb authored by Jason Gunthorpe's avatar Jason Gunthorpe Committed by Will Deacon
Browse files

iommu/arm-smmu-v3: Test the STE S1DSS functionality



S1DSS brings in quite a few new transition pairs that are
interesting. Test to/from S1DSS_BYPASS <-> S1DSS_SSID0, and
BYPASS <-> S1DSS_SSID0.

Test a contrived non-hitless flow to make sure that the logic works.

Tested-by: default avatarNicolin Chen <nicolinc@nvidia.com>
Signed-off-by: default avatarMichael Shavit <mshavit@google.com>
Reviewed-by: default avatarNicolin Chen <nicolinc@nvidia.com>
Reviewed-by: default avatarJerry Snitselaar <jsnitsel@redhat.com>
Signed-off-by: default avatarJason Gunthorpe <jgg@nvidia.com>
Link: https://lore.kernel.org/r/12-v9-5cd718286059+79186-smmuv3_newapi_p2b_jgg@nvidia.com


Signed-off-by: default avatarWill Deacon <will@kernel.org>
parent ce26ea9e
Loading
Loading
Loading
Loading
+108 −5
Original line number Diff line number Diff line
@@ -144,6 +144,14 @@ static void arm_smmu_v3_test_ste_expect_transition(
	KUNIT_EXPECT_MEMEQ(test, target->data, cur_copy.data, sizeof(cur_copy));
}

static void arm_smmu_v3_test_ste_expect_non_hitless_transition(
	struct kunit *test, const struct arm_smmu_ste *cur,
	const struct arm_smmu_ste *target, unsigned int num_syncs_expected)
{
	arm_smmu_v3_test_ste_expect_transition(test, cur, target,
					       num_syncs_expected, false);
}

static void arm_smmu_v3_test_ste_expect_hitless_transition(
	struct kunit *test, const struct arm_smmu_ste *cur,
	const struct arm_smmu_ste *target, unsigned int num_syncs_expected)
@@ -155,6 +163,7 @@ static void arm_smmu_v3_test_ste_expect_hitless_transition(
static const dma_addr_t fake_cdtab_dma_addr = 0xF0F0F0F0F0F0;

static void arm_smmu_test_make_cdtable_ste(struct arm_smmu_ste *ste,
					   unsigned int s1dss,
					   const dma_addr_t dma_addr)
{
	struct arm_smmu_master master = {
@@ -164,7 +173,7 @@ static void arm_smmu_test_make_cdtable_ste(struct arm_smmu_ste *ste,
		.smmu = &smmu,
	};

	arm_smmu_make_cdtable_ste(ste, &master, true, STRTAB_STE_1_S1DSS_SSID0);
	arm_smmu_make_cdtable_ste(ste, &master, true, s1dss);
}

static void arm_smmu_v3_write_ste_test_bypass_to_abort(struct kunit *test)
@@ -194,7 +203,8 @@ static void arm_smmu_v3_write_ste_test_cdtable_to_abort(struct kunit *test)
{
	struct arm_smmu_ste ste;

	arm_smmu_test_make_cdtable_ste(&ste, fake_cdtab_dma_addr);
	arm_smmu_test_make_cdtable_ste(&ste, STRTAB_STE_1_S1DSS_SSID0,
				       fake_cdtab_dma_addr);
	arm_smmu_v3_test_ste_expect_hitless_transition(test, &ste, &abort_ste,
						       NUM_EXPECTED_SYNCS(2));
}
@@ -203,7 +213,8 @@ static void arm_smmu_v3_write_ste_test_abort_to_cdtable(struct kunit *test)
{
	struct arm_smmu_ste ste;

	arm_smmu_test_make_cdtable_ste(&ste, fake_cdtab_dma_addr);
	arm_smmu_test_make_cdtable_ste(&ste, STRTAB_STE_1_S1DSS_SSID0,
				       fake_cdtab_dma_addr);
	arm_smmu_v3_test_ste_expect_hitless_transition(test, &abort_ste, &ste,
						       NUM_EXPECTED_SYNCS(2));
}
@@ -212,7 +223,8 @@ static void arm_smmu_v3_write_ste_test_cdtable_to_bypass(struct kunit *test)
{
	struct arm_smmu_ste ste;

	arm_smmu_test_make_cdtable_ste(&ste, fake_cdtab_dma_addr);
	arm_smmu_test_make_cdtable_ste(&ste, STRTAB_STE_1_S1DSS_SSID0,
				       fake_cdtab_dma_addr);
	arm_smmu_v3_test_ste_expect_hitless_transition(test, &ste, &bypass_ste,
						       NUM_EXPECTED_SYNCS(3));
}
@@ -221,11 +233,54 @@ static void arm_smmu_v3_write_ste_test_bypass_to_cdtable(struct kunit *test)
{
	struct arm_smmu_ste ste;

	arm_smmu_test_make_cdtable_ste(&ste, fake_cdtab_dma_addr);
	arm_smmu_test_make_cdtable_ste(&ste, STRTAB_STE_1_S1DSS_SSID0,
				       fake_cdtab_dma_addr);
	arm_smmu_v3_test_ste_expect_hitless_transition(test, &bypass_ste, &ste,
						       NUM_EXPECTED_SYNCS(3));
}

static void arm_smmu_v3_write_ste_test_cdtable_s1dss_change(struct kunit *test)
{
	struct arm_smmu_ste ste;
	struct arm_smmu_ste s1dss_bypass;

	arm_smmu_test_make_cdtable_ste(&ste, STRTAB_STE_1_S1DSS_SSID0,
				       fake_cdtab_dma_addr);
	arm_smmu_test_make_cdtable_ste(&s1dss_bypass, STRTAB_STE_1_S1DSS_BYPASS,
				       fake_cdtab_dma_addr);

	/*
	 * Flipping s1dss on a CD table STE only involves changes to the second
	 * qword of an STE and can be done in a single write.
	 */
	arm_smmu_v3_test_ste_expect_hitless_transition(
		test, &ste, &s1dss_bypass, NUM_EXPECTED_SYNCS(1));
	arm_smmu_v3_test_ste_expect_hitless_transition(
		test, &s1dss_bypass, &ste, NUM_EXPECTED_SYNCS(1));
}

static void
arm_smmu_v3_write_ste_test_s1dssbypass_to_stebypass(struct kunit *test)
{
	struct arm_smmu_ste s1dss_bypass;

	arm_smmu_test_make_cdtable_ste(&s1dss_bypass, STRTAB_STE_1_S1DSS_BYPASS,
				       fake_cdtab_dma_addr);
	arm_smmu_v3_test_ste_expect_hitless_transition(
		test, &s1dss_bypass, &bypass_ste, NUM_EXPECTED_SYNCS(2));
}

static void
arm_smmu_v3_write_ste_test_stebypass_to_s1dssbypass(struct kunit *test)
{
	struct arm_smmu_ste s1dss_bypass;

	arm_smmu_test_make_cdtable_ste(&s1dss_bypass, STRTAB_STE_1_S1DSS_BYPASS,
				       fake_cdtab_dma_addr);
	arm_smmu_v3_test_ste_expect_hitless_transition(
		test, &bypass_ste, &s1dss_bypass, NUM_EXPECTED_SYNCS(2));
}

static void arm_smmu_test_make_s2_ste(struct arm_smmu_ste *ste,
				      bool ats_enabled)
{
@@ -285,6 +340,48 @@ static void arm_smmu_v3_write_ste_test_bypass_to_s2(struct kunit *test)
						       NUM_EXPECTED_SYNCS(2));
}

static void arm_smmu_v3_write_ste_test_s1_to_s2(struct kunit *test)
{
	struct arm_smmu_ste s1_ste;
	struct arm_smmu_ste s2_ste;

	arm_smmu_test_make_cdtable_ste(&s1_ste, STRTAB_STE_1_S1DSS_SSID0,
				       fake_cdtab_dma_addr);
	arm_smmu_test_make_s2_ste(&s2_ste, true);
	arm_smmu_v3_test_ste_expect_hitless_transition(test, &s1_ste, &s2_ste,
						       NUM_EXPECTED_SYNCS(3));
}

static void arm_smmu_v3_write_ste_test_s2_to_s1(struct kunit *test)
{
	struct arm_smmu_ste s1_ste;
	struct arm_smmu_ste s2_ste;

	arm_smmu_test_make_cdtable_ste(&s1_ste, STRTAB_STE_1_S1DSS_SSID0,
				       fake_cdtab_dma_addr);
	arm_smmu_test_make_s2_ste(&s2_ste, true);
	arm_smmu_v3_test_ste_expect_hitless_transition(test, &s2_ste, &s1_ste,
						       NUM_EXPECTED_SYNCS(3));
}

static void arm_smmu_v3_write_ste_test_non_hitless(struct kunit *test)
{
	struct arm_smmu_ste ste;
	struct arm_smmu_ste ste_2;

	/*
	 * Although no flow resembles this in practice, one way to force an STE
	 * update to be non-hitless is to change its CD table pointer as well as
	 * s1 dss field in the same update.
	 */
	arm_smmu_test_make_cdtable_ste(&ste, STRTAB_STE_1_S1DSS_SSID0,
				       fake_cdtab_dma_addr);
	arm_smmu_test_make_cdtable_ste(&ste_2, STRTAB_STE_1_S1DSS_BYPASS,
				       0x4B4B4b4B4B);
	arm_smmu_v3_test_ste_expect_non_hitless_transition(
		test, &ste, &ste_2, NUM_EXPECTED_SYNCS(3));
}

static void arm_smmu_v3_test_cd_expect_transition(
	struct kunit *test, const struct arm_smmu_cd *cur,
	const struct arm_smmu_cd *target, unsigned int num_syncs_expected,
@@ -438,10 +535,16 @@ static struct kunit_case arm_smmu_v3_test_cases[] = {
	KUNIT_CASE(arm_smmu_v3_write_ste_test_abort_to_cdtable),
	KUNIT_CASE(arm_smmu_v3_write_ste_test_cdtable_to_bypass),
	KUNIT_CASE(arm_smmu_v3_write_ste_test_bypass_to_cdtable),
	KUNIT_CASE(arm_smmu_v3_write_ste_test_cdtable_s1dss_change),
	KUNIT_CASE(arm_smmu_v3_write_ste_test_s1dssbypass_to_stebypass),
	KUNIT_CASE(arm_smmu_v3_write_ste_test_stebypass_to_s1dssbypass),
	KUNIT_CASE(arm_smmu_v3_write_ste_test_s2_to_abort),
	KUNIT_CASE(arm_smmu_v3_write_ste_test_abort_to_s2),
	KUNIT_CASE(arm_smmu_v3_write_ste_test_s2_to_bypass),
	KUNIT_CASE(arm_smmu_v3_write_ste_test_bypass_to_s2),
	KUNIT_CASE(arm_smmu_v3_write_ste_test_s1_to_s2),
	KUNIT_CASE(arm_smmu_v3_write_ste_test_s2_to_s1),
	KUNIT_CASE(arm_smmu_v3_write_ste_test_non_hitless),
	KUNIT_CASE(arm_smmu_v3_write_cd_test_s1_clear),
	KUNIT_CASE(arm_smmu_v3_write_cd_test_s1_change_asid),
	KUNIT_CASE(arm_smmu_v3_write_cd_test_sva_clear),