bnx2x: fix DMA API usage
authorMichal Schmidt <mschmidt@redhat.com>
Fri, 26 Jun 2015 15:50:00 +0000 (17:50 +0200)
committerDavid S. Miller <davem@davemloft.net>
Mon, 29 Jun 2015 03:25:01 +0000 (20:25 -0700)
With CONFIG_DMA_API_DEBUG=y bnx2x triggers the error "DMA-API: device
driver frees DMA memory with wrong function".
On archs where PAGE_SIZE > SGE_PAGE_SIZE it also triggers "DMA-API:
device driver frees DMA memory with different size".

Fix this by making the mapping and unmapping symmetric:
 - Do not map the whole pool page at once. Instead map the
   SGE_PAGE_SIZE-sized pieces individually, so they can be unmapped in
   the same manner.
 - What's mapped using dma_map_page() must be unmapped using
   dma_unmap_page().

Tested on ppc64.

Fixes: 4cace675d687 ("bnx2x: Alloc 4k fragment for each rx ring buffer element")
Signed-off-by: Michal Schmidt <mschmidt@redhat.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/ethernet/broadcom/bnx2x/bnx2x.h
drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c
drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.h

index b4bfdd29fb54e5d8dc23c4ceaec6cfe274bb2b53..cd4ae76bbff2f8acda89154e65cf699e141553e5 100644 (file)
@@ -530,7 +530,6 @@ enum bnx2x_tpa_mode_t {
 
 struct bnx2x_alloc_pool {
        struct page     *page;
-       dma_addr_t      dma;
        unsigned int    offset;
 };
 
index 45e7c65f411f60c7ea56b802d134393565d50583..a90d7364334f9dfa3687dc813e068508a342861c 100644 (file)
@@ -563,23 +563,20 @@ static int bnx2x_alloc_rx_sge(struct bnx2x *bp, struct bnx2x_fastpath *fp,
                        return -ENOMEM;
                }
 
-               pool->dma = dma_map_page(&bp->pdev->dev, pool->page, 0,
-                                        PAGE_SIZE, DMA_FROM_DEVICE);
-               if (unlikely(dma_mapping_error(&bp->pdev->dev,
-                                              pool->dma))) {
-                       __free_pages(pool->page, PAGES_PER_SGE_SHIFT);
-                       pool->page = NULL;
-                       BNX2X_ERR("Can't map sge\n");
-                       return -ENOMEM;
-               }
                pool->offset = 0;
        }
 
+       mapping = dma_map_page(&bp->pdev->dev, pool->page,
+                              pool->offset, SGE_PAGE_SIZE, DMA_FROM_DEVICE);
+       if (unlikely(dma_mapping_error(&bp->pdev->dev, mapping))) {
+               BNX2X_ERR("Can't map sge\n");
+               return -ENOMEM;
+       }
+
        get_page(pool->page);
        sw_buf->page = pool->page;
        sw_buf->offset = pool->offset;
 
-       mapping = pool->dma + sw_buf->offset;
        dma_unmap_addr_set(sw_buf, mapping, mapping);
 
        sge->addr_hi = cpu_to_le32(U64_HI(mapping));
@@ -648,9 +645,9 @@ static int bnx2x_fill_frag_skb(struct bnx2x *bp, struct bnx2x_fastpath *fp,
                        return err;
                }
 
-               dma_unmap_single(&bp->pdev->dev,
-                                dma_unmap_addr(&old_rx_pg, mapping),
-                                SGE_PAGE_SIZE, DMA_FROM_DEVICE);
+               dma_unmap_page(&bp->pdev->dev,
+                              dma_unmap_addr(&old_rx_pg, mapping),
+                              SGE_PAGE_SIZE, DMA_FROM_DEVICE);
                /* Add one frag and update the appropriate fields in the skb */
                if (fp->mode == TPA_MODE_LRO)
                        skb_fill_page_desc(skb, j, old_rx_pg.page,
index 2b30081ec26d128ec86c602eb5a097c77c664159..03b7404d5b9ba59c5470fe36ec0746d6b75f7eee 100644 (file)
@@ -807,8 +807,8 @@ static inline void bnx2x_free_rx_sge(struct bnx2x *bp,
        /* Since many fragments can share the same page, make sure to
         * only unmap and free the page once.
         */
-       dma_unmap_single(&bp->pdev->dev, dma_unmap_addr(sw_buf, mapping),
-                        SGE_PAGE_SIZE, DMA_FROM_DEVICE);
+       dma_unmap_page(&bp->pdev->dev, dma_unmap_addr(sw_buf, mapping),
+                      SGE_PAGE_SIZE, DMA_FROM_DEVICE);
 
        put_page(page);
 
@@ -974,14 +974,6 @@ static inline void bnx2x_free_rx_mem_pool(struct bnx2x *bp,
        if (!pool->page)
                return;
 
-       /* Page was not fully fragmented.  Unmap unused space */
-       if (pool->offset < PAGE_SIZE) {
-               dma_addr_t dma = pool->dma + pool->offset;
-               int size = PAGE_SIZE - pool->offset;
-
-               dma_unmap_single(&bp->pdev->dev, dma, size, DMA_FROM_DEVICE);
-       }
-
        put_page(pool->page);
 
        pool->page = NULL;