Skip to content

Commit 2781f2a

Browse files
jgunthorpewilldeacon
authored andcommitted
iommu/arm-smmu-v3: Add update_safe bits to fix STE update sequence
C_BAD_STE was observed when updating nested STE from an S1-bypass mode to an S1DSS-bypass mode. As both modes enabled S2, the used bit is slightly different than the normal S1-bypass and S1DSS-bypass modes. As a result, fields like MEV and EATS in S2's used list marked the word1 as a critical word that requested a STE.V=0. This breaks a hitless update. However, both MEV and EATS aren't critical in terms of STE update. One controls the merge of the events and the other controls the ATS that is managed by the driver at the same time via pci_enable_ats(). Add an arm_smmu_get_ste_update_safe() to allow STE update algorithm to relax those fields, avoiding the STE update breakages. After this change, entry_set has no caller checking its return value, so change it to void. Note that this change is required by both MEV and EATS fields, which were introduced in different kernel versions. So add get_update_safe() first. MEV and EATS will be added to arm_smmu_get_ste_update_safe() separately. Fixes: 1e8be08 ("iommu/arm-smmu-v3: Support IOMMU_DOMAIN_NESTED") Cc: stable@vger.kernel.org Signed-off-by: Jason Gunthorpe <jgg@nvidia.com> Reviewed-by: Shuai Xue <xueshuai@linux.alibaba.com> Reviewed-by: Mostafa Saleh <smostafa@google.com> Reviewed-by: Pranjal Shrivastava <praan@google.com> Signed-off-by: Nicolin Chen <nicolinc@nvidia.com> Signed-off-by: Will Deacon <will@kernel.org>
1 parent ea69dc4 commit 2781f2a

3 files changed

Lines changed: 53 additions & 10 deletions

