async_xor: dma_map destination DMA_BIDIRECTIONAL
authorDan Williams <dan.j.williams@intel.com>
Mon, 8 Dec 2008 20:46:00 +0000 (13:46 -0700)
committerDan Williams <dan.j.williams@intel.com>
Mon, 8 Dec 2008 20:46:00 +0000 (13:46 -0700)
Mapping the destination multiple times is a misuse of the dma-api.
Since the destination may be reused as a source, ensure that it is only
mapped once and that it is mapped bidirectionally.  This appears to add
ugliness on the unmap side in that it always reads back the destination
address from the descriptor, but gcc can determine that dma_unmap is a
nop and not emit the code that calculates its arguments.

Cc: <stable@kernel.org>
Cc: Saeed Bishara <saeed@marvell.com>
Acked-by: Yuri Tikhonov <yur@emcraft.com>
Signed-off-by: Dan Williams <dan.j.williams@intel.com>
crypto/async_tx/async_xor.c
drivers/dma/iop-adma.c
drivers/dma/mv_xor.c

index c029d3eb9ef0a76ed248deb4a40e47511ba12afe..595b78672b36ad90e27cf50ed28126bb43970fb5 100644 (file)
@@ -53,10 +53,17 @@ do_async_xor(struct dma_chan *chan, struct page *dest, struct page **src_list,
        int xor_src_cnt;
        dma_addr_t dma_dest;
 
-       dma_dest = dma_map_page(dma->dev, dest, offset, len, DMA_FROM_DEVICE);
-       for (i = 0; i < src_cnt; i++)
+       /* map the dest bidrectional in case it is re-used as a source */
+       dma_dest = dma_map_page(dma->dev, dest, offset, len, DMA_BIDIRECTIONAL);
+       for (i = 0; i < src_cnt; i++) {
+               /* only map the dest once */
+               if (unlikely(src_list[i] == dest)) {
+                       dma_src[i] = dma_dest;
+                       continue;
+               }
                dma_src[i] = dma_map_page(dma->dev, src_list[i], offset,
                                          len, DMA_TO_DEVICE);
+       }
 
        while (src_cnt) {
                async_flags = flags;
index c7a9306d951d2c61f622c601ef2d49af422b679e..6be3172622009ff67ec502b049f167d6ccebe1cd 100644 (file)
@@ -85,18 +85,28 @@ iop_adma_run_tx_complete_actions(struct iop_adma_desc_slot *desc,
                        enum dma_ctrl_flags flags = desc->async_tx.flags;
                        u32 src_cnt;
                        dma_addr_t addr;
+                       dma_addr_t dest;
 
+                       src_cnt = unmap->unmap_src_cnt;
+                       dest = iop_desc_get_dest_addr(unmap, iop_chan);
                        if (!(flags & DMA_COMPL_SKIP_DEST_UNMAP)) {
-                               addr = iop_desc_get_dest_addr(unmap, iop_chan);
-                               dma_unmap_page(dev, addr, len, DMA_FROM_DEVICE);
+                               enum dma_data_direction dir;
+
+                               if (src_cnt > 1) /* is xor? */
+                                       dir = DMA_BIDIRECTIONAL;
+                               else
+                                       dir = DMA_FROM_DEVICE;
+
+                               dma_unmap_page(dev, dest, len, dir);
                        }
 
                        if (!(flags & DMA_COMPL_SKIP_SRC_UNMAP)) {
-                               src_cnt = unmap->unmap_src_cnt;
                                while (src_cnt--) {
                                        addr = iop_desc_get_src_addr(unmap,
                                                                     iop_chan,
                                                                     src_cnt);
+                                       if (addr == dest)
+                                               continue;
                                        dma_unmap_page(dev, addr, len,
                                                       DMA_TO_DEVICE);
                                }
index 0328da020a1084e6c7f0fcfd0e729a00e079702d..bcda17426411ea6f4c311aa5b077a1e91e906b63 100644 (file)
@@ -311,17 +311,26 @@ mv_xor_run_tx_complete_actions(struct mv_xor_desc_slot *desc,
                        enum dma_ctrl_flags flags = desc->async_tx.flags;
                        u32 src_cnt;
                        dma_addr_t addr;
+                       dma_addr_t dest;
 
+                       src_cnt = unmap->unmap_src_cnt;
+                       dest = mv_desc_get_dest_addr(unmap);
                        if (!(flags & DMA_COMPL_SKIP_DEST_UNMAP)) {
-                               addr = mv_desc_get_dest_addr(unmap);
-                               dma_unmap_page(dev, addr, len, DMA_FROM_DEVICE);
+                               enum dma_data_direction dir;
+
+                               if (src_cnt > 1) /* is xor ? */
+                                       dir = DMA_BIDIRECTIONAL;
+                               else
+                                       dir = DMA_FROM_DEVICE;
+                               dma_unmap_page(dev, dest, len, dir);
                        }
 
                        if (!(flags & DMA_COMPL_SKIP_SRC_UNMAP)) {
-                               src_cnt = unmap->unmap_src_cnt;
                                while (src_cnt--) {
                                        addr = mv_desc_get_src_addr(unmap,
                                                                    src_cnt);
+                                       if (addr == dest)
+                                               continue;
                                        dma_unmap_page(dev, addr, len,
                                                       DMA_TO_DEVICE);
                                }