Skip to content

Commit ce26ea9

Browse files
jgunthorpewilldeacon
authored andcommitted
iommu/arm-smmu-v3: Allow IDENTITY/BLOCKED to be set while PASID is used
The HW supports this, use the S1DSS bits to configure the behavior of SSID=0 which is the RID's translation. If SSID's are currently being used in the CD table then just update the S1DSS bits in the STE, remove the master_domain and leave ATS alone. For iommufd the driver design has a small problem that all the unused CD table entries are set with V=0 which will generate an event if VFIO userspace tries to use the CD entry. This patch extends this problem to include the RID as well if PASID is being used. For BLOCKED with used PASIDs the F_STREAM_DISABLED (STRTAB_STE_1_S1DSS_TERMINATE) event is generated on untagged traffic and a substream CD table entry with V=0 (removed pasid) will generate C_BAD_CD. Arguably there is no advantage to using S1DSS over the CD entry 0 with V=0. As we don't yet support PASID in iommufd this is a problem to resolve later, possibly by using EPD0 for unused CD table entries instead of V=0, and not using S1DSS for BLOCKED. Tested-by: Nicolin Chen <[email protected]> Tested-by: Shameer Kolothum <[email protected]> Reviewed-by: Nicolin Chen <[email protected]> Reviewed-by: Jerry Snitselaar <[email protected]> Signed-off-by: Jason Gunthorpe <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Will Deacon <[email protected]>
1 parent d38c28d commit ce26ea9

File tree

3 files changed

+50
-16
lines changed

3 files changed

+50
-16
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,7 @@ static void arm_smmu_test_make_cdtable_ste(struct arm_smmu_ste *ste,
164164
.smmu = &smmu,
165165
};
166166

167-
arm_smmu_make_cdtable_ste(ste, &master, true);
167+
arm_smmu_make_cdtable_ste(ste, &master, true, STRTAB_STE_1_S1DSS_SSID0);
168168
}
169169

170170
static void arm_smmu_v3_write_ste_test_bypass_to_abort(struct kunit *test)

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

Lines changed: 47 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -991,6 +991,14 @@ void arm_smmu_get_ste_used(const __le64 *ent, __le64 *used_bits)
991991
STRTAB_STE_1_S1STALLD | STRTAB_STE_1_STRW |
992992
STRTAB_STE_1_EATS);
993993
used_bits[2] |= cpu_to_le64(STRTAB_STE_2_S2VMID);
994+
995+
/*
996+
* See 13.5 Summary of attribute/permission configuration fields
997+
* for the SHCFG behavior.
998+
*/
999+
if (FIELD_GET(STRTAB_STE_1_S1DSS, le64_to_cpu(ent[1])) ==
1000+
STRTAB_STE_1_S1DSS_BYPASS)
1001+
used_bits[1] |= cpu_to_le64(STRTAB_STE_1_SHCFG);
9941002
}
9951003

9961004
/* S2 translates */
@@ -1531,7 +1539,8 @@ EXPORT_SYMBOL_IF_KUNIT(arm_smmu_make_bypass_ste);
15311539