File tree

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

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,13 +38,16 @@ enum arm_smmu_test_master_feat {
3838
static bool arm_smmu_entry_differs_in_used_bits(const __le64 *entry,
3939
const __le64 *used_bits,
4040
const __le64 *target,
41+
const __le64 *safe,
4142
unsigned int length)
4243
{
4344
bool differs = false;
4445
unsigned int i;
4546

4647
for (i = 0; i < length; i++) {
47-
if ((entry[i] & used_bits[i]) != target[i])
48+
__le64 used = used_bits[i] & ~safe[i];
49+
50+
if ((entry[i] & used) != (target[i] & used))
4851
differs = true;
4952
}
5053
return differs;
@@ -56,12 +59,24 @@ arm_smmu_test_writer_record_syncs(struct arm_smmu_entry_writer *writer)
5659
struct arm_smmu_test_writer *test_writer =
5760
container_of(writer, struct arm_smmu_test_writer, writer);
5861
__le64 *entry_used_bits;
62+
__le64 *safe_target;
63+
__le64 *safe_init;
5964

6065
entry_used_bits = kunit_kzalloc(
6166
test_writer->test, sizeof(*entry_used_bits) * NUM_ENTRY_QWORDS,
6267
GFP_KERNEL);
6368
KUNIT_ASSERT_NOT_NULL(test_writer->test, entry_used_bits);
6469

70+
safe_target = kunit_kzalloc(test_writer->test,
71+
sizeof(*safe_target) * NUM_ENTRY_QWORDS,
72+
GFP_KERNEL);
73+
KUNIT_ASSERT_NOT_NULL(test_writer->test, safe_target);
74+
75+
safe_init = kunit_kzalloc(test_writer->test,
76+
sizeof(*safe_init) * NUM_ENTRY_QWORDS,
77+
GFP_KERNEL);
78+
KUNIT_ASSERT_NOT_NULL(test_writer->test, safe_init);
79+
6580
pr_debug("STE value is now set to: ");
6681
print_hex_dump_debug(" ", DUMP_PREFIX_NONE, 16, 8,
6782
test_writer->entry,
@@ -79,14 +94,23 @@ arm_smmu_test_writer_record_syncs(struct arm_smmu_entry_writer *writer)
7994
* configuration.
8095
*/
8196
writer->ops->get_used(test_writer->entry, entry_used_bits);
97+
if (writer->ops->get_update_safe)
98+
writer->ops->get_update_safe(test_writer->entry,
99+
test_writer->init_entry,
100+
safe_init);
101+
if (writer->ops->get_update_safe)
102+
writer->ops->get_update_safe(test_writer->entry,
103+
test_writer->target_entry,
104+
safe_target);
82105
KUNIT_EXPECT_FALSE(
83106
test_writer->test,
84107
arm_smmu_entry_differs_in_used_bits(
85108
test_writer->entry, entry_used_bits,
86-
test_writer->init_entry, NUM_ENTRY_QWORDS) &&
109+
test_writer->init_entry, safe_init,
110+
NUM_ENTRY_QWORDS) &&
87111
arm_smmu_entry_differs_in_used_bits(
88112
test_writer->entry, entry_used_bits,
89-
test_writer->target_entry,
113+
test_writer->target_entry, safe_target,
90114
NUM_ENTRY_QWORDS));
91115
}
92116
}
@@ -106,6 +130,7 @@ arm_smmu_v3_test_debug_print_used_bits(struct arm_smmu_entry_writer *writer,
106130
static const struct arm_smmu_entry_writer_ops test_ste_ops = {
107131
.sync = arm_smmu_test_writer_record_syncs,
108132
.get_used = arm_smmu_get_ste_used,
133+
.get_update_safe = arm_smmu_get_ste_update_safe,
109134
};
110135

111136
static const struct arm_smmu_entry_writer_ops test_cd_ops = {

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

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1093,6 +1093,13 @@ void arm_smmu_get_ste_used(const __le64 *ent, __le64 *used_bits)
10931093
}
10941094
EXPORT_SYMBOL_IF_KUNIT(arm_smmu_get_ste_used);
10951095

1096+
VISIBLE_IF_KUNIT
1097+
void arm_smmu_get_ste_update_safe(const __le64 *cur, const __le64 *target,
1098+
__le64 *safe_bits)
1099+
{
1100+
}
1101+
EXPORT_SYMBOL_IF_KUNIT(arm_smmu_get_ste_update_safe);
1102+
10961103
/*
10971104
* Figure out if we can do a hitless update of entry to become target. Returns a
10981105
* bit mask where 1 indicates that qword needs to be set disruptively.
@@ -1105,13 +1112,22 @@ static u8 arm_smmu_entry_qword_diff(struct arm_smmu_entry_writer *writer,
11051112
{
11061113
__le64 target_used[NUM_ENTRY_QWORDS] = {};
11071114
__le64 cur_used[NUM_ENTRY_QWORDS] = {};
1115+
__le64 safe[NUM_ENTRY_QWORDS] = {};
11081116
u8 used_qword_diff = 0;
11091117
unsigned int i;
11101118

11111119
writer->ops->get_used(entry, cur_used);
11121120
writer->ops->get_used(target, target_used);
1121+
if (writer->ops->get_update_safe)
1122+
writer->ops->get_update_safe(entry, target, safe);
11131123

11141124
for (i = 0; i != NUM_ENTRY_QWORDS; i++) {
1125+
/*
1126+
* Safe is only used for bits that are used by both entries,
1127+
* otherwise it is sequenced according to the unused entry.
1128+
*/
1129+
safe[i] &= target_used[i] & cur_used[i];
1130+
11151131
/*
11161132
* Check that masks are up to date, the make functions are not
11171133
* allowed to set a bit to 1 if the used function doesn't say it
@@ -1120,6 +1136,7 @@ static u8 arm_smmu_entry_qword_diff(struct arm_smmu_entry_writer *writer,
11201136
WARN_ON_ONCE(target[i] & ~target_used[i]);
11211137

11221138
/* Bits can change because they are not currently being used */
1139+
cur_used[i] &= ~safe[i];
11231140
unused_update[i] = (entry[i] & cur_used[i]) |
11241141
(target[i] & ~cur_used[i]);
11251142
/*
@@ -1132,7 +1149,7 @@ static u8 arm_smmu_entry_qword_diff(struct arm_smmu_entry_writer *writer,
11321149
return used_qword_diff;
11331150
}
11341151

1135-
static bool entry_set(struct arm_smmu_entry_writer *writer, __le64 *entry,
1152+
static void entry_set(struct arm_smmu_entry_writer *writer, __le64 *entry,
11361153
const __le64 *target, unsigned int start,
11371154
unsigned int len)
11381155
{
@@ -1148,7 +1165,6 @@ static bool entry_set(struct arm_smmu_entry_writer *writer, __le64 *entry,
11481165

11491166
if (changed)
11501167
writer->ops->sync(writer);
1151-
return changed;
11521168
}
11531169

11541170
/*
@@ -1218,12 +1234,9 @@ void arm_smmu_write_entry(struct arm_smmu_entry_writer *writer, __le64 *entry,
12181234
entry_set(writer, entry, target, 0, 1);
12191235
} else {
12201236
/*
1221-
* No inuse bit changed. Sanity check that all unused bits are 0
1222-
* in the entry. The target was already sanity checked by
1223-
* compute_qword_diff().
1237+
* No inuse bit changed, though safe bits may have changed.
12241238
*/
1225-
WARN_ON_ONCE(
1226-
entry_set(writer, entry, target, 0, NUM_ENTRY_QWORDS));
1239+
entry_set(writer, entry, target, 0, NUM_ENTRY_QWORDS);
12271240
}
12281241
}
12291242
EXPORT_SYMBOL_IF_KUNIT(arm_smmu_write_entry);
@@ -1554,6 +1567,7 @@ static void arm_smmu_ste_writer_sync_entry(struct arm_smmu_entry_writer *writer)
15541567
static const struct arm_smmu_entry_writer_ops arm_smmu_ste_writer_ops = {
15551568
.sync = arm_smmu_ste_writer_sync_entry,
15561569
.get_used = arm_smmu_get_ste_used,
1570+
.get_update_safe = arm_smmu_get_ste_update_safe,
15571571
};
15581572

15591573
static void arm_smmu_write_ste(struct arm_smmu_master *master, u32 sid,

drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -898,6 +898,8 @@ struct arm_smmu_entry_writer {
898898

899899
struct arm_smmu_entry_writer_ops {
900900
void (*get_used)(const __le64 *entry, __le64 *used);
901+
void (*get_update_safe)(const __le64 *cur, const __le64 *target,
902+
__le64 *safe_bits);
901903
void (*sync)(struct arm_smmu_entry_writer *writer);
902904
};
903905

@@ -909,6 +911,8 @@ void arm_smmu_make_s2_domain_ste(struct arm_smmu_ste *target,
909911

910912
#if IS_ENABLED(CONFIG_KUNIT)
911913
void arm_smmu_get_ste_used(const __le64 *ent, __le64 *used_bits);
914+
void arm_smmu_get_ste_update_safe(const __le64 *cur, const __le64 *target,
915+
__le64 *safe_bits);
912916
void arm_smmu_write_entry(struct arm_smmu_entry_writer *writer, __le64 *cur,
913917
const __le64 *target);
914918
void arm_smmu_get_cd_used(const __le64 *ent, __le64 *used_bits);

0 commit comments

Comments
 (0)