Skip to content

Commit f8bb555

Browse files
nicolincnvidia-bfigg
authored andcommitted
NVIDIA: VR: SAUCE: iommu/arm-smmu-v3: Allow ATS to be always on
BugLink: https://bugs.launchpad.net/bugs/2150727 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 at least 1, meaning that the CD table will have a dummy entry (SSID=1) that will never be used. Now for these devices, 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, add "!master->ats_always_on" to the condition. Reviewed-by: Jonathan Cameron <jonathan.cameron@huawei.com> Tested-by: Nirmoy Das <nirmoyd@nvidia.com> Acked-by: Nirmoy Das <nirmoyd@nvidia.com> Reviewed-by: Jason Gunthorpe <jgg@nvidia.com> Reviewed-by: Kevin Tian <kevin.tian@intel.com> Signed-off-by: Nicolin Chen <nicolinc@nvidia.com> Reviewed-by: Dave Jiang <dave.jiang@intel.com> (backported from https://lore.kernel.org/r/7403163ebf59380f88c7503b3adf0dae07428df8.1777269009.git.nicolinc@nvidia.com) [Nirmoy: Apply after reverting older ATS always-on arm-smmu-v3 support.] Signed-off-by: Nirmoy Das <nirmoyd@nvidia.com> Acked-by: Jamie Nguyen <jamien@nvidia.com> Acked-by: Carol L Soto <csoto@nvidia.com> Acked-by: Matthew R. Ochs <mochs@nvidia.com> Signed-off-by: Brad Figg <bfigg@nvidia.com>
1 parent 9e852ca commit f8bb555

2 files changed

Lines changed: 68 additions & 8 deletions

File tree

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

Lines changed: 67 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1482,8 +1482,11 @@ void arm_smmu_clear_cd(struct arm_smmu_master *master, ioasid_t ssid)
14821482
if (!arm_smmu_cdtab_allocated(&master->cd_table))
14831483
return;
14841484
cdptr = arm_smmu_get_cd_ptr(master, ssid);
1485-
if (WARN_ON(!cdptr))
1485+
if (!cdptr) {
1486+
/* Only ats_always_on allows a NULL CD on default substream */
1487+
WARN_ON(!master->ats_always_on || ssid);
14861488
return;
1489+
}
14871490
arm_smmu_write_cd_entry(master, ssid, cdptr, &target);
14881491
}
14891492

@@ -1496,6 +1499,22 @@ static int arm_smmu_alloc_cd_tables(struct arm_smmu_master *master)
14961499
struct arm_smmu_ctx_desc_cfg *cd_table = &master->cd_table;
14971500

14981501
cd_table->s1cdmax = master->ssid_bits;
1502+
1503+
/*
1504+
* When a device doesn't support PASID (non default SSID), ssid_bits is
1505+
* set to 0. This also sets S1CDMAX to 0, which disables the substreams
1506+
* and ignores the S1DSS field.
1507+
*
1508+
* On the other hand, if a device demands ATS to be always on even when
1509+
* its default substream is IOMMU bypassed, it has to use EATS that is
1510+
* only effective with an STE (CFG=S1translate, S1DSS=Bypass). For such
1511+
* use cases, S1CDMAX has to be !0, in order to make use of S1DSS/EATS.
1512+
*
1513+
* Set S1CDMAX no lower than 1. This would add a dummy substream in the
1514+
* CD table but it should never be used by an actual CD.
1515+
*/
1516+
if (master->ats_always_on)
1517+
cd_table->s1cdmax = max_t(u8, cd_table->s1cdmax, 1);
14991518
max_contexts = 1 << cd_table->s1cdmax;
15001519

