Skip to content

Commit b512e3b

Browse files
nicolincnirmoy
authored andcommitted
NVIDIA: VR: SAUCE: iommu/arm-smmu-v3: Allow ATS to be always on
When a device's default substream attaches to an identity domain, the SMMU driver currently sets the device's STE between two modes: Mode 1: Cfg=Translate, S1DSS=Bypass, EATS=1 Mode 2: Cfg=bypass (EATS is ignored by HW) When there is an active PASID (non-default substream), mode 1 is used. And when there is no PASID support or no active PASID, mode 2 is used. The driver will also downgrade an STE from mode 1 to mode 2, when the last active substream becomes inactive. However, there are PCIe devices that demand ATS to be always on. For these devices, their STEs have to use the mode 1 as HW ignores EATS with mode 2. Change the driver accordingly: - always use the mode 1 - never downgrade to mode 2 - allocate and retain a CD table (see note below) Note that these devices might not support PASID, i.e. doing non-PASID ATS. In such a case, the ssid_bits is set to 0. However, s1cdmax must be set to a !0 value in order to keep the S1DSS field effective. Thus, when a master requires ats_always_on, set its s1cdmax to minimal 1, meaning the CD table will have a dummy entry (SSID=1) that will be never used. Now, for these device, arm_smmu_cdtab_allocated() will always return true, v.s. false prior to this change. When its default substream is attached to an IDENTITY domain, its first CD is NULL in the table, which is a totally valid case. Thus, drop the WARN_ON(). Signed-off-by: Nicolin Chen <nicolinc@nvidia.com> (backported from https://lore.kernel.org/linux-iommu/cover.1768624180.git.nicolinc@nvidia.com) Signed-off-by: Nirmoy Das <nirmoyd@nvidia.com>
1 parent 384fb52 commit b512e3b

File tree

2 files changed

+64
-11
lines changed

2 files changed

+64
-11
lines changed

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

Lines changed: 63 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1423,7 +1423,7 @@ void arm_smmu_clear_cd(struct arm_smmu_master *master, ioasid_t ssid)
14231423
if (!arm_smmu_cdtab_allocated(&master->cd_table))
14241424
return;
14251425
cdptr = arm_smmu_get_cd_ptr(master, ssid);
1426-
if (WARN_ON(!cdptr))
1426+
if (!cdptr)
14271427
return;
14281428
arm_smmu_write_cd_entry(master, ssid, cdptr, &target);
14291429
}
@@ -1437,6 +1437,22 @@ static int arm_smmu_alloc_cd_tables(struct arm_smmu_master *master)
14371437
struct arm_smmu_ctx_desc_cfg *cd_table = &master->cd_table;
14381438

14391439
cd_table->s1cdmax = master->ssid_bits;
1440+
1441+
/*
1442+
* When a device doesn't support PASID (non default SSID), ssid_bits is
1443+
* set to 0. This also sets S1CDMAX to 0, which disables the substreams
1444+
* and ignores the S1DSS field.
1445+
*
1446+
* On the other hand, if a device demands ATS to be always on even when
1447+
* its default substream is IOMMU bypassed, it has to use EATS that is
1448+
* only effective with an STE (CFG=S1translate, S1DSS=Bypass). For such
1449+
* use cases, S1CDMAX has to be !0, in order to make use of S1DSS/EATS.
1450+
*
1451+
* Set S1CDMAX no lower than 1. This would add a dummy substream in the
1452+
* CD table but it should never be used by an actual CD.
1453+
*/
1454+
if (master->ats_always_on)
1455+
cd_table->s1cdmax = max_t(u8, cd_table->s1cdmax, 1);
14401456
max_contexts = 1 << cd_table->s1cdmax;
14411457

