From 7342cd76bed218a5bdf23eeb21c490644f20f47d Mon Sep 17 00:00:00 2001 From: Tushar Dave Date: Wed, 1 Mar 2017 17:29:57 -0800 Subject: [PATCH] SPARC64: Correct ATU IOTSB binding flow MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Any PCIe device attempting to use ATU for 64-bit DMA must be successfully bound to IOTSB otherwise DMA access from device will be rejected by PCIe root complex. In the current code, ATU initialization and PCIe device binding to IOTSB is done during system boot when PCI Bus Module (PBM) is initialized. (PBM driver traverse through the PCI tree and binds each PCI device to IOTSB). In case if user/system add SRIOV VFs later on (either after system is up or when PF driver is loaded), the new VF devices never get bounded to IOTSB. So when attempt is made from VF devices to access DMA region, ATU HW will flag it as invalid access; generates CTE_Invalid errors and VF driver/device fails to perform DMA. This patch fixes the aforementioned issue by delaying PCIe device binding to IOTSB only when driver requests DMA setting from kernel. Doing it this way makes sure every PCIe device requesting 64bit DMA mask (and therefore attempting to use ATU) will get bind to IOTSB and can do successful DMA operations. Orabug: 25177689 Orabug: 25450353 Signed-off-by: Tushar Dave Reviewed-by: Govinda Tatti Reviewed-by: HÃ¥kon Bugge Reviewed-by: Shannon Nelson Signed-off-by: Allen Pais --- arch/sparc/include/asm/iommu_64.h | 2 ++ arch/sparc/kernel/iommu.c | 14 ++++++-- arch/sparc/kernel/pci_sun4v.c | 59 ++++++++++++------------------- 3 files changed, 37 insertions(+), 38 deletions(-) diff --git a/arch/sparc/include/asm/iommu_64.h b/arch/sparc/include/asm/iommu_64.h index f24f356f2503..b81b4e3eaf83 100644 --- a/arch/sparc/include/asm/iommu_64.h +++ b/arch/sparc/include/asm/iommu_64.h @@ -35,6 +35,8 @@ struct atu_iotsb { u64 table_size; /* IOTSB table size */ u64 page_size; /* IO PAGE size for IOTSB */ u32 iotsb_num; /* tsbnum is same as iotsb_handle */ + + unsigned long (*bind)(struct device *dev); }; struct atu_ranges { diff --git a/arch/sparc/kernel/iommu.c b/arch/sparc/kernel/iommu.c index 3c752f454e79..a48dccb75215 100644 --- a/arch/sparc/kernel/iommu.c +++ b/arch/sparc/kernel/iommu.c @@ -759,12 +759,22 @@ int dma_supported(struct device *dev, u64 device_mask) { struct iommu *iommu = dev->archdata.iommu; u64 dma_addr_mask = iommu->dma_addr_mask; + unsigned long err; if (device_mask > DMA_BIT_MASK(32)) { - if (iommu->atu) + if (iommu->atu && dev_is_pci(dev)) { + /* bind this pci device to ATU IOTSB */ + err = iommu->atu->iotsb->bind(dev); + /* if binding fails, ATU cannot be used and hence + * no 64bit DMA. + */ + if (err) + return 0; + dma_addr_mask = iommu->atu->dma_addr_mask; - else + } else { return 0; + } } if ((device_mask & dma_addr_mask) == dma_addr_mask) diff --git a/arch/sparc/kernel/pci_sun4v.c b/arch/sparc/kernel/pci_sun4v.c index 0da3b11b98af..d7c7d2195cba 100644 --- a/arch/sparc/kernel/pci_sun4v.c +++ b/arch/sparc/kernel/pci_sun4v.c @@ -265,41 +265,33 @@ range_alloc_fail: return NULL; } -unsigned long dma_4v_iotsb_bind(unsigned long devhandle, - unsigned long iotsb_num, - struct pci_bus *bus_dev) +unsigned long dma_4v_iotsb_bind(struct device *dev) { - struct pci_dev *pdev; - unsigned long err; + struct pci_pbm_info *pbm = dev->archdata.host_controller; + struct pci_dev *pdev = to_pci_dev(dev); + unsigned long iotsb_num; + unsigned long err = 0; unsigned int bus; unsigned int device; unsigned int fun; - list_for_each_entry(pdev, &bus_dev->devices, bus_list) { - if (pdev->subordinate) { - /* No need to bind pci bridge */ - dma_4v_iotsb_bind(devhandle, iotsb_num, - pdev->subordinate); - } else { - bus = bus_dev->number; - device = PCI_SLOT(pdev->devfn); - fun = PCI_FUNC(pdev->devfn); - err = pci_sun4v_iotsb_bind(devhandle, iotsb_num, - HV_PCI_DEVICE_BUILD(bus, - device, - fun)); - - /* If bind fails for one device it is going to fail - * for rest of the devices because we are sharing - * IOTSB. So in case of failure simply return with - * error. - */ - if (err) - return err; - } - } - - return 0; + iotsb_num = pbm->iommu->atu->iotsb->iotsb_num; + bus = pdev->bus->number; + device = PCI_SLOT(pdev->devfn); + fun = PCI_FUNC(pdev->devfn); + + err = pci_sun4v_iotsb_bind(pbm->devhandle, iotsb_num, + HV_PCI_DEVICE_BUILD(bus, + device, + fun)); + + /* If bind fails, device will no longer be able to + * use ATU because hypervisor will reject any access + * to IOTSB until device is successfully bound to IOTSB. + * However, device still can continue using legacy IOMMU + * with 32bit DMA. + */ + return err; } static void dma_4v_iommu_demap(struct device *dev, unsigned long devhandle, @@ -873,12 +865,7 @@ static int pci_sun4v_atu_alloc_iotsb(struct pci_pbm_info *pbm) goto iotsb_conf_failed; } iotsb->iotsb_num = iotsb_num; - - err = dma_4v_iotsb_bind(pbm->devhandle, iotsb_num, pbm->pci_bus); - if (err) { - pr_err(PFX "pci_iotsb_bind failed error: %ld\n", err); - goto iotsb_conf_failed; - } + iotsb->bind = dma_4v_iotsb_bind; return 0; -- 2.50.1