Skip to content

Commit f27298a

Browse files
committed
iommu/arm-smmu-v3: Allow ATS for IOMMU_DOMAIN_NESTED
The EATS flag needs to flow through the vSTE and into the pSTE, and ensure physical ATS is enabled on the PCI device. The physical ATS state must match the VM's idea of EATS as we rely on the VM to issue the ATS invalidation commands. Thus ATS must remain off at the device until EATS on a nesting domain turns it on. Attaching a nesting domain is the point where the invalidation responsibility transfers to userspace. Update the ATS logic to track EATS for nesting domains and flush the ATC whenever the S2 nesting parent changes. Link: https://patch.msgid.link/r/[email protected] Signed-off-by: Nicolin Chen <[email protected]> Tested-by: Nicolin Chen <[email protected]> Signed-off-by: Jason Gunthorpe <[email protected]>
1 parent 67e4fe3 commit f27298a

File tree

4 files changed

+53
-10
lines changed

4 files changed

+53
-10
lines changed

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

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -95,8 +95,6 @@ static int arm_smmu_attach_dev_nested(struct iommu_domain *domain,
9595
.master = master,
9696
.old_domain = iommu_get_domain_for_dev(dev),
9797
.ssid = IOMMU_NO_PASID,
98-
/* Currently invalidation of ATC is not supported */
99-
.disable_ats = true,
10098
};
10199
struct arm_smmu_ste ste;
102100
int ret;
@@ -107,6 +105,15 @@ static int arm_smmu_attach_dev_nested(struct iommu_domain *domain,
107105
return -EBUSY;
108106

109107
mutex_lock(&arm_smmu_asid_lock);
108+
/*
109+
* The VM has to control the actual ATS state at the PCI device because
110+
* we forward the invalidations directly from the VM. If the VM doesn't
111+
* think ATS is on it will not generate ATC flushes and the ATC will
112+
* become incoherent. Since we can't access the actual virtual PCI ATS
113+
* config bit here base this off the EATS value in the STE. If the EATS
114+
* is set then the VM must generate ATC flushes.
115+
*/
116+
state.disable_ats = !nested_domain->enable_ats;
110117
ret = arm_smmu_attach_prepare(&state, domain);
111118
if (ret) {
112119
mutex_unlock(&arm_smmu_asid_lock);
@@ -131,8 +138,10 @@ static const struct iommu_domain_ops arm_smmu_nested_ops = {
131138
.free = arm_smmu_domain_nested_free,
132139
};
133140

134-
static int arm_smmu_validate_vste(struct iommu_hwpt_arm_smmuv3 *arg)
141+
static int arm_smmu_validate_vste(struct iommu_hwpt_arm_smmuv3 *arg,
142+
bool *enable_ats)
135143
{
144+
unsigned int eats;
136145
unsigned int cfg;
137146

138147
if (!(arg->ste[0] & cpu_to_le64(STRTAB_STE_0_V))) {
@@ -149,6 +158,18 @@ static int arm_smmu_validate_vste(struct iommu_hwpt_arm_smmuv3 *arg)
149158
if (cfg != STRTAB_STE_0_CFG_ABORT && cfg != STRTAB_STE_0_CFG_BYPASS &&
150159
cfg != STRTAB_STE_0_CFG_S1_TRANS)
151160
return -EIO;
161+
162+
/*
163+
* Only Full ATS or ATS UR is supported
164+
* The EATS field will be set by arm_smmu_make_nested_domain_ste()
165+
*/
166+
eats = FIELD_GET(STRTAB_STE_1_EATS, le64_to_cpu(arg->ste[1]));
167+
arg->ste[1] &= ~cpu_to_le64(STRTAB_STE_1_EATS);
168+
if (eats != STRTAB_STE_1_EATS_ABT && eats != STRTAB_STE_1_EATS_TRANS)
169+
return -EIO;
170+
171+
if (cfg == STRTAB_STE_0_CFG_S1_TRANS)
172+
*enable_ats = (eats == STRTAB_STE_1_EATS_TRANS);
152173
return 0;
153174
}
154175

@@ -160,6 +181,7 @@ arm_vsmmu_alloc_domain_nested(struct iommufd_viommu *viommu, u32 flags,
160181
const u32 SUPPORTED_FLAGS = IOMMU_HWPT_FAULT_ID_VALID;
161182
struct arm_smmu_nested_domain *nested_domain;
162183
struct iommu_hwpt_arm_smmuv3 arg;
184+
bool enable_ats = false;
163185
int ret;
164186

165187
/*
@@ -175,7 +197,7 @@ arm_vsmmu_alloc_domain_nested(struct iommufd_viommu *viommu, u32 flags,
175197
if (ret)
176198
return ERR_PTR(ret);
177199

178-
ret = arm_smmu_validate_vste(&arg);
200+
ret = arm_smmu_validate_vste(&arg, &enable_ats);
179201
if (ret)
180202
return ERR_PTR(ret);
181203

@@ -185,6 +207,7 @@ arm_vsmmu_alloc_domain_nested(struct iommufd_viommu *viommu, u32 flags,
185207

186208
nested_domain->domain.type = IOMMU_DOMAIN_NESTED;
187209
nested_domain->domain.ops = &arm_smmu_nested_ops;
210+
nested_domain->enable_ats = enable_ats;
188211
nested_domain->vsmmu = vsmmu;
189212
nested_domain->ste[0] = arg.ste[0];
190213
nested_domain->ste[1] = arg.ste[1] & ~cpu_to_le64(STRTAB_STE_1_EATS);

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

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2107,7 +2107,16 @@ int arm_smmu_atc_inv_domain(struct arm_smmu_domain *smmu_domain,
21072107
if (!master->ats_enabled)
21082108
continue;
21092109

2110-
arm_smmu_atc_inv_to_cmd(master_domain->ssid, iova, size, &cmd);
2110+
if (master_domain->nested_ats_flush) {
2111+
/*
2112+
* If a S2 used as a nesting parent is changed we have
2113+
* no option but to completely flush the ATC.
2114+
*/
2115+
arm_smmu_atc_inv_to_cmd(IOMMU_NO_PASID, 0, 0, &cmd);
2116+
} else {
2117+
arm_smmu_atc_inv_to_cmd(master_domain->ssid, iova, size,
2118+
&cmd);
2119+
}
21112120

21122121
for (i = 0; i < master->num_streams; i++) {
21132122
cmd.atc.sid = master->streams[i].id;
@@ -2631,7 +2640,7 @@ static void arm_smmu_disable_pasid(struct arm_smmu_master *master)
26312640
static struct arm_smmu_master_domain *
26322641
arm_smmu_find_master_domain(struct arm_smmu_domain *smmu_domain,
26332642
struct arm_smmu_master *master,
2634-
ioasid_t ssid)
2643+
ioasid_t ssid, bool nested_ats_flush)
26352644
{
26362645
struct arm_smmu_master_domain *master_domain;
26372646

@@ -2640,7 +2649,8 @@ arm_smmu_find_master_domain(struct arm_smmu_domain *smmu_domain,
26402649
list_for_each_entry(master_domain, &smmu_domain->devices,
26412650
devices_elm) {
26422651
if (master_domain->master == master &&
2643-
master_domain->ssid == ssid)
2652+
master_domain->ssid == ssid &&
2653+
master_domain->nested_ats_flush == nested_ats_flush)
26442654
return master_domain;
26452655
}
26462656
return NULL;
@@ -2671,13 +2681,18 @@ static void arm_smmu_remove_master_domain(struct arm_smmu_master *master,
26712681
{
26722682
struct arm_smmu_domain *smmu_domain = to_smmu_domain_devices(domain);
26732683
struct arm_smmu_master_domain *master_domain;
2684+
bool nested_ats_flush = false;
26742685
unsigned long flags;
26752686

26762687
if (!smmu_domain)
26772688
return;
26782689

2690+
if (domain->type == IOMMU_DOMAIN_NESTED)
2691+
nested_ats_flush = to_smmu_nested_domain(domain)->enable_ats;
2692+
26792693
spin_lock_irqsave(&smmu_domain->devices_lock, flags);
2680-
master_domain = arm_smmu_find_master_domain(smmu_domain, master, ssid);
2694+
master_domain = arm_smmu_find_master_domain(smmu_domain, master, ssid,
2695+
nested_ats_flush);
26812696
if (master_domain) {
26822697
list_del(&master_domain->devices_elm);
26832698
kfree(master_domain);
@@ -2744,6 +2759,9 @@ int arm_smmu_attach_prepare(struct arm_smmu_attach_state *state,
27442759
return -ENOMEM;
27452760
master_domain->master = master;
27462761
master_domain->ssid = state->ssid;
2762+
if (new_domain->type == IOMMU_DOMAIN_NESTED)
2763+
master_domain->nested_ats_flush =
2764+
to_smmu_nested_domain(new_domain)->enable_ats;
27472765

27482766
/*
27492767
* During prepare we want the current smmu_domain and new

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -305,7 +305,7 @@ static inline u32 arm_smmu_strtab_l2_idx(u32 sid)
305305
#define STRTAB_STE_1_NESTING_ALLOWED \
306306
cpu_to_le64(STRTAB_STE_1_S1DSS | STRTAB_STE_1_S1CIR | \
307307
STRTAB_STE_1_S1COR | STRTAB_STE_1_S1CSH | \
308-
STRTAB_STE_1_S1STALLD)
308+
STRTAB_STE_1_S1STALLD | STRTAB_STE_1_EATS)
309309

310310
/*
311311
* Context descriptors.
@@ -837,6 +837,7 @@ struct arm_smmu_domain {
837837
struct arm_smmu_nested_domain {
838838
struct iommu_domain domain;
839839
struct arm_vsmmu *vsmmu;
840+
bool enable_ats : 1;
840841

841842
__le64 ste[2];
842843
};
@@ -878,6 +879,7 @@ struct arm_smmu_master_domain {
878879
struct list_head devices_elm;
879880
struct arm_smmu_master *master;
880881
ioasid_t ssid;
882+
bool nested_ats_flush : 1;
881883
};
882884

883885
static inline struct arm_smmu_domain *to_smmu_domain(struct iommu_domain *dom)

include/uapi/linux/iommufd.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -429,7 +429,7 @@ struct iommu_hwpt_vtd_s1 {
429429
* the translation. Must be little-endian.
430430
* Allowed fields: (Refer to "5.2 Stream Table Entry" in SMMUv3 HW Spec)
431431
* - word-0: V, Cfg, S1Fmt, S1ContextPtr, S1CDMax
432-
* - word-1: S1DSS, S1CIR, S1COR, S1CSH, S1STALLD
432+
* - word-1: EATS, S1DSS, S1CIR, S1COR, S1CSH, S1STALLD
433433
*
434434
* -EIO will be returned if @ste is not legal or contains any non-allowed field.
435435
* Cfg can be used to select a S1, Bypass or Abort configuration. A Bypass

0 commit comments

Comments
 (0)