linux/crypto/async_tx/async_xor.c
<<
>>
Prefs
   1/*
   2 * xor offload engine api
   3 *
   4 * Copyright \xC2\xA9 2006, Intel Corporation.
   5 *
   6 *      Dan Williams <dan.j.williams@intel.com>
   7 *
   8 *      with architecture considerations by:
   9 *      Neil Brown <neilb@suse.de>
  10 *      Jeff Garzik <jeff@garzik.org>
  11 *
  12 * This program is free software; you can redistribute it and/or modify it
  13 * under the terms and conditions of the GNU General Public License,
  14 * version 2, as published by the Free Software Foundation.
  15 *
  16 * This program is distributed in the hope it will be useful, but WITHOUT
  17 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  18 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
  19 * more details.
  20 *
  21 * You should have received a copy of the GNU General Public License along with
  22 * this program; if not, write to the Free Software Foundation, Inc.,
  23 * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
  24 *
  25 */
  26#include <linux/kernel.h>
  27#include <linux/interrupt.h>
  28#include <linux/mm.h>
  29#include <linux/dma-mapping.h>
  30#include <linux/raid/xor.h>
  31#include <linux/async_tx.h>
  32
  33static void
  34do_async_xor(struct dma_async_tx_descriptor *tx, struct dma_device *device,
  35        struct dma_chan *chan, struct page *dest, struct page **src_list,
  36        unsigned int offset, unsigned int src_cnt, size_t len,
  37        enum async_tx_flags flags, struct dma_async_tx_descriptor *depend_tx,
  38        dma_async_tx_callback cb_fn, void *cb_param)
  39{
  40        dma_addr_t dma_addr;
  41        enum dma_data_direction dir;
  42        int i;
  43
  44        pr_debug("%s: len: %zu\n", __FUNCTION__, len);
  45
  46        dir = (flags & ASYNC_TX_ASSUME_COHERENT) ?
  47                DMA_NONE : DMA_FROM_DEVICE;
  48
  49        dma_addr = dma_map_page(device->dev, dest, offset, len, dir);
  50        tx->tx_set_dest(dma_addr, tx, 0);
  51
  52        dir = (flags & ASYNC_TX_ASSUME_COHERENT) ?
  53                DMA_NONE : DMA_TO_DEVICE;
  54
  55        for (i = 0; i < src_cnt; i++) {
  56                dma_addr = dma_map_page(device->dev, src_list[i],
  57                        offset, len, dir);
  58                tx->tx_set_src(dma_addr, tx, i);
  59        }
  60
  61        async_tx_submit(chan, tx, flags, depend_tx, cb_fn, cb_param);
  62}
  63
  64static void
  65do_sync_xor(struct page *dest, struct page **src_list, unsigned int offset,
  66        unsigned int src_cnt, size_t len, enum async_tx_flags flags,
  67        struct dma_async_tx_descriptor *depend_tx,
  68        dma_async_tx_callback cb_fn, void *cb_param)
  69{
  70        void *_dest;
  71        int i;
  72
  73        pr_debug("%s: len: %zu\n", __FUNCTION__, len);
  74
  75        /* reuse the 'src_list' array to convert to buffer pointers */
  76        for (i = 0; i < src_cnt; i++)
  77                src_list[i] = (struct page *)
  78                        (page_address(src_list[i]) + offset);
  79
  80        /* set destination address */
  81        _dest = page_address(dest) + offset;
  82
  83        if (flags & ASYNC_TX_XOR_ZERO_DST)
  84                memset(_dest, 0, len);
  85
  86        xor_blocks(src_cnt, len, _dest,
  87                (void **) src_list);
  88
  89        async_tx_sync_epilog(flags, depend_tx, cb_fn, cb_param);
  90}
  91
  92/**
  93 * async_xor - attempt to xor a set of blocks with a dma engine.
  94 *      xor_blocks always uses the dest as a source so the ASYNC_TX_XOR_ZERO_DST
  95 *      flag must be set to not include dest data in the calculation.  The
  96 *      assumption with dma eninges is that they only use the destination
  97 *      buffer as a source when it is explicity specified in the source list.
  98 * @dest: destination page
  99 * @src_list: array of source pages (if the dest is also a source it must be
 100 *      at index zero).  The contents of this array may be overwritten.
 101 * @offset: offset in pages to start transaction
 102 * @src_cnt: number of source pages
 103 * @len: length in bytes
 104 * @flags: ASYNC_TX_XOR_ZERO_DST, ASYNC_TX_XOR_DROP_DEST,
 105 *      ASYNC_TX_ASSUME_COHERENT, ASYNC_TX_ACK, ASYNC_TX_DEP_ACK
 106 * @depend_tx: xor depends on the result of this transaction.
 107 * @cb_fn: function to call when the xor completes
 108 * @cb_param: parameter to pass to the callback routine
 109 */
 110struct dma_async_tx_descriptor *
 111async_xor(struct page *dest, struct page **src_list, unsigned int offset,
 112        int src_cnt, size_t len, enum async_tx_flags flags,
 113        struct dma_async_tx_descriptor *depend_tx,
 114        dma_async_tx_callback cb_fn, void *cb_param)
 115{
 116        struct dma_chan *chan = async_tx_find_channel(depend_tx, DMA_XOR);
 117        struct dma_device *device = chan ? chan->device : NULL;
 118        struct dma_async_tx_descriptor *tx = NULL;
 119        dma_async_tx_callback _cb_fn;
 120        void *_cb_param;
 121        unsigned long local_flags;
 122        int xor_src_cnt;
 123        int i = 0, src_off = 0, int_en;
 124
 125        BUG_ON(src_cnt <= 1);
 126
 127        while (src_cnt) {
 128                local_flags = flags;
 129                if (device) { /* run the xor asynchronously */
 130                        xor_src_cnt = min(src_cnt, device->max_xor);
 131                        /* if we are submitting additional xors
 132                         * only set the callback on the last transaction
 133                         */
 134                        if (src_cnt > xor_src_cnt) {
 135                                local_flags &= ~ASYNC_TX_ACK;
 136                                _cb_fn = NULL;
 137                                _cb_param = NULL;
 138                        } else {
 139                                _cb_fn = cb_fn;
 140                                _cb_param = cb_param;
 141                        }
 142
 143                        int_en = _cb_fn ? 1 : 0;
 144
 145                        tx = device->device_prep_dma_xor(
 146                                chan, xor_src_cnt, len, int_en);
 147
 148                        if (tx) {
 149                                do_async_xor(tx, device, chan, dest,
 150                                &src_list[src_off], offset, xor_src_cnt, len,
 151                                local_flags, depend_tx, _cb_fn,
 152                                _cb_param);
 153                        } else /* fall through */
 154                                goto xor_sync;
 155                } else { /* run the xor synchronously */
 156xor_sync:
 157                        /* in the sync case the dest is an implied source
 158                         * (assumes the dest is at the src_off index)
 159                         */
 160                        if (flags & ASYNC_TX_XOR_DROP_DST) {
 161                                src_cnt--;
 162                                src_off++;
 163                        }
 164
 165                        /* process up to 'MAX_XOR_BLOCKS' sources */
 166                        xor_src_cnt = min(src_cnt, MAX_XOR_BLOCKS);
 167
 168                        /* if we are submitting additional xors
 169                         * only set the callback on the last transaction
 170                         */
 171                        if (src_cnt > xor_src_cnt) {
 172                                local_flags &= ~ASYNC_TX_ACK;
 173                                _cb_fn = NULL;
 174                                _cb_param = NULL;
 175                        } else {
 176                                _cb_fn = cb_fn;
 177                                _cb_param = cb_param;
 178                        }
 179
 180                        /* wait for any prerequisite operations */
 181                        if (depend_tx) {
 182                                /* if ack is already set then we cannot be sure
 183                                 * we are referring to the correct operation
 184                                 */
 185                                BUG_ON(depend_tx->ack);
 186                                if (dma_wait_for_async_tx(depend_tx) ==
 187                                        DMA_ERROR)
 188                                        panic("%s: DMA_ERROR waiting for "
 189                                                "depend_tx\n",
 190                                                __FUNCTION__);
 191                        }
 192
 193                        do_sync_xor(dest, &src_list[src_off], offset,
 194                                xor_src_cnt, len, local_flags, depend_tx,
 195                                _cb_fn, _cb_param);
 196                }
 197
 198                /* the previous tx is hidden from the client,
 199                 * so ack it
 200                 */
 201                if (i && depend_tx)
 202                        async_tx_ack(depend_tx);
 203
 204                depend_tx = tx;
 205
 206                if (src_cnt > xor_src_cnt) {
 207                        /* drop completed sources */
 208                        src_cnt -= xor_src_cnt;
 209                        src_off += xor_src_cnt;
 210
 211                        /* unconditionally preserve the destination */
 212                        flags &= ~ASYNC_TX_XOR_ZERO_DST;
 213
 214                        /* use the intermediate result a source, but remember
 215                         * it's dropped, because it's implied, in the sync case
 216                         */
 217                        src_list[--src_off] = dest;
 218                        src_cnt++;
 219                        flags |= ASYNC_TX_XOR_DROP_DST;
 220                } else
 221                        src_cnt = 0;
 222                i++;
 223        }
 224
 225        return tx;
 226}
 227EXPORT_SYMBOL_GPL(async_xor);
 228
 229static int page_is_zero(struct page *p, unsigned int offset, size_t len)
 230{
 231        char *a = page_address(p) + offset;
 232        return ((*(u32 *) a) == 0 &&
 233                memcmp(a, a + 4, len - 4) == 0);
 234}
 235
 236/**
 237 * async_xor_zero_sum - attempt a xor parity check with a dma engine.
 238 * @dest: destination page used if the xor is performed synchronously
 239 * @src_list: array of source pages.  The dest page must be listed as a source
 240 *      at index zero.  The contents of this array may be overwritten.
 241 * @offset: offset in pages to start transaction
 242 * @src_cnt: number of source pages
 243 * @len: length in bytes
 244 * @result: 0 if sum == 0 else non-zero
 245 * @flags: ASYNC_TX_ASSUME_COHERENT, ASYNC_TX_ACK, ASYNC_TX_DEP_ACK
 246 * @depend_tx: xor depends on the result of this transaction.
 247 * @cb_fn: function to call when the xor completes
 248 * @cb_param: parameter to pass to the callback routine
 249 */
 250struct dma_async_tx_descriptor *
 251async_xor_zero_sum(struct page *dest, struct page **src_list,
 252        unsigned int offset, int src_cnt, size_t len,
 253        u32 *result, enum async_tx_flags flags,
 254        struct dma_async_tx_descriptor *depend_tx,
 255        dma_async_tx_callback cb_fn, void *cb_param)
 256{
 257        struct dma_chan *chan = async_tx_find_channel(depend_tx, DMA_ZERO_SUM);
 258        struct dma_device *device = chan ? chan->device : NULL;
 259        int int_en = cb_fn ? 1 : 0;
 260        struct dma_async_tx_descriptor *tx = device ?
 261                device->device_prep_dma_zero_sum(chan, src_cnt, len, result,
 262                        int_en) : NULL;
 263        int i;
 264
 265        BUG_ON(src_cnt <= 1);
 266
 267        if (tx) {
 268                dma_addr_t dma_addr;
 269                enum dma_data_direction dir;
 270
 271                pr_debug("%s: (async) len: %zu\n", __FUNCTION__, len);
 272
 273                dir = (flags & ASYNC_TX_ASSUME_COHERENT) ?
 274                        DMA_NONE : DMA_TO_DEVICE;
 275
 276                for (i = 0; i < src_cnt; i++) {
 277                        dma_addr = dma_map_page(device->dev, src_list[i],
 278                                offset, len, dir);
 279                        tx->tx_set_src(dma_addr, tx, i);
 280                }
 281
 282                async_tx_submit(chan, tx, flags, depend_tx, cb_fn, cb_param);
 283        } else {
 284                unsigned long xor_flags = flags;
 285
 286                pr_debug("%s: (sync) len: %zu\n", __FUNCTION__, len);
 287
 288                xor_flags |= ASYNC_TX_XOR_DROP_DST;
 289                xor_flags &= ~ASYNC_TX_ACK;
 290
 291                tx = async_xor(dest, src_list, offset, src_cnt, len, xor_flags,
 292                        depend_tx, NULL, NULL);
 293
 294                if (tx) {
 295                        if (dma_wait_for_async_tx(tx) == DMA_ERROR)
 296                                panic("%s: DMA_ERROR waiting for tx\n",
 297                                        __FUNCTION__);
 298                        async_tx_ack(tx);
 299                }
 300
 301                *result = page_is_zero(dest, offset, len) ? 0 : 1;
 302
 303                tx = NULL;
 304
 305                async_tx_sync_epilog(flags, depend_tx, cb_fn, cb_param);
 306        }
 307
 308        return tx;
 309}
 310EXPORT_SYMBOL_GPL(async_xor_zero_sum);
 311
 312static int __init async_xor_init(void)
 313{
 314        return 0;
 315}
 316
 317static void __exit async_xor_exit(void)
 318{
 319        do { } while (0);
 320}
 321
 322module_init(async_xor_init);
 323module_exit(async_xor_exit);
 324
 325MODULE_AUTHOR("Intel Corporation");
 326MODULE_DESCRIPTION("asynchronous xor/xor-zero-sum api");
 327MODULE_LICENSE("GPL");
 328