powerpc/powernv: Override dma_get_required_mask()
authorGavin Shan <gwshan@linux.vnet.ibm.com>
Tue, 30 Sep 2014 02:39:10 +0000 (12:39 +1000)
committerMichael Ellerman <mpe@ellerman.id.au>
Tue, 30 Sep 2014 07:15:20 +0000 (17:15 +1000)
The dma_get_required_mask() function is used by some drivers to
query the platform about what DMA mask is needed to cover all of
memory. This is a bit of a strange semantic when we have to choose
between IOMMU translation or bypass, but essentially what it means
is "what DMA mask will give best performances".

Currently, our IOMMU backend always returns a 32-bit mask here, we
don't do anything special to it when we have bypass available. This
causes some drivers to choose a 32-bit mask, thus losing the ability
to use the bypass window, thinking this is more efficient. The problem
was reported from the driver of following device:

0004:03:00.0 0107: 1000:0087 (rev 05)
0004:03:00.0 Serial Attached SCSI controller: LSI Logic / Symbios \
             Logic SAS2308 PCI-Express Fusion-MPT SAS-2 (rev 05)

This patch adds an override of that function in order to, instead,
return a 64-bit mask whenever a bypass window is available in order
for drivers to prefer this configuration.

Reported-by: Murali N. Iyer <mniyer@us.ibm.com>
Suggested-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Signed-off-by: Gavin Shan <gwshan@linux.vnet.ibm.com>
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
arch/powerpc/include/asm/dma-mapping.h
arch/powerpc/kernel/dma.c
arch/powerpc/platforms/powernv/pci-ioda.c
arch/powerpc/platforms/powernv/pci.c
arch/powerpc/platforms/powernv/pci.h
arch/powerpc/platforms/powernv/powernv.h
arch/powerpc/platforms/powernv/setup.c

index 150866b2a3fe07337f38905511a73cd726e07689..894d538f356783336f6addf6468ab941cafd0d02 100644 (file)
@@ -135,6 +135,7 @@ static inline int dma_supported(struct device *dev, u64 mask)
 
 extern int dma_set_mask(struct device *dev, u64 dma_mask);
 extern int __dma_set_mask(struct device *dev, u64 dma_mask);
+extern u64 __dma_get_required_mask(struct device *dev);
 
 #define dma_alloc_coherent(d,s,h,f)    dma_alloc_attrs(d,s,h,f,NULL)
 
index ee78f6e49d64bddc78d58382b73008841896b38a..210ff9d3c182821001f9025ddb80fd696121d10c 100644 (file)
@@ -202,6 +202,7 @@ int __dma_set_mask(struct device *dev, u64 dma_mask)
        *dev->dma_mask = dma_mask;
        return 0;
 }
+
 int dma_set_mask(struct device *dev, u64 dma_mask)
 {
        if (ppc_md.dma_set_mask)
@@ -210,13 +211,10 @@ int dma_set_mask(struct device *dev, u64 dma_mask)
 }
 EXPORT_SYMBOL(dma_set_mask);
 
-u64 dma_get_required_mask(struct device *dev)
+u64 __dma_get_required_mask(struct device *dev)
 {
        struct dma_map_ops *dma_ops = get_dma_ops(dev);
 
-       if (ppc_md.dma_get_required_mask)
-               return ppc_md.dma_get_required_mask(dev);
-
        if (unlikely(dma_ops == NULL))
                return 0;
 
@@ -225,6 +223,14 @@ u64 dma_get_required_mask(struct device *dev)
 
        return DMA_BIT_MASK(8 * sizeof(dma_addr_t));
 }
+
+u64 dma_get_required_mask(struct device *dev)
+{
+       if (ppc_md.dma_get_required_mask)
+               return ppc_md.dma_get_required_mask(dev);
+
+       return __dma_get_required_mask(dev);
+}
 EXPORT_SYMBOL_GPL(dma_get_required_mask);
 
 static int __init dma_init(void)
index 8685dacccb0b9d9dc0f35bce089d62a5ab81fef4..0fd6bdb493c978ab59b25ea46217232b726067dd 100644 (file)
@@ -889,6 +889,28 @@ static int pnv_pci_ioda_dma_set_mask(struct pnv_phb *phb,
        return 0;
 }
 
+static u64 pnv_pci_ioda_dma_get_required_mask(struct pnv_phb *phb,
+                                             struct pci_dev *pdev)
+{
+       struct pci_dn *pdn = pci_get_pdn(pdev);
+       struct pnv_ioda_pe *pe;
+       u64 end, mask;
+
+       if (WARN_ON(!pdn || pdn->pe_number == IODA_INVALID_PE))
+               return 0;
+
+       pe = &phb->ioda.pe_array[pdn->pe_number];
+       if (!pe->tce_bypass_enabled)
+               return __dma_get_required_mask(&pdev->dev);
+
+
+       end = pe->tce_bypass_base + memblock_end_of_DRAM();
+       mask = 1ULL << (fls64(end) - 1);
+       mask += mask - 1;
+
+       return mask;
+}
+
 static void pnv_ioda_setup_bus_dma(struct pnv_ioda_pe *pe,
                                   struct pci_bus *bus,
                                   bool add_to_iommu_group)
