]> www.infradead.org Git - users/jedix/linux-maple.git/commitdiff
sparc64: bypass iommu to use 64bit address space
authorTushar Dave <tushar.n.dave@oracle.com>
Thu, 7 Jan 2016 23:24:26 +0000 (15:24 -0800)
committerAllen Pais <allen.pais@oracle.com>
Tue, 19 Apr 2016 12:21:01 +0000 (17:51 +0530)
This patch is internal only not for UPSTREAM. This is a temporary
workaround based on UEK2 commit c1a12ed1d125
("sparc64: enable iommu bypass workaround for IB. This is temporary.")

Current design of sparc iommu is based on iommu V1 APIs which at max
can have 2G/8K DMA addresses. Due to this, kernel entity (e.g. i40e,
PSIF) requesting more than 2G/8K DMA addresses does not work at all.
This patch adds temporary workaround to remedy this issue by bypassing
iommu.

When 64bit iommu implementation is complete, this workaround will be
reverted.

Orabug: 21149316
Signed-off-by: Tushar Dave <tushar.n.dave@oracle.com>
(cherry picked from commit d751c5e1e6575b1dc119383045ba488e0d30de4d)
(cherry picked from commit 2ecc8426003036609fc447c3cf2dcf54139770cf)

arch/sparc/Kconfig
arch/sparc/kernel/iommu.c
arch/sparc/kernel/pci_sun4v.c
arch/sparc/kernel/pci_sun4v.h
arch/sparc/kernel/pci_sun4v_asm.S

index 28b3e9b76755294fd57821e867ab9a15b649a863..5be445d60a5a6e1cc6e7a1c9acfb4ce893fb4c5b 100644 (file)
@@ -82,6 +82,10 @@ config SPARC64
        select ARCH_SUPPORTS_ATOMIC_RMW
        select ARCH_CLOCKSOURCE_DATA
 
+config ARCH_DMA_ADDR_T_64BIT
+       bool
+       default y if SPARC64
+
 config ARCH_DEFCONFIG
        string
        default "arch/sparc/configs/sparc32_defconfig" if SPARC32
index 5320689c06e97a5731825b5c2b45d955ef340af3..c330bd5bc362ef9dcc2f7fa5fea2578d62e4e62b 100644 (file)
@@ -760,9 +760,6 @@ int dma_supported(struct device *dev, u64 device_mask)
        struct iommu *iommu = dev->archdata.iommu;
        u64 dma_addr_mask = iommu->dma_addr_mask;
 
-       if (device_mask >= (1UL << 32UL))
-               return 0;
-
        if ((device_mask & dma_addr_mask) == dma_addr_mask)
                return 1;
 
