crypto: gcm - Add RFC4543 wrapper for GCM
[safe/jmp/linux-2.6] / crypto / async_tx / async_xor.c
index 691fa98..079ae8c 100644 (file)
 /* do_async_xor - dma map the pages and perform the xor with an engine */
 static __async_inline struct dma_async_tx_descriptor *
 do_async_xor(struct dma_chan *chan, struct page *dest, struct page **src_list,
-            unsigned int offset, int src_cnt, size_t len,
+            unsigned int offset, int src_cnt, size_t len, dma_addr_t *dma_src,
             struct async_submit_ctl *submit)
 {
        struct dma_device *dma = chan->device;
-       dma_addr_t *dma_src = (dma_addr_t *) src_list;
        struct dma_async_tx_descriptor *tx = NULL;
        int src_off = 0;
        int i;
@@ -45,31 +44,35 @@ do_async_xor(struct dma_chan *chan, struct page *dest, struct page **src_list,
        void *cb_param_orig = submit->cb_param;
        enum async_tx_flags flags_orig = submit->flags;
        enum dma_ctrl_flags dma_flags;
-       int xor_src_cnt;
+       int xor_src_cnt = 0;
        dma_addr_t dma_dest;
 
        /* 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 (!src_list[i])
+                       continue;
                if (unlikely(src_list[i] == dest)) {
-                       dma_src[i] = dma_dest;
+                       dma_src[xor_src_cnt++] = dma_dest;
                        continue;
                }
-               dma_src[i] = dma_map_page(dma->dev, src_list[i], offset,
-                                         len, DMA_TO_DEVICE);
+               dma_src[xor_src_cnt++] = dma_map_page(dma->dev, src_list[i], offset,
+                                                     len, DMA_TO_DEVICE);
        }
+       src_cnt = xor_src_cnt;
 
        while (src_cnt) {
                submit->flags = flags_orig;
                dma_flags = 0;
-               xor_src_cnt = min(src_cnt, dma->max_xor);
+               xor_src_cnt = min(src_cnt, (int)dma->max_xor);
                /* if we are submitting additional xors, leave the chain open,
                 * clear the callback parameters, and leave the destination
                 * buffer mapped
                 */
                if (src_cnt > xor_src_cnt) {
                        submit->flags &= ~ASYNC_TX_ACK;
+                       submit->flags |= ASYNC_TX_FENCE;
                        dma_flags = DMA_COMPL_SKIP_DEST_UNMAP;
                        submit->cb_fn = NULL;
                        submit->cb_param = NULL;
@@ -79,7 +82,8 @@ do_async_xor(struct dma_chan *chan, struct page *dest, struct page **src_list,
                }
                if (submit->cb_fn)
                        dma_flags |= DMA_PREP_INTERRUPT;
-
+               if (submit->flags & ASYNC_TX_FENCE)
+                       dma_flags |= DMA_PREP_FENCE;
                /* Since we have clobbered the src_list we are committed
                 * to doing this asynchronously.  Drivers force forward progress
                 * in case they can not provide a descriptor
@@ -122,15 +126,21 @@ do_sync_xor(struct page *dest, struct page **src_list, unsigned int offset,
            int src_cnt, size_t len, struct async_submit_ctl *submit)
 {
        int i;
-       int xor_src_cnt;
+       int xor_src_cnt = 0;
        int src_off = 0;
        void *dest_buf;
-       void **srcs = (void **) src_list;
+       void **srcs;
 
-       /* reuse the 'src_list' array to convert to buffer pointers */
-       for (i = 0; i < src_cnt; i++)
-               srcs[i] = page_address(src_list[i]) + offset;
+       if (submit->scribble)
+               srcs = submit->scribble;
+       else
+               srcs = (void **) src_list;
 
+       /* convert to buffer pointers */
+       for (i = 0; i < src_cnt; i++)
+               if (src_list[i])
+                       srcs[xor_src_cnt++] = page_address(src_list[i]) + offset;
+       src_cnt = xor_src_cnt;
        /* set destination address */
        dest_buf = page_address(dest) + offset;
 
@@ -178,17 +188,26 @@ async_xor(struct page *dest, struct page **src_list, unsigned int offset,
        struct dma_chan *chan = async_tx_find_channel(submit, DMA_XOR,
                                                      &dest, 1, src_list,
                                                      src_cnt, len);
+       dma_addr_t *dma_src = NULL;
+
        BUG_ON(src_cnt <= 1);
 
-       if (chan) {
+       if (submit->scribble)
+               dma_src = submit->scribble;
+       else if (sizeof(dma_addr_t) <= sizeof(struct page *))
+               dma_src = (dma_addr_t *) src_list;
+
+       if (dma_src && chan && is_dma_xor_aligned(chan->device, offset, 0, len)) {
                /* run the xor asynchronously */
                pr_debug("%s (async): len: %zu\n", __func__, len);
 
                return do_async_xor(chan, dest, src_list, offset, src_cnt, len,
-                                   submit);
+                                   dma_src, submit);
        } else {
                /* run the xor synchronously */
                pr_debug("%s (sync): len: %zu\n", __func__, len);
+               WARN_ONCE(chan, "%s: no space for dma address conversion\n",
+                         __func__);
 
                /* in the sync case the dest is an implied source
                 * (assumes the dest is the first source)
@@ -215,6 +234,17 @@ static int page_is_zero(struct page *p, unsigned int offset, size_t len)
                memcmp(a, a + 4, len - 4) == 0);
 }
 
+static inline struct dma_chan *
+xor_val_chan(struct async_submit_ctl *submit, struct page *dest,
+                struct page **src_list, int src_cnt, size_t len)
+{
+       #ifdef CONFIG_ASYNC_TX_DISABLE_XOR_VAL_DMA
+       return NULL;
+       #endif
+       return async_tx_find_channel(submit, DMA_XOR_VAL, &dest, 1, src_list,
+                                    src_cnt, len);
+}
+
 /**
  * async_xor_val - attempt a xor parity check with a dma engine.
  * @dest: destination page used if the xor is performed synchronously
@@ -233,25 +263,32 @@ static int page_is_zero(struct page *p, unsigned int offset, size_t len)
  */
 struct dma_async_tx_descriptor *
 async_xor_val(struct page *dest, struct page **src_list, unsigned int offset,
-             int src_cnt, size_t len, u32 *result,
+             int src_cnt, size_t len, enum sum_check_flags *result,
              struct async_submit_ctl *submit)
 {
-       struct dma_chan *chan = async_tx_find_channel(submit, DMA_XOR_VAL,
-                                                     &dest, 1, src_list,
-                                                     src_cnt, len);
+       struct dma_chan *chan = xor_val_chan(submit, dest, src_list, src_cnt, len);
        struct dma_device *device = chan ? chan->device : NULL;
        struct dma_async_tx_descriptor *tx = NULL;
+       dma_addr_t *dma_src = NULL;
 
        BUG_ON(src_cnt <= 1);
 
-       if (device && src_cnt <= device->max_xor) {
-               dma_addr_t *dma_src = (dma_addr_t *) src_list;
-               unsigned long dma_prep_flags;
+       if (submit->scribble)
+               dma_src = submit->scribble;
+       else if (sizeof(dma_addr_t) <= sizeof(struct page *))
+               dma_src = (dma_addr_t *) src_list;
+
+       if (dma_src && device && src_cnt <= device->max_xor &&
+           is_dma_xor_aligned(device, offset, 0, len)) {
+               unsigned long dma_prep_flags = 0;
                int i;
 
                pr_debug("%s: (async) len: %zu\n", __func__, len);
 
-               dma_prep_flags = submit->cb_fn ? DMA_PREP_INTERRUPT : 0;
+               if (submit->cb_fn)
+                       dma_prep_flags |= DMA_PREP_INTERRUPT;
+               if (submit->flags & ASYNC_TX_FENCE)
+                       dma_prep_flags |= DMA_PREP_FENCE;
                for (i = 0; i < src_cnt; i++)
                        dma_src[i] = dma_map_page(device->dev, src_list[i],
                                                  offset, len, DMA_TO_DEVICE);
@@ -275,6 +312,9 @@ async_xor_val(struct page *dest, struct page **src_list, unsigned int offset,
                enum async_tx_flags flags_orig = submit->flags;
 
                pr_debug("%s: (sync) len: %zu\n", __func__, len);
+               WARN_ONCE(device && src_cnt <= device->max_xor,
+                         "%s: no space for dma address conversion\n",
+                         __func__);
 
                submit->flags |= ASYNC_TX_XOR_DROP_DST;
                submit->flags &= ~ASYNC_TX_ACK;
@@ -283,7 +323,7 @@ async_xor_val(struct page *dest, struct page **src_list, unsigned int offset,
 
                async_tx_quiesce(&tx);
 
-               *result = page_is_zero(dest, offset, len) ? 0 : 1;
+               *result = !page_is_zero(dest, offset, len) << SUM_CHECK_P;
 
                async_tx_sync_epilog(submit);
                submit->flags = flags_orig;
@@ -293,29 +333,6 @@ async_xor_val(struct page *dest, struct page **src_list, unsigned int offset,
 }
 EXPORT_SYMBOL_GPL(async_xor_val);
 
-static int __init async_xor_init(void)
-{
-       #ifdef CONFIG_DMA_ENGINE
-       /* To conserve stack space the input src_list (array of page pointers)
-        * is reused to hold the array of dma addresses passed to the driver.
-        * This conversion is only possible when dma_addr_t is less than the
-        * the size of a pointer.  HIGHMEM64G is known to violate this
-        * assumption.
-        */
-       BUILD_BUG_ON(sizeof(dma_addr_t) > sizeof(struct page *));
-       #endif
-
-       return 0;
-}
-
-static void __exit async_xor_exit(void)
-{
-       do { } while (0);
-}
-
-module_init(async_xor_init);
-module_exit(async_xor_exit);
-
 MODULE_AUTHOR("Intel Corporation");
 MODULE_DESCRIPTION("asynchronous xor/xor-zero-sum api");
 MODULE_LICENSE("GPL");