Skip to content

Commit 15a2a56

Browse files
jgunthorpewilldeacon
authored andcommitted
iommu/arm-smmu-v3: Introduce a per-domain arm_smmu_invs array
Create a new data structure to hold an array of invalidations that need to be performed for the domain based on what masters are attached, to replace the single smmu pointer and linked list of masters in the current design. Each array entry holds one of the invalidation actions - S1_ASID, S2_VMID, ATS or their variant with information to feed invalidation commands to HW. It is structured so that multiple SMMUs can participate in the same array, removing one key limitation of the current system. To maximize performance, a sorted array is used as the data structure. It allows grouping SYNCs together to parallelize invalidations. For instance, it will group all the ATS entries after the ASID/VMID entry, so they will all be pushed to the PCI devices in parallel with one SYNC. To minimize the locking cost on the invalidation fast path (reader of the invalidation array), the array is managed with RCU. Provide a set of APIs to add/delete entries to/from an array, which cover cannot-fail attach cases, e.g. attaching to arm_smmu_blocked_domain. Also add kunit coverage for those APIs. Signed-off-by: Jason Gunthorpe <jgg@nvidia.com> Reviewed-by: Jason Gunthorpe <jgg@nvidia.com> Reviewed-by: Pranjal Shrivastava <praan@google.com> Co-developed-by: Nicolin Chen <nicolinc@nvidia.com> Signed-off-by: Nicolin Chen <nicolinc@nvidia.com> Signed-off-by: Will Deacon <will@kernel.org>
1 parent c317452 commit 15a2a56

3 files changed

Lines changed: 502 additions & 0 deletions

File tree

drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-test.c

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -637,6 +637,140 @@ static void arm_smmu_v3_write_cd_test_sva_release(struct kunit *test)
637637
NUM_EXPECTED_SYNCS(2));
638638
}
639639