index d2fe57dad433ea9409c52a3aaf8ae9efa2113032..02edfbd5c10447044a2519084c410eb287e83052 100644 (file)
@@ -46,6 +46,28 @@ struct iommu_batch {
 static DEFINE_PER_CPU(struct iommu_batch, iommu_batch);
 static int iommu_batch_initialized;
 
+
+unsigned long iommu_getbypass(unsigned long ra,
+                             unsigned long attr,
+                             unsigned long *io_addr_p)
+{
+       struct iommu_batch *p = this_cpu_ptr(&iommu_batch);
+       struct pci_pbm_info *pbm = p->dev->archdata.host_controller;
+       unsigned long devhandle = pbm->devhandle;
+       unsigned long ret;
+
+       ret = pci_sun4v_iommu_getbypass(devhandle, ra, attr, io_addr_p);
+
+       printk(KERN_ERR "iommu_getbypass: devhandle 0x%lx ra 0x%lx prot 0x%lx dma 0x%lx\n",
+                       devhandle, ra, attr, *io_addr_p);
+
+       if (ret)
+               printk(KERN_ERR "iommu_getbypass: err 0x%lx\n", ret);
+
+       return ret;
+}
+EXPORT_SYMBOL(iommu_getbypass);
+
 /* Interrupts must be disabled.  */
 static inline void iommu_batch_start(struct device *dev, unsigned long prot, unsigned long entry)
 {
@@ -232,6 +254,42 @@ static void dma_4v_free_coherent(struct device *dev, size_t size, void *cpu,
                free_pages((unsigned long)cpu, order);
 }
 
+static dma_addr_t dma_4v_map_page_bypass(struct device *dev, struct page *page,
+                                        unsigned long offset, size_t sz,
+                                        enum dma_data_direction direction,
+                                        struct dma_attrs *attrs)
+{
+       struct pci_pbm_info *pbm;
+       unsigned long devhandle;
+       unsigned long ra;
+       unsigned long prot;
+       unsigned long dma_addr;
+
+       BUG_ON(!dev);
+       pbm = dev->archdata.host_controller;
+       BUG_ON(!pbm);
+       devhandle = pbm->devhandle;
+
+       if (unlikely(direction == DMA_NONE))
+               goto bad;
+
+       prot = HV_PCI_MAP_ATTR_READ;
+
+       if (direction != DMA_TO_DEVICE)
+               prot |= HV_PCI_MAP_ATTR_WRITE;
+
+
+       ra = __pa(page_address(page) + offset);
+
+       if (pci_sun4v_iommu_getbypass(devhandle, ra, prot, &dma_addr))
+               goto bad;
+
+       return dma_addr;
+
+bad:
+       return DMA_ERROR_CODE;
+}
+
 static dma_addr_t dma_4v_map_page(struct device *dev, struct page *page,
                                  unsigned long offset, size_t sz,
                                  enum dma_data_direction direction,
@@ -244,6 +302,9 @@ static dma_addr_t dma_4v_map_page(struct device *dev, struct page *page,
        unsigned long prot;
        long entry;
 
+       return dma_4v_map_page_bypass(dev, page, offset, sz,
+                                             direction, attrs);
+
        iommu = dev->archdata.iommu;
 
        if (unlikely(direction == DMA_NONE))
@@ -302,6 +363,9 @@ static void dma_4v_unmap_page(struct device *dev, dma_addr_t bus_addr,
        long entry;
        u32 devhandle;
 
+       /* no need to un-map bypass dma address */
+       return;
+
        if (unlikely(direction == DMA_NONE)) {
                if (printk_ratelimit())
                        WARN_ON(1);
@@ -320,6 +384,45 @@ static void dma_4v_unmap_page(struct device *dev, dma_addr_t bus_addr,
        iommu_tbl_range_free(&iommu->tbl, bus_addr, npages, DMA_ERROR_CODE);
 }
 
+static int dma_4v_map_sg_bypass(struct device *dev, struct scatterlist *sglist,
+                               int nelems, enum dma_data_direction direction,
+                               struct dma_attrs *attrs)
+{
+       struct pci_pbm_info *pbm;
+       unsigned long devhandle;
+       unsigned long ra;
+       unsigned long prot;
+       unsigned long dma_addr;
+       struct scatterlist *s;
+       int i;
+
+       BUG_ON(!dev);
+       pbm = dev->archdata.host_controller;
+       BUG_ON(!pbm);
+       devhandle = pbm->devhandle;
+
+       if (unlikely(direction == DMA_NONE))
+               goto bad;
+
+       prot = HV_PCI_MAP_ATTR_READ;
+
+       if (direction != DMA_TO_DEVICE)
+               prot |= HV_PCI_MAP_ATTR_WRITE;
+
+       for_each_sg(sglist, s, nelems, i) {
+               ra = (unsigned long) SG_ENT_PHYS_ADDRESS(s);
+               if (pci_sun4v_iommu_getbypass(devhandle, ra, prot, &dma_addr))
+                       goto bad;
+               s->dma_address = dma_addr;
+               s->dma_length = s->length;
+       }
+
+       return nelems;
+
+bad:
+       return 0;
+}
+
 static int dma_4v_map_sg(struct device *dev, struct scatterlist *sglist,
                         int nelems, enum dma_data_direction direction,
                         struct dma_attrs *attrs)
@@ -334,6 +437,9 @@ static int dma_4v_map_sg(struct device *dev, struct scatterlist *sglist,
        unsigned long base_shift;
        long err;
 
+       return dma_4v_map_sg_bypass(dev, sglist, nelems,
+                                           direction, attrs);
+
        BUG_ON(direction == DMA_NONE);
 
        iommu = dev->archdata.iommu;
@@ -474,6 +580,9 @@ static void dma_4v_unmap_sg(struct device *dev, struct scatterlist *sglist,
        unsigned long flags, entry;
        u32 devhandle;
 
+       /* no need to un-map bypass dma address */
+       return;
+
        BUG_ON(direction == DMA_NONE);
 
        iommu = dev->archdata.iommu;
index 5642212390b2ea7911cea77b1fcc2f6c133f882b..1ef586d36b586975ce48cdbca68f9c64874cfdfe 100644 (file)
@@ -18,6 +18,10 @@ unsigned long pci_sun4v_iommu_getmap(unsigned long devhandle,
                                     unsigned long tsbid,
                                     unsigned long *io_attributes,
                                     unsigned long *real_address);
+extern unsigned long pci_sun4v_iommu_getbypass(unsigned long dev_hdl,
+                                              unsigned long ra,
+                                              unsigned long attr,
+                                              unsigned long *io_addr_p);
 unsigned long pci_sun4v_config_get(unsigned long devhandle,
                                   unsigned long pci_device,
                                   unsigned long config_offset,
index e606d46c68159a9c773dfd571fb60b6659882055..98d11ed76d129d4f6beea755af4e5a6c1a2c382a 100644 (file)
@@ -56,6 +56,24 @@ ENTRY(pci_sun4v_iommu_getmap)
         mov    %o0, %o0
 ENDPROC(pci_sun4v_iommu_getmap)
 
+
+       /* %o0: devhandle
+        * %o1: r_addr
+        * %o2: io_attributes
+        * %o3: &io_addr
+        *
+        * returns: %o0: status
+        */
+ENTRY(pci_sun4v_iommu_getbypass)
+       mov     HV_FAST_PCI_IOMMU_GETBYPASS, %o5
+       ta      HV_FAST_TRAP
+       brnz    %o0, 1f
+       stx     %o1, [%o3]
+1:     retl
+       nop
+ENDPROC(pci_sun4v_iommu_getbypass)
+
+
        /* %o0: devhandle
         * %o1: pci_device
         * %o2: pci_config_offset