]> www.infradead.org Git - users/jedix/linux-maple.git/commitdiff
SPARC64: Correct ATU IOTSB binding flow
authorTushar Dave <tushar.n.dave@oracle.com>
Thu, 2 Mar 2017 01:29:57 +0000 (17:29 -0800)
committerAllen Pais <allen.pais@oracle.com>
Tue, 11 Jul 2017 07:36:50 +0000 (13:06 +0530)
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 <tushar.n.dave@oracle.com>
Reviewed-by: Govinda Tatti <Govinda.Tatti@Oracle.COM>
Reviewed-by: HÃ¥kon Bugge <haakon.bugge@oracle.com>
Reviewed-by: Shannon Nelson <shannon.nelson@oracle.com>
Signed-off-by: Allen Pais <allen.pais@oracle.com>
arch/sparc/include/asm/iommu_64.h
arch/sparc/kernel/iommu.c
arch/sparc/kernel/pci_sun4v.c

index f24f356f250376e0931a4d4bc5d6bc631f469a74..b81b4e3eaf8338fbb405bd3c94e48a54afef9305 100644 (file)
@@ -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 {
index 3c752f454e79ca0e02b728f78ac06ef8174438ca..a48dccb752153a52bab95442106ba1a789f77957 100644 (file)
@@ -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)
index 0da3b11b98af66f61608b3a510355e27282092f0..d7c7d2195cba7a481e8b36447520b8589c941a70 100644 (file)
@@ -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;