Skip to content

Commit 6c17c7d

Browse files
jgunthorpejoergroedel
authored andcommitted
iommu: Allow ATS to work on VFs when the PF uses IDENTITY
PCI ATS has a global Smallest Translation Unit field that is located in the PF but shared by all of the VFs. The expectation is that the STU will be set to the root port's global STU capability which is driven by the IO page table configuration of the iommu HW. Today it becomes set when the iommu driver first enables ATS. Thus, to enable ATS on the VF, the PF must have already had the correct STU programmed, even if ATS is off on the PF. Unfortunately the PF only programs the STU when the PF enables ATS. The iommu drivers tend to leave ATS disabled when IDENTITY translation is being used. Thus we can get into a state where the PF is setup to use IDENTITY with the DMA API while the VF would like to use VFIO with a PAGING domain and have ATS turned on. This fails because the PF never loaded a PAGING domain and so it never setup the STU, and the VF can't do it. The simplest solution is to have the iommu driver set the ATS STU when it probes the device. This way the ATS STU is loaded immediately at boot time to all PFs and there is no issue when a VF comes to use it. Add a new call pci_prepare_ats() which should be called by iommu drivers in their probe_device() op for every PCI device if the iommu driver supports ATS. This will setup the STU based on whatever page size capability the iommu HW has. Signed-off-by: Jason Gunthorpe <[email protected]> Acked-by: Bjorn Helgaas <[email protected]> Reviewed-by: Kevin Tian <[email protected]> Reviewed-by: Lu Baolu <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Joerg Roedel <[email protected]>
1 parent 7c626ce commit 6c17c7d

File tree

5 files changed

+46
-0
lines changed

5 files changed

+46
-0
lines changed

drivers/iommu/amd/iommu.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2203,6 +2203,9 @@ static struct iommu_device *amd_iommu_probe_device(struct device *dev)
22032203

22042204
iommu_completion_wait(iommu);
22052205

2206+
if (dev_is_pci(dev))
2207+
pci_prepare_ats(to_pci_dev(dev), PAGE_SHIFT);
2208+
22062209
return iommu_dev;
22072210
}
22082211

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3295,6 +3295,12 @@ static struct iommu_device *arm_smmu_probe_device(struct device *dev)
32953295
smmu->features & ARM_SMMU_FEAT_STALL_FORCE)
32963296
master->stall_enabled = true;
32973297

3298+
if (dev_is_pci(dev)) {
3299+
unsigned int stu = __ffs(smmu->pgsize_bitmap);
3300+
3301+
pci_prepare_ats(to_pci_dev(dev), stu);
3302+
}
3303+
32983304
return &smmu->iommu;
32993305

33003306
err_free_master:

drivers/iommu/intel/iommu.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4091,6 +4091,7 @@ static struct iommu_device *intel_iommu_probe_device(struct device *dev)
40914091

40924092
dev_iommu_priv_set(dev, info);
40934093
if (pdev && pci_ats_supported(pdev)) {
4094+
pci_prepare_ats(pdev, VTD_PAGE_SHIFT);
40944095
ret = device_rbtree_insert(iommu, info);
40954096
if (ret)
40964097
goto free;

drivers/pci/ats.c

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,39 @@ bool pci_ats_supported(struct pci_dev *dev)
4747
}
4848
EXPORT_SYMBOL_GPL(pci_ats_supported);
4949

50+
/**
51+
* pci_prepare_ats - Setup the PS for ATS
52+
* @dev: the PCI device
53+
* @ps: the IOMMU page shift
54+
*
55+
* This must be done by the IOMMU driver on the PF before any VFs are created to
56+
* ensure that the VF can have ATS enabled.
57+
*
58+
* Returns 0 on success, or negative on failure.
59+
*/
60+
int pci_prepare_ats(struct pci_dev *dev, int ps)
61+
{
62+
u16 ctrl;
63+
64+
if (!pci_ats_supported(dev))
65+
return -EINVAL;
66+
67+
if (WARN_ON(dev->ats_enabled))
68+
return -EBUSY;
69+
70+
if (ps < PCI_ATS_MIN_STU)
71+
return -EINVAL;
72+
73+
if (dev->is_virtfn)
74+
return 0;
75+
76+
dev->ats_stu = ps;
77+
ctrl = PCI_ATS_CTRL_STU(dev->ats_stu - PCI_ATS_MIN_STU);
78+
pci_write_config_word(dev, dev->ats_cap + PCI_ATS_CTRL, ctrl);
79+
return 0;
80+
}
81+
EXPORT_SYMBOL_GPL(pci_prepare_ats);
82+
5083
/**
5184
* pci_enable_ats - enable the ATS capability
5285
* @dev: the PCI device

include/linux/pci-ats.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
/* Address Translation Service */
99
bool pci_ats_supported(struct pci_dev *dev);
1010
int pci_enable_ats(struct pci_dev *dev, int ps);
11+
int pci_prepare_ats(struct pci_dev *dev, int ps);
1112
void pci_disable_ats(struct pci_dev *dev);
1213
int pci_ats_queue_depth(struct pci_dev *dev);
1314
int pci_ats_page_aligned(struct pci_dev *dev);
@@ -16,6 +17,8 @@ static inline bool pci_ats_supported(struct pci_dev *d)
1617
{ return false; }
1718
static inline int pci_enable_ats(struct pci_dev *d, int ps)
1819
{ return -ENODEV; }
20+
static inline int pci_prepare_ats(struct pci_dev *dev, int ps)
21+
{ return -ENODEV; }
1922
static inline void pci_disable_ats(struct pci_dev *d) { }
2023
static inline int pci_ats_queue_depth(struct pci_dev *d)
2124
{ return -ENODEV; }

0 commit comments

Comments
 (0)