@@ -1781,6 +1803,7 @@ static void __init pnv_pci_init_ioda_phb(struct device_node *np,
        /* Setup TCEs */
        phb->dma_dev_setup = pnv_pci_ioda_dma_dev_setup;
        phb->dma_set_mask = pnv_pci_ioda_dma_set_mask;
+       phb->dma_get_required_mask = pnv_pci_ioda_dma_get_required_mask;
 
        /* Setup shutdown function for kexec */
        phb->shutdown = pnv_pci_ioda_shutdown;
index b854b57ed5e1e26c95c9925db9465c6767a89567..e9f509bbc078843288357d8fd3628532777b07b6 100644 (file)
@@ -761,6 +761,17 @@ int pnv_pci_dma_set_mask(struct pci_dev *pdev, u64 dma_mask)
        return __dma_set_mask(&pdev->dev, dma_mask);
 }
 
+u64 pnv_pci_dma_get_required_mask(struct pci_dev *pdev)
+{
+       struct pci_controller *hose = pci_bus_to_host(pdev->bus);
+       struct pnv_phb *phb = hose->private_data;
+
+       if (phb && phb->dma_get_required_mask)
+               return phb->dma_get_required_mask(phb, pdev);
+
+       return __dma_get_required_mask(&pdev->dev);
+}
+
 void pnv_pci_shutdown(void)
 {
        struct pci_controller *hose;
index 27594cf7f76d42f1202dc926180013ded1b627f6..34d29eb2a4def4dc83044ad6cfaf5c42210a9e86 100644 (file)
@@ -124,6 +124,8 @@ struct pnv_phb {
        void (*dma_dev_setup)(struct pnv_phb *phb, struct pci_dev *pdev);
        int (*dma_set_mask)(struct pnv_phb *phb, struct pci_dev *pdev,
                            u64 dma_mask);
+       u64 (*dma_get_required_mask)(struct pnv_phb *phb,
+                                    struct pci_dev *pdev);
        void (*fixup_phb)(struct pci_controller *hose);
        u32 (*bdfn_to_pe)(struct pnv_phb *phb, struct pci_bus *bus, u32 devfn);
        void (*shutdown)(struct pnv_phb *phb);
index 75501bfede7f9b2fdc51a3987fd03d09383477ee..6c8e2d188cd096330b7b1926da13c50de1e73f2e 100644 (file)
@@ -13,6 +13,7 @@ struct pci_dev;
 extern void pnv_pci_init(void);
 extern void pnv_pci_shutdown(void);
 extern int pnv_pci_dma_set_mask(struct pci_dev *pdev, u64 dma_mask);
+extern u64 pnv_pci_dma_get_required_mask(struct pci_dev *pdev);
 #else
 static inline void pnv_pci_init(void) { }
 static inline void pnv_pci_shutdown(void) { }
@@ -21,6 +22,11 @@ static inline int pnv_pci_dma_set_mask(struct pci_dev *pdev, u64 dma_mask)
 {
        return -ENODEV;
 }
+
+static inline u64 pnv_pci_dma_get_required_mask(struct pci_dev *pdev)
+{
+       return 0;
+}
 #endif
 
 extern void pnv_lpc_init(void);
index bb1fc9b8d55eb1b914f7446d50ed836208f6e416..3f9546d8a51f907dda294d33c1d507bc01d6aa1b 100644 (file)
@@ -173,6 +173,14 @@ static int pnv_dma_set_mask(struct device *dev, u64 dma_mask)
        return __dma_set_mask(dev, dma_mask);
 }
 
+static u64 pnv_dma_get_required_mask(struct device *dev)
+{
+       if (dev_is_pci(dev))
+               return pnv_pci_dma_get_required_mask(to_pci_dev(dev));
+
+       return __dma_get_required_mask(dev);
+}
+
 static void pnv_shutdown(void)
 {
        /* Let the PCI code clear up IODA tables */
@@ -335,6 +343,7 @@ define_machine(powernv) {
        .power_save             = power7_idle,
        .calibrate_decr         = generic_calibrate_decr,
        .dma_set_mask           = pnv_dma_set_mask,
+       .dma_get_required_mask  = pnv_dma_get_required_mask,
 #ifdef CONFIG_KEXEC
        .kexec_cpu_down         = pnv_kexec_cpu_down,
 #endif