14421458
if (!(smmu->features & ARM_SMMU_FEAT_2_LVL_CDTAB) ||
@@ -3189,7 +3205,8 @@ static int arm_smmu_blocking_set_dev_pasid(struct iommu_domain *new_domain,
31893205
* When the last user of the CD table goes away downgrade the STE back
31903206
* to a non-cd_table one.
31913207
*/
3192-
if (!arm_smmu_ssids_in_use(&master->cd_table)) {
3208+
if (!master->ats_always_on &&
3209+
!arm_smmu_ssids_in_use(&master->cd_table)) {
31933210
struct iommu_domain *sid_domain =
31943211
iommu_get_domain_for_dev(master->dev);
31953212

@@ -3203,7 +3220,7 @@ static int arm_smmu_blocking_set_dev_pasid(struct iommu_domain *new_domain,
32033220
static void arm_smmu_attach_dev_ste(struct iommu_domain *domain,
32043221
struct device *dev,
32053222
struct arm_smmu_ste *ste,
3206-
unsigned int s1dss)
3223+
unsigned int s1dss, bool ats_always_on)
32073224
{
32083225
struct arm_smmu_master *master = dev_iommu_priv_get(dev);
32093226
struct arm_smmu_attach_state state = {
@@ -3222,7 +3239,7 @@ static void arm_smmu_attach_dev_ste(struct iommu_domain *domain,
32223239
* If the CD table is not in use we can use the provided STE, otherwise
32233240
* we use a cdtable STE with the provided S1DSS.
32243241
*/
3225-
if (arm_smmu_ssids_in_use(&master->cd_table)) {
3242+
if (ats_always_on || arm_smmu_ssids_in_use(&master->cd_table)) {
32263243
/*
32273244
* If a CD table has to be present then we need to run with ATS
32283245
* on because we have to assume a PASID is using ATS. For
@@ -3256,7 +3273,8 @@ static int arm_smmu_attach_dev_identity(struct iommu_domain *domain,
32563273

32573274
arm_smmu_master_clear_vmaster(master);
32583275
arm_smmu_make_bypass_ste(master->smmu, &ste);
3259-
arm_smmu_attach_dev_ste(domain, dev, &ste, STRTAB_STE_1_S1DSS_BYPASS);
3276+
arm_smmu_attach_dev_ste(domain, dev, &ste, STRTAB_STE_1_S1DSS_BYPASS,
3277+
master->ats_always_on);
32603278
return 0;
32613279
}
32623280

@@ -3278,7 +3296,7 @@ static int arm_smmu_attach_dev_blocked(struct iommu_domain *domain,
32783296
arm_smmu_master_clear_vmaster(master);
32793297
arm_smmu_make_abort_ste(&ste);
32803298
arm_smmu_attach_dev_ste(domain, dev, &ste,
3281-
STRTAB_STE_1_S1DSS_TERMINATE);
3299+
STRTAB_STE_1_S1DSS_TERMINATE, false);
32823300
return 0;
32833301
}
32843302

@@ -3516,6 +3534,40 @@ static void arm_smmu_remove_master(struct arm_smmu_master *master)
35163534
kfree(master->streams);
35173535
}
35183536

3537+
static int arm_smmu_master_prepare_ats(struct arm_smmu_master *master)
3538+
{
3539+
bool s1p = master->smmu->features & ARM_SMMU_FEAT_TRANS_S1;
3540+
unsigned int stu = __ffs(master->smmu->pgsize_bitmap);
3541+
struct pci_dev *pdev = to_pci_dev(master->dev);
3542+
int ret;
3543+
3544+
if (!arm_smmu_ats_supported(master))
3545+
return 0;
3546+
3547+
if (!pci_ats_always_on(pdev))
3548+
goto out_prepare;
3549+
3550+
/*
3551+
* S1DSS is required for ATS to be always on for identity domain cases.
3552+
* However, the S1DSS field is ignored if !IDR0_S1P or !IDR1_SSIDSIZE.
3553+
*/
3554+
if (!s1p || !master->smmu->ssid_bits) {
3555+
dev_info_once(master->dev,
3556+
"SMMU doesn't support ATS to be always on\n");
3557+
goto out_prepare;
3558+
}
3559+
3560+
master->ats_always_on = true;
3561+
3562+
ret = arm_smmu_alloc_cd_tables(master);
3563+
if (ret)
3564+
return ret;
3565+
3566+
out_prepare:
3567+
pci_prepare_ats(pdev, stu);
3568+
return 0;
3569+
}
3570+
35193571
static struct iommu_device *arm_smmu_probe_device(struct device *dev)
35203572
{
35213573
int ret;
@@ -3564,14 +3616,14 @@ static struct iommu_device *arm_smmu_probe_device(struct device *dev)
35643616
smmu->features & ARM_SMMU_FEAT_STALL_FORCE)
35653617
master->stall_enabled = true;
35663618

3567-
if (dev_is_pci(dev)) {
3568-
unsigned int stu = __ffs(smmu->pgsize_bitmap);
3569-
3570-
pci_prepare_ats(to_pci_dev(dev), stu);
3571-
}
3619+
ret = arm_smmu_master_prepare_ats(master);
3620+
if (ret)
3621+
goto err_disable_pasid;
35723622

35733623
return &smmu->iommu;
35743624

3625+
err_disable_pasid:
3626+
arm_smmu_disable_pasid(master);
35753627
err_free_master:
35763628
kfree(master);
35773629
return ERR_PTR(ret);

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -861,6 +861,7 @@ struct arm_smmu_master {
861861
bool ats_enabled : 1;
862862
bool ste_ats_enabled : 1;
863863
bool stall_enabled;
864+
bool ats_always_on;
864865
unsigned int ssid_bits;
865866
unsigned int iopf_refcount;
866867
u16 partid;

0 commit comments

Comments
 (0)