15011520
if (!(smmu->features & ARM_SMMU_FEAT_2_LVL_CDTAB) ||
@@ -3250,7 +3269,8 @@ static int arm_smmu_blocking_set_dev_pasid(struct iommu_domain *new_domain,
32503269
* When the last user of the CD table goes away downgrade the STE back
32513270
* to a non-cd_table one, by re-attaching its sid_domain.
32523271
*/
3253-
if (!arm_smmu_ssids_in_use(&master->cd_table)) {
3272+
if (!master->ats_always_on &&
3273+
!arm_smmu_ssids_in_use(&master->cd_table)) {
32543274
struct iommu_domain *sid_domain =
32553275
iommu_driver_get_domain_for_dev(master->dev);
32563276

@@ -3274,6 +3294,8 @@ static void arm_smmu_attach_dev_ste(struct iommu_domain *domain,
32743294
.old_domain = old_domain,
32753295
.ssid = IOMMU_NO_PASID,
32763296
};
3297+
bool ats_always_on = master->ats_always_on &&
3298+
s1dss != STRTAB_STE_1_S1DSS_TERMINATE;
32773299

32783300
/*
32793301
* Do not allow any ASID to be changed while are working on the STE,
@@ -3285,7 +3307,7 @@ static void arm_smmu_attach_dev_ste(struct iommu_domain *domain,
32853307
* If the CD table is not in use we can use the provided STE, otherwise
32863308
* we use a cdtable STE with the provided S1DSS.
32873309
*/
3288-
if (arm_smmu_ssids_in_use(&master->cd_table)) {
3310+
if (ats_always_on || arm_smmu_ssids_in_use(&master->cd_table)) {
32893311
/*
32903312
* If a CD table has to be present then we need to run with ATS
32913313
* on because we have to assume a PASID is using ATS. For
@@ -3581,6 +3603,42 @@ static void arm_smmu_remove_master(struct arm_smmu_master *master)
35813603
kfree(master->streams);
35823604
}
35833605

3606+
static int arm_smmu_master_prepare_ats(struct arm_smmu_master *master)
3607+
{
3608+
bool s1p = master->smmu->features & ARM_SMMU_FEAT_TRANS_S1;
3609+
unsigned int stu = __ffs(master->smmu->pgsize_bitmap);
3610+
struct pci_dev *pdev;
3611+
int ret;
3612+
3613+
if (!arm_smmu_ats_supported(master))
3614+
return 0;
3615+
3616+
pdev = to_pci_dev(master->dev);
3617+
3618+
if (!pci_ats_always_on(pdev))
3619+
goto out_prepare;
3620+
3621+
/*
3622+
* S1DSS is required for ATS to be always on for identity domain cases.
3623+
* However, the S1DSS field is ignored if !IDR0_S1P or !IDR1_SSIDSIZE.
3624+
*/
3625+
if (!s1p || !master->smmu->ssid_bits) {
3626+
dev_info_once(master->dev,
3627+
"SMMU doesn't support ATS to be always on\n");
3628+
goto out_prepare;
3629+
}
3630+
3631+
master->ats_always_on = true;
3632+
3633+
ret = arm_smmu_alloc_cd_tables(master);
3634+
if (ret)
3635+
return ret;
3636+
3637+
out_prepare:
3638+
pci_prepare_ats(pdev, stu);
3639+
return 0;
3640+
}
3641+
35843642
static struct iommu_device *arm_smmu_probe_device(struct device *dev)
35853643
{
35863644
int ret;
@@ -3629,14 +3687,15 @@ static struct iommu_device *arm_smmu_probe_device(struct device *dev)
36293687
smmu->features & ARM_SMMU_FEAT_STALL_FORCE)
36303688
master->stall_enabled = true;
36313689

3632-
if (dev_is_pci(dev)) {
3633-
unsigned int stu = __ffs(smmu->pgsize_bitmap);
3634-
3635-
pci_prepare_ats(to_pci_dev(dev), stu);
3636-
}
3690+
ret = arm_smmu_master_prepare_ats(master);
3691+
if (ret)
3692+
goto err_disable_pasid;
36373693

36383694
return &smmu->iommu;
36393695

3696+
err_disable_pasid:
3697+
arm_smmu_disable_pasid(master);
3698+
arm_smmu_remove_master(master);
36403699
err_free_master:
36413700
kfree(master);
36423701
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
@@ -848,6 +848,7 @@ struct arm_smmu_master {
848848
bool ats_enabled : 1;
849849
bool ste_ats_enabled : 1;
850850
bool stall_enabled;
851+
bool ats_always_on;
851852
unsigned int ssid_bits;
852853
unsigned int iopf_refcount;
853854
};

0 commit comments

Comments
 (0)