15321540
VISIBLE_IF_KUNIT
15331541
void arm_smmu_make_cdtable_ste(struct arm_smmu_ste *target,
1534-
struct arm_smmu_master *master, bool ats_enabled)
1542+
struct arm_smmu_master *master, bool ats_enabled,
1543+
unsigned int s1dss)
15351544
{
15361545
struct arm_smmu_ctx_desc_cfg *cd_table = &master->cd_table;
15371546
struct arm_smmu_device *smmu = master->smmu;
@@ -1545,7 +1554,7 @@ void arm_smmu_make_cdtable_ste(struct arm_smmu_ste *target,
15451554
FIELD_PREP(STRTAB_STE_0_S1CDMAX, cd_table->s1cdmax));
15461555

15471556
target->data[1] = cpu_to_le64(
1548-
FIELD_PREP(STRTAB_STE_1_S1DSS, STRTAB_STE_1_S1DSS_SSID0) |
1557+
FIELD_PREP(STRTAB_STE_1_S1DSS, s1dss) |
15491558
FIELD_PREP(STRTAB_STE_1_S1CIR, STRTAB_STE_1_S1C_CACHE_WBRA) |
15501559
FIELD_PREP(STRTAB_STE_1_S1COR, STRTAB_STE_1_S1C_CACHE_WBRA) |
15511560
FIELD_PREP(STRTAB_STE_1_S1CSH, ARM_SMMU_SH_ISH) |
@@ -1556,6 +1565,11 @@ void arm_smmu_make_cdtable_ste(struct arm_smmu_ste *target,
15561565
FIELD_PREP(STRTAB_STE_1_EATS,
15571566
ats_enabled ? STRTAB_STE_1_EATS_TRANS : 0));
15581567

1568+
if ((smmu->features & ARM_SMMU_FEAT_ATTR_TYPES_OVR) &&
1569+
s1dss == STRTAB_STE_1_S1DSS_BYPASS)
1570+
target->data[1] |= cpu_to_le64(FIELD_PREP(
1571+
STRTAB_STE_1_SHCFG, STRTAB_STE_1_SHCFG_INCOMING));
1572+
15591573
if (smmu->features & ARM_SMMU_FEAT_E2H) {
15601574
/*
15611575
* To support BTM the streamworld needs to match the
@@ -2579,6 +2593,7 @@ struct arm_smmu_attach_state {
25792593
/* Inputs */
25802594
struct iommu_domain *old_domain;
25812595
struct arm_smmu_master *master;
2596+
bool cd_needs_ats;
25822597
ioasid_t ssid;
25832598
/* Resulting state */
25842599
bool ats_enabled;
@@ -2620,7 +2635,7 @@ static int arm_smmu_attach_prepare(struct arm_smmu_attach_state *state,
26202635
*/
26212636
lockdep_assert_held(&arm_smmu_asid_lock);
26222637

2623-
if (smmu_domain) {
2638+
if (smmu_domain || state->cd_needs_ats) {
26242639
/*
26252640
* The SMMU does not support enabling ATS with bypass/abort.
26262641
* When the STE is in bypass (STE.Config[2:0] == 0b100), ATS
@@ -2632,7 +2647,9 @@ static int arm_smmu_attach_prepare(struct arm_smmu_attach_state *state,
26322647
* tables.
26332648
*/
26342649
state->ats_enabled = arm_smmu_ats_supported(master);
2650+
}
26352651

2652+
if (smmu_domain) {
26362653
master_domain = kzalloc(sizeof(*master_domain), GFP_KERNEL);
26372654
if (!master_domain)
26382655
return -ENOMEM;
@@ -2760,7 +2777,8 @@ static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev)
27602777
arm_smmu_make_s1_cd(&target_cd, master, smmu_domain);
27612778
arm_smmu_write_cd_entry(master, IOMMU_NO_PASID, cdptr,
27622779
&target_cd);
2763-
arm_smmu_make_cdtable_ste(&target, master, state.ats_enabled);
2780+
arm_smmu_make_cdtable_ste(&target, master, state.ats_enabled,
2781+
STRTAB_STE_1_S1DSS_SSID0);
27642782
arm_smmu_install_ste_for_dev(master, &target);
27652783
break;
27662784
}
@@ -2834,8 +2852,10 @@ static void arm_smmu_remove_dev_pasid(struct device *dev, ioasid_t pasid,
28342852
mutex_unlock(&arm_smmu_asid_lock);
28352853
}
28362854

2837-
static int arm_smmu_attach_dev_ste(struct iommu_domain *domain,
2838-
struct device *dev, struct arm_smmu_ste *ste)
2855+
static void arm_smmu_attach_dev_ste(struct iommu_domain *domain,
2856+
struct device *dev,
2857+
struct arm_smmu_ste *ste,
2858+
unsigned int s1dss)
28392859
{
28402860
struct arm_smmu_master *master = dev_iommu_priv_get(dev);
28412861
struct arm_smmu_attach_state state = {
@@ -2844,16 +2864,28 @@ static int arm_smmu_attach_dev_ste(struct iommu_domain *domain,
28442864
.ssid = IOMMU_NO_PASID,
28452865
};
28462866

2847-
if (arm_smmu_ssids_in_use(&master->cd_table))
2848-
return -EBUSY;
2849-
28502867
/*
28512868
* Do not allow any ASID to be changed while are working on the STE,
28522869
* otherwise we could miss invalidations.
28532870
*/
28542871
mutex_lock(&arm_smmu_asid_lock);
28552872

2856-
arm_smmu_attach_prepare(&state, domain);
2873+
/*
2874+
* If the CD table is not in use we can use the provided STE, otherwise
2875+
* we use a cdtable STE with the provided S1DSS.
2876+
*/
2877+
if (arm_smmu_ssids_in_use(&master->cd_table)) {
2878+
/*
2879+
* If a CD table has to be present then we need to run with ATS
2880+
* on even though the RID will fail ATS queries with UR. This is
2881+
* because we have no idea what the PASID's need.
2882+
*/
2883+
state.cd_needs_ats = true;
2884+
arm_smmu_attach_prepare(&state, domain);
2885+
arm_smmu_make_cdtable_ste(ste, master, state.ats_enabled, s1dss);
2886+
} else {
2887+
arm_smmu_attach_prepare(&state, domain);
2888+
}
28572889
arm_smmu_install_ste_for_dev(master, ste);
28582890
arm_smmu_attach_commit(&state);
28592891
mutex_unlock(&arm_smmu_asid_lock);
@@ -2864,7 +2896,6 @@ static int arm_smmu_attach_dev_ste(struct iommu_domain *domain,
28642896
* descriptor from arm_smmu_share_asid().
28652897
*/
28662898
arm_smmu_clear_cd(master, IOMMU_NO_PASID);
2867-
return 0;
28682899
}
28692900

28702901
static int arm_smmu_attach_dev_identity(struct iommu_domain *domain,
@@ -2874,7 +2905,8 @@ static int arm_smmu_attach_dev_identity(struct iommu_domain *domain,
28742905
struct arm_smmu_master *master = dev_iommu_priv_get(dev);
28752906

28762907
arm_smmu_make_bypass_ste(master->smmu, &ste);
2877-
return arm_smmu_attach_dev_ste(domain, dev, &ste);
2908+
arm_smmu_attach_dev_ste(domain, dev, &ste, STRTAB_STE_1_S1DSS_BYPASS);
2909+
return 0;
28782910
}
28792911

28802912
static const struct iommu_domain_ops arm_smmu_identity_ops = {
@@ -2892,7 +2924,9 @@ static int arm_smmu_attach_dev_blocked(struct iommu_domain *domain,
28922924
struct arm_smmu_ste ste;
28932925

28942926
arm_smmu_make_abort_ste(&ste);
2895-
return arm_smmu_attach_dev_ste(domain, dev, &ste);
2927+
arm_smmu_attach_dev_ste(domain, dev, &ste,
2928+
STRTAB_STE_1_S1DSS_TERMINATE);
2929+
return 0;
28962930
}
28972931

28982932
static const struct iommu_domain_ops arm_smmu_blocked_ops = {

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -761,8 +761,8 @@ void arm_smmu_make_abort_ste(struct arm_smmu_ste *target);
761761
void arm_smmu_make_bypass_ste(struct arm_smmu_device *smmu,
762762
struct arm_smmu_ste *target);
763763
void arm_smmu_make_cdtable_ste(struct arm_smmu_ste *target,
764-
struct arm_smmu_master *master,
765-
bool ats_enabled);
764+
struct arm_smmu_master *master, bool ats_enabled,
765+
unsigned int s1dss);
766766
void arm_smmu_make_s2_domain_ste(struct arm_smmu_ste *target,
767767
struct arm_smmu_master *master,
768768
struct arm_smmu_domain *smmu_domain,

0 commit comments

Comments
 (0)