640+
static void arm_smmu_v3_invs_test_verify(struct kunit *test,
641+
struct arm_smmu_invs *invs,
642+
int num_invs, const int num_trashes,
643+
const int *ids, const int *users,
644+
const int *ssids)
645+
{
646+
KUNIT_EXPECT_EQ(test, invs->num_invs, num_invs);
647+
KUNIT_EXPECT_EQ(test, invs->num_trashes, num_trashes);
648+
while (num_invs--) {
649+
KUNIT_EXPECT_EQ(test, invs->inv[num_invs].id, ids[num_invs]);
650+
KUNIT_EXPECT_EQ(test, READ_ONCE(invs->inv[num_invs].users),
651+
users[num_invs]);
652+
KUNIT_EXPECT_EQ(test, invs->inv[num_invs].ssid, ssids[num_invs]);
653+
}
654+
}
655+
656+
static struct arm_smmu_invs invs1 = {
657+
.num_invs = 3,
658+
.inv = { { .type = INV_TYPE_S2_VMID, .id = 1, },
659+
{ .type = INV_TYPE_S2_VMID_S1_CLEAR, .id = 1, },
660+
{ .type = INV_TYPE_ATS, .id = 3, }, },
661+
};
662+
663+
static struct arm_smmu_invs invs2 = {
664+
.num_invs = 3,
665+
.inv = { { .type = INV_TYPE_S2_VMID, .id = 1, }, /* duplicated */
666+
{ .type = INV_TYPE_ATS, .id = 4, },
667+
{ .type = INV_TYPE_ATS, .id = 5, }, },
668+
};
669+
670+
static struct arm_smmu_invs invs3 = {
671+
.num_invs = 3,
672+
.inv = { { .type = INV_TYPE_S2_VMID, .id = 1, }, /* duplicated */
673+
{ .type = INV_TYPE_ATS, .id = 5, }, /* recover a trash */
674+
{ .type = INV_TYPE_ATS, .id = 6, }, },
675+
};
676+
677+
static struct arm_smmu_invs invs4 = {
678+
.num_invs = 3,
679+
.inv = { { .type = INV_TYPE_ATS, .id = 10, .ssid = 1 },
680+
{ .type = INV_TYPE_ATS, .id = 10, .ssid = 3 },
681+
{ .type = INV_TYPE_ATS, .id = 12, .ssid = 1 }, },
682+
};
683+
684+
static struct arm_smmu_invs invs5 = {
685+
.num_invs = 3,
686+
.inv = { { .type = INV_TYPE_ATS, .id = 10, .ssid = 2 },
687+
{ .type = INV_TYPE_ATS, .id = 10, .ssid = 3 }, /* duplicate */
688+
{ .type = INV_TYPE_ATS, .id = 12, .ssid = 2 }, },
689+
};
690+
691+
static void arm_smmu_v3_invs_test(struct kunit *test)
692+
{
693+
const int results1[3][3] = { { 1, 1, 3, }, { 1, 1, 1, }, { 0, 0, 0, } };
694+
const int results2[3][5] = { { 1, 1, 3, 4, 5, }, { 2, 1, 1, 1, 1, }, { 0, 0, 0, 0, 0, } };
695+
const int results3[3][3] = { { 1, 1, 3, }, { 1, 1, 1, }, { 0, 0, 0, } };
696+
const int results4[3][5] = { { 1, 1, 3, 5, 6, }, { 2, 1, 1, 1, 1, }, { 0, 0, 0, 0, 0, } };
697+
const int results5[3][5] = { { 1, 1, 3, 5, 6, }, { 1, 0, 0, 1, 1, }, { 0, 0, 0, 0, 0, } };
698+
const int results6[3][3] = { { 1, 5, 6, }, { 1, 1, 1, }, { 0, 0, 0, } };
699+
const int results7[3][3] = { { 10, 10, 12, }, { 1, 1, 1, }, { 1, 3, 1, } };
700+
const int results8[3][5] = { { 10, 10, 10, 12, 12, }, { 1, 1, 2, 1, 1, }, { 1, 2, 3, 1, 2, } };
701+
const int results9[3][4] = { { 10, 10, 10, 12, }, { 1, 0, 1, 1, }, { 1, 2, 3, 1, } };
702+
const int results10[3][3] = { { 10, 10, 12, }, { 1, 1, 1, }, { 1, 3, 1, } };
703+
struct arm_smmu_invs *test_a, *test_b;
704+
705+
/* New array */
706+
test_a = arm_smmu_invs_alloc(0);
707+
KUNIT_EXPECT_EQ(test, test_a->num_invs, 0);
708+
709+
/* Test1: merge invs1 (new array) */
710+
test_b = arm_smmu_invs_merge(test_a, &invs1);
711+
kfree(test_a);
712+
arm_smmu_v3_invs_test_verify(test, test_b, ARRAY_SIZE(results1[0]), 0,
713+
results1[0], results1[1], results1[2]);
714+
715+
/* Test2: merge invs2 (new array) */
716+
test_a = arm_smmu_invs_merge(test_b, &invs2);
717+
kfree(test_b);
718+
arm_smmu_v3_invs_test_verify(test, test_a, ARRAY_SIZE(results2[0]), 0,
719+
results2[0], results2[1], results2[2]);
720+
721+
/* Test3: unref invs2 (same array) */
722+
arm_smmu_invs_unref(test_a, &invs2);
723+
arm_smmu_v3_invs_test_verify(test, test_a, ARRAY_SIZE(results3[0]), 0,
724+
results3[0], results3[1], results3[2]);
725+
726+
/* Test4: merge invs3 (new array) */
727+
test_b = arm_smmu_invs_merge(test_a, &invs3);
728+
kfree(test_a);
729+
arm_smmu_v3_invs_test_verify(test, test_b, ARRAY_SIZE(results4[0]), 0,
730+
results4[0], results4[1], results4[2]);
731+
732+
/* Test5: unref invs1 (same array) */
733+
arm_smmu_invs_unref(test_b, &invs1);
734+
arm_smmu_v3_invs_test_verify(test, test_b, ARRAY_SIZE(results5[0]), 2,
735+
results5[0], results5[1], results5[2]);
736+
737+
/* Test6: purge test_b (new array) */
738+
test_a = arm_smmu_invs_purge(test_b);
739+
kfree(test_b);
740+
arm_smmu_v3_invs_test_verify(test, test_a, ARRAY_SIZE(results6[0]), 0,
741+
results6[0], results6[1], results6[2]);
742+
743+
/* Test7: unref invs3 (same array) */
744+
arm_smmu_invs_unref(test_a, &invs3);
745+
KUNIT_EXPECT_EQ(test, test_a->num_invs, 0);
746+
KUNIT_EXPECT_EQ(test, test_a->num_trashes, 0);
747+
748+
/* Test8: merge invs4 (new array) */
749+
test_b = arm_smmu_invs_merge(test_a, &invs4);
750+
kfree(test_a);
751+
arm_smmu_v3_invs_test_verify(test, test_b, ARRAY_SIZE(results7[0]), 0,
752+
results7[0], results7[1], results7[2]);
753+
754+
/* Test9: merge invs5 (new array) */
755+
test_a = arm_smmu_invs_merge(test_b, &invs5);
756+
kfree(test_b);
757+
arm_smmu_v3_invs_test_verify(test, test_a, ARRAY_SIZE(results8[0]), 0,
758+
results8[0], results8[1], results8[2]);
759+
760+
/* Test10: unref invs5 (same array) */
761+
arm_smmu_invs_unref(test_a, &invs5);
762+
arm_smmu_v3_invs_test_verify(test, test_a, ARRAY_SIZE(results9[0]), 1,
763+
results9[0], results9[1], results9[2]);
764+
765+
/* Test11: purge test_a (new array) */
766+
test_b = arm_smmu_invs_purge(test_a);
767+
kfree(test_a);
768+
arm_smmu_v3_invs_test_verify(test, test_b, ARRAY_SIZE(results10[0]), 0,
769+
results10[0], results10[1], results10[2]);
770+
771+
kfree(test_b);
772+
}
773+
640774
static struct kunit_case arm_smmu_v3_test_cases[] = {
641775
KUNIT_CASE(arm_smmu_v3_write_ste_test_bypass_to_abort),
642776
KUNIT_CASE(arm_smmu_v3_write_ste_test_abort_to_bypass),
@@ -662,6 +796,7 @@ static struct kunit_case arm_smmu_v3_test_cases[] = {
662796
KUNIT_CASE(arm_smmu_v3_write_ste_test_nested_s1bypass_to_s1dssbypass),
663797
KUNIT_CASE(arm_smmu_v3_write_cd_test_sva_clear),
664798
KUNIT_CASE(arm_smmu_v3_write_cd_test_sva_release),
799+
KUNIT_CASE(arm_smmu_v3_invs_test),
665800
{},
666801
};
667802

0 commit comments

Comments
 (0)