From: Dan Williams <[EMAIL PROTECTED]>

async_tx is an api to describe a series of bulk memory
transfers/transforms.  When possible these transactions are carried out by
asynchrounous dma engines.  The api handles inter-transaction dependencies
and hides dma channel management from the client.  When a dma engine is not
present the transaction is carried out via synchronous software routines.

Xor operations are handled by async_tx, to this end xor.c is moved into
drivers/dma and is changed to take an explicit destination address and
a series of sources to match the hardware engine implementation.

When CONFIG_DMA_ENGINE is not set the asynchrounous path is compiled away.

Changelog:
* fixed a leftover debug print
* don't allow callbacks in async_interrupt_cond
* fixed xor_block changes
* fixed usage of ASYNC_TX_XOR_DROP_DEST

Signed-off-by: Dan Williams <[EMAIL PROTECTED]>
---

 drivers/Makefile         |    1 
 drivers/dma/Kconfig      |   16 +
 drivers/dma/Makefile     |    1 
 drivers/dma/async_tx.c   |  910 ++++++++++++++++++++++++++++++++++++++++++++++
 drivers/dma/xor.c        |  153 ++++++++
 drivers/md/Kconfig       |    2 
 drivers/md/Makefile      |    6 
 drivers/md/raid5.c       |   52 +--
 drivers/md/xor.c         |  154 --------
 include/linux/async_tx.h |  180 +++++++++
 include/linux/raid/xor.h |    5 
 11 files changed, 1291 insertions(+), 189 deletions(-)

diff --git a/drivers/Makefile b/drivers/Makefile
index 0dd96d1..7d55837 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -61,6 +61,7 @@ obj-$(CONFIG_I2C)             += i2c/
 obj-$(CONFIG_W1)               += w1/
 obj-$(CONFIG_HWMON)            += hwmon/
 obj-$(CONFIG_PHONE)            += telephony/
+obj-$(CONFIG_ASYNC_TX_DMA)     += dma/
 obj-$(CONFIG_MD)               += md/
 obj-$(CONFIG_BT)               += bluetooth/
 obj-$(CONFIG_ISDN)             += isdn/
diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig
index 30d021d..c82ed5f 100644
--- a/drivers/dma/Kconfig
+++ b/drivers/dma/Kconfig
@@ -7,8 +7,8 @@ menu "DMA Engine support"
 config DMA_ENGINE
        bool "Support for DMA engines"
        ---help---
-         DMA engines offload copy operations from the CPU to dedicated
-         hardware, allowing the copies to happen asynchronously.
+          DMA engines offload bulk memory operations from the CPU to dedicated
+          hardware, allowing the operations to happen asynchronously.
 
 comment "DMA Clients"
 
@@ -22,6 +22,17 @@ config NET_DMA
          Since this is the main user of the DMA engine, it should be enabled;
          say Y here.
 
+config ASYNC_TX_DMA
+       tristate "Asynchronous Bulk Memory Transfers/Transforms API"
+       default y
+       ---help---
+         This enables the async_tx management layer for dma engines.
+         Subsystems coded to this API will use offload engines for bulk
+         memory operations where present.  Software implementations are
+         called when a dma engine is not present or fails to allocate
+         memory to carry out the transaction.
+         Current subsystems ported to async_tx: MD_RAID4,5
+
 comment "DMA Devices"
 
 config INTEL_IOATDMA
@@ -30,5 +41,4 @@ config INTEL_IOATDMA
        default m
        ---help---
          Enable support for the Intel(R) I/OAT DMA engine.
-
 endmenu
diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile
index bdcfdbd..6a99341 100644
--- a/drivers/dma/Makefile
+++ b/drivers/dma/Makefile
@@ -1,3 +1,4 @@
 obj-$(CONFIG_DMA_ENGINE) += dmaengine.o
 obj-$(CONFIG_NET_DMA) += iovlock.o
 obj-$(CONFIG_INTEL_IOATDMA) += ioatdma.o
+obj-$(CONFIG_ASYNC_TX_DMA) += async_tx.o xor.o
diff --git a/drivers/dma/async_tx.c b/drivers/dma/async_tx.c
new file mode 100644
index 0000000..eee208d
--- /dev/null
+++ b/drivers/dma/async_tx.c
@@ -0,0 +1,910 @@
+/*
+ * Copyright(c) 2006 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called COPYING.
+ */
+#include <linux/kernel.h>
+#include <linux/interrupt.h>
+#include <linux/dma-mapping.h>
+#include <linux/raid/xor.h>
+#include <linux/async_tx.h>
+
+#define ASYNC_TX_DEBUG 0
+#define PRINTK(x...) ((void)(ASYNC_TX_DEBUG && printk(x)))
+
+#ifdef CONFIG_DMA_ENGINE
+static struct dma_client *async_api_client;
+static struct async_channel_entry async_channel_directory[] = {
+       [DMA_MEMCPY] = { .list =
+               LIST_HEAD_INIT(async_channel_directory[DMA_MEMCPY].list), },
+       [DMA_XOR] = { .list =
+               LIST_HEAD_INIT(async_channel_directory[DMA_XOR].list), },
+       [DMA_PQ_XOR] = { .list =
+               LIST_HEAD_INIT(async_channel_directory[DMA_PQ_XOR].list), },
+       [DMA_DUAL_XOR] = { .list =
+               LIST_HEAD_INIT(async_channel_directory[DMA_DUAL_XOR].list), },
+       [DMA_PQ_UPDATE] = { .list =
+               LIST_HEAD_INIT(async_channel_directory[DMA_PQ_UPDATE].list), },
+       [DMA_ZERO_SUM] = { .list =
+               LIST_HEAD_INIT(async_channel_directory[DMA_ZERO_SUM].list), },
+       [DMA_PQ_ZERO_SUM] = { .list =
+               LIST_HEAD_INIT(async_channel_directory[DMA_PQ_ZERO_SUM].list), 
},
+       [DMA_MEMSET] = { .list =
+               LIST_HEAD_INIT(async_channel_directory[DMA_MEMSET].list), },
+       [DMA_MEMCPY_CRC32C] = { .list =
+               
LIST_HEAD_INIT(async_channel_directory[DMA_MEMCPY_CRC32C].list), },
+       [DMA_INTERRUPT] = { .list =
+               LIST_HEAD_INIT(async_channel_directory[DMA_INTERRUPT].list), },
+};
+
+struct async_channel_entry async_tx_master_list = {
+       .list = LIST_HEAD_INIT(async_tx_master_list.list),
+};
+EXPORT_SYMBOL_GPL(async_tx_master_list);
+
+static void
+free_dma_chan_ref(struct rcu_head *rcu)
+{
+       struct dma_chan_ref *ref;
+       ref = container_of(rcu, struct dma_chan_ref, rcu);
+       dma_chan_put(ref->chan);
+       kfree(ref);
+}
+
+static inline void
+init_dma_chan_ref(struct dma_chan_ref *ref, struct dma_chan *chan)
+{
+       INIT_LIST_HEAD(&ref->async_node);
+       INIT_RCU_HEAD(&ref->rcu);
+       ref->chan = chan;
+}
+
+static void
+dma_channel_add_remove(struct dma_client *client,
+       struct dma_chan *chan, enum dma_event event)
+{
+       unsigned long i, flags;
+       struct dma_chan_ref *master_ref, *ref;
+       struct async_channel_entry *channel_entry;
+
+       switch (event) {
+       case DMA_RESOURCE_ADDED:
+               PRINTK("async_tx: dma resource added (capabilities: %#lx)\n",
+                       chan->device->capabilities);
+               /* add the channel to the generic management list */
+               master_ref = kmalloc(sizeof(*master_ref), GFP_KERNEL);
+               if (master_ref) {
+                       /* keep a reference until async_tx is unloaded */
+                       dma_chan_get(chan);
+                       init_dma_chan_ref(master_ref, chan);
+                       spin_lock_irqsave(&async_tx_master_list.lock, flags);
+                       list_add_tail_rcu(&master_ref->async_node,
+                               &async_tx_master_list.list);
+                       spin_unlock_irqrestore(&async_tx_master_list.lock,
+                               flags);
+               } else {
+                       printk(KERN_WARNING "async_tx: unable to create"
+                               " new master entry in response to"
+                               " a DMA_RESOURCE_ADDED event"
+                               " (-ENOMEM)\n");
+                       return;
+               }
+
+               /* add an entry for each capability of this channel */
+               dma_async_for_each_tx_type(i) {
+                       if (test_bit(i, &chan->device->capabilities))
+                               ref = kmalloc(sizeof(*ref), GFP_KERNEL);
+                       else
+                               continue;
+
+                       if (ref) {
+                               channel_entry = &async_channel_directory[i];
+                               init_dma_chan_ref(ref, chan);
+                               spin_lock_irqsave(&channel_entry->lock, flags);
+                               atomic_inc(&channel_entry->version);
+                               list_add_tail_rcu(&ref->async_node,
+                                       &channel_entry->list);
+                               spin_unlock_irqrestore(&channel_entry->lock,
+                                       flags);
+                       } else {
+                               printk(KERN_WARNING "async_tx: unable to create"
+                                       " new op-specific entry in response to"
+                                       " a DMA_RESOURCE_ADDED event"
+                                       " (-ENOMEM)\n");
+                               return;
+                       }
+               }
+               break;
+       case DMA_RESOURCE_REMOVED:
+               PRINTK("async_tx: dma resource removed (capabilities: %#lx)\n",
+                       chan->device->capabilities);
+               dma_async_for_each_tx_type(i) {
+                       if (!test_bit(i, &chan->device->capabilities))
+                               continue;
+
+                       channel_entry = &async_channel_directory[i];
+
+                       spin_lock_irqsave(&channel_entry->lock, flags);
+                       list_for_each_entry_rcu(ref, &channel_entry->list,
+                               async_node) {
+                               if (ref->chan == chan) {
+                                       atomic_inc(&channel_entry->version);
+                                       list_del_rcu(&ref->async_node);
+                                       call_rcu(&ref->rcu, free_dma_chan_ref);
+                                       break;
+                               }
+                       }
+                       spin_unlock_irqrestore(&channel_entry->lock, flags);
+               }
+               break;
+       case DMA_RESOURCE_SUSPEND:
+       case DMA_RESOURCE_RESUME:
+               printk(KERN_WARNING "async_tx: does not support dma channel"
+                       " suspend/resume\n");
+               break;
+       default:
+               BUG();
+       }
+}
+
+static int __init
+async_tx_init(void)
+{
+       unsigned long i;
+       struct async_channel_entry *channel_entry;
+       int cpu;
+
+       spin_lock_init(&async_tx_master_list.lock);
+
+       dma_async_for_each_tx_type(i) {
+               channel_entry = &async_channel_directory[i];
+               spin_lock_init(&channel_entry->lock);
+               channel_entry->local_iter = alloc_percpu(struct 
async_iter_percpu);
+               if (!channel_entry->local_iter) {
+                       i++;
+                       goto err;
+               }
+
+               atomic_set(&channel_entry->version, 0);
+
+               for_each_possible_cpu(cpu) {
+                       struct async_iter_percpu *local_iter =
+                               channel_entry->local_iter;
+                       per_cpu_ptr(local_iter, cpu)->iter = 
&channel_entry->list;
+                       per_cpu_ptr(local_iter, cpu)->local_version = 0;
+               }
+       }
+
+       async_api_client = dma_async_client_register(dma_channel_add_remove);
+
+       if (!async_api_client)
+               goto err;
+
+       dma_async_client_chan_request(async_api_client, DMA_CHAN_REQUEST_ALL);
+
+       printk("async_tx: api initialized (async)\n");
+
+       return 0;
+err:
+       printk("async_tx: initialization failure\n");
+
+       while (--i >= 0)
+               free_percpu(async_channel_directory[i].local_iter);
+
+       return 1;
+}
+
+static void __exit async_tx_exit(void)
+{
+       unsigned long i, flags;
+       struct async_channel_entry *channel_entry;
+       struct dma_chan_ref *ref;
+
+       if (async_api_client)
+               dma_async_client_unregister(async_api_client);
+
+       dma_async_for_each_tx_type(i) {
+               channel_entry = &async_channel_directory[i];
+               if (channel_entry->local_iter)
+                       free_percpu(channel_entry->local_iter);
+
+               /* free all the per operation channel references */
+               spin_lock_irqsave(&channel_entry->lock, flags);
+               list_for_each_entry_rcu(ref, &channel_entry->list, async_node) {
+                       list_del_rcu(&ref->async_node);
+                       call_rcu(&ref->rcu, free_dma_chan_ref);
+               }
+               spin_unlock_irqrestore(&channel_entry->lock, flags);
+       }
+
+       /* free all the channels on the master list */
+       spin_lock_irqsave(&async_tx_master_list.lock, flags);
+       list_for_each_entry_rcu(ref, &async_tx_master_list.list, async_node) {
+               dma_chan_put(ref->chan); /* permit backing devices to go away */
+               list_del_rcu(&ref->async_node);
+               call_rcu(&ref->rcu, free_dma_chan_ref);
+       }
+       spin_unlock_irqrestore(&async_tx_master_list.lock, flags);
+}
+
+/**
+ * async_tx_find_channel - find a channel to carry out the operation or let
+ *     the transaction execute synchronously
+ * @depend_tx: transaction dependency
+ * @tx_type: transaction type
+ */
+static struct dma_chan *
+async_tx_find_channel(struct dma_async_tx_descriptor *depend_tx,
+       enum dma_transaction_type tx_type)
+{
+       /* see if we can keep the chain on one channel */
+       if (depend_tx &&
+               test_bit(tx_type, &depend_tx->chan->device->capabilities))
+               return depend_tx->chan;
+       else {
+               int cpu;
+               struct async_channel_entry *channel_entry =
+                       &async_channel_directory[tx_type];
+               struct async_iter_percpu *local_iter;
+               struct list_head *iter;
+               struct dma_chan *chan;
+
+               rcu_read_lock();
+               if (list_empty(&channel_entry->list)) {
+                       rcu_read_unlock();
+                       return NULL;
+               }
+
+               cpu = get_cpu();
+               local_iter = per_cpu_ptr(channel_entry->local_iter, cpu);
+               put_cpu();
+
+               /* ensure the percpu place holder is pointing to a
+                * valid list entry and get the next channel in the
+                * round robin
+                */
+               if (unlikely(local_iter->local_version !=
+                       atomic_read(&channel_entry->version))) {
+                       local_iter->local_version =
+                               atomic_read(&channel_entry->version);
+                       iter = channel_entry->list.next;
+               } else {
+                       iter = local_iter->iter->next;
+                       /* wrap around detect */
+                       if (iter == &channel_entry->list)
+                               iter = iter->next;
+               }
+
+               /* if we are still pointing to the head then the list
+                * recently became empty
+                */
+               if (iter == &channel_entry->list)
+                       chan = NULL;
+               else {
+                       local_iter->iter = iter;
+                       chan = list_entry(iter, struct dma_chan_ref, 
async_node)->chan;
+               }
+               rcu_read_unlock();
+
+               return chan;
+       }
+}
+#else
+static int __init async_tx_init(void)
+{
+       printk("async_tx: api initialized (sync-only)\n");
+       return 0;
+}
+
+static void __exit async_tx_exit(void)
+{
+       do { } while (0);
+}
+
+static inline struct dma_chan *
+async_tx_find_channel(struct dma_async_tx_descriptor *depend_tx,
+       enum dma_transaction_type tx_type)
+{
+       return NULL;
+}
+#endif
+
+static inline void
+async_tx_submit(struct dma_chan *chan, struct dma_async_tx_descriptor *tx,
+       enum async_tx_flags flags, struct dma_async_tx_descriptor *depend_tx,
+       dma_async_tx_callback callback, void *callback_param)
+{
+       tx->callback = callback;
+       tx->callback_param = callback_param;
+
+       /* set this new tx to run after depend_tx if:
+        * 1/ a dependency exists (depend_tx is !NULL)
+        * 2/ the tx can not be submitted to the current channel
+        */
+       if (depend_tx && depend_tx->chan != chan) {
+               /* if ack is already set then we cannot be sure
+                * we are referring to the correct operation
+                */
+               BUG_ON(depend_tx->ack);
+
+               tx->parent = depend_tx;
+               spin_lock_bh(&depend_tx->lock);
+               list_add_tail(&tx->depend_node, &depend_tx->depend_list);
+               if (depend_tx->cookie == 0) {
+                       struct dma_chan *dep_chan = depend_tx->chan;
+                       struct dma_device *dep_dev = dep_chan->device;
+                       dep_dev->device_dependency_added(dep_chan);
+               }
+               spin_unlock_bh(&depend_tx->lock);
+       } else {
+               tx->parent = NULL;
+               chan->device->device_tx_submit(tx);
+       }
+
+       if (flags & ASYNC_TX_ACK)
+               async_tx_ack(tx);
+
+       if (depend_tx && (flags & ASYNC_TX_DEP_ACK))
+               async_tx_ack(depend_tx);
+}
+
+/**
+ * sync_epilog - actions to take if an operation is run synchronously
+ * @flags: async_tx flags
+ * @depend_tx: transaction depends on depend_tx
+ * @callback: function to call when the transaction completes
+ * @callback_param: parameter to pass to the callback routine
+ */
+static inline void
+sync_epilog(unsigned long flags, struct dma_async_tx_descriptor *depend_tx,
+       dma_async_tx_callback callback, void *callback_param)
+{
+       if (callback)
+               callback(callback_param);
+
+       if (depend_tx && (flags & ASYNC_TX_DEP_ACK))
+               async_tx_ack(depend_tx);
+}
+
+static inline void
+do_async_xor(struct dma_async_tx_descriptor *tx, struct dma_device *device,
+       struct dma_chan *chan, struct page *dest, struct page **src_list,
+       unsigned int offset, unsigned int src_cnt, size_t len,
+       enum async_tx_flags flags, struct dma_async_tx_descriptor *depend_tx,
+       dma_async_tx_callback callback, void *callback_param)
+{
+       dma_addr_t dma_addr;
+       enum dma_data_direction dir;
+       int i;
+
+       PRINTK("%s: len: %u\n", __FUNCTION__, len);
+
+       dir = (flags & ASYNC_TX_ASSUME_COHERENT) ?
+               DMA_NONE : DMA_FROM_DEVICE;
+
+       dma_addr = device->map_page(chan, dest, offset, len, dir);
+       device->device_set_dest(dma_addr, tx, 0);
+
+       dir = (flags & ASYNC_TX_ASSUME_COHERENT) ?
+               DMA_NONE : DMA_TO_DEVICE;
+
+       for (i = 0; i < src_cnt; i++) {
+               dma_addr = device->map_page(chan, src_list[i],
+                       offset, len, dir);
+               device->device_set_src(dma_addr, tx, i);
+       }
+
+       async_tx_submit(chan, tx, flags, depend_tx, callback,
+               callback_param);
+}
+
+static inline void
+do_sync_xor(struct page *dest, struct page **src_list, unsigned int offset,
+       unsigned int src_cnt, size_t len, enum async_tx_flags flags,
+       struct dma_async_tx_descriptor *depend_tx,
+       dma_async_tx_callback callback, void *callback_param)
+{
+       void *_dest;
+       int i;
+
+       PRINTK("%s: len: %u\n", __FUNCTION__, len);
+
+       /* reuse the 'src_list' array to convert to buffer pointers */
+       for (i = 0; i < src_cnt; i++)
+               src_list[i] = (struct page *)
+                       (page_address(src_list[i]) + offset);
+
+       /* set destination address */
+       _dest = page_address(dest) + offset;
+
+       if (flags & ASYNC_TX_XOR_ZERO_DST)
+               memset(_dest, 0, len);
+
+       xor_block(src_cnt, len, _dest,
+               (void **) src_list);
+
+       sync_epilog(flags, depend_tx, callback, callback_param);
+}
+
+/**
+ * async_xor - attempt to xor a set of blocks with a dma engine.
+ *     xor_block always uses the dest as a source so the ASYNC_TX_XOR_ZERO_DST
+ *     flag must be set to not include dest data in the calculation.  The
+ *     assumption with dma eninges is that they only use the destination
+ *     buffer as a source when it is explicity specified in the source list.
+ * @dest: destination page
+ * @src_list: array of source pages (if the dest is also a source it must be
+ *     at index zero).  The contents of this array may be overwritten.
+ * @offset: offset in pages to start transaction
+ * @src_cnt: number of source pages
+ * @len: length in bytes
+ * @flags: ASYNC_TX_XOR_ZERO_DST, ASYNC_TX_XOR_DROP_DEST,
+       ASYNC_TX_ASSUME_COHERENT, ASYNC_TX_ACK, ASYNC_TX_DEP_ACK
+ * @depend_tx: xor depends on the result of this transaction.
+ * @callback: function to call when the xor completes
+ * @callback_param: parameter to pass to the callback routine
+ */
+struct dma_async_tx_descriptor *
+async_xor(struct page *dest, struct page **src_list, unsigned int offset,
+       unsigned int src_cnt, size_t len, enum async_tx_flags flags,
+       struct dma_async_tx_descriptor *depend_tx,
+       dma_async_tx_callback callback, void *callback_param)
+{
+       struct dma_chan *chan = async_tx_find_channel(depend_tx, DMA_XOR);
+       struct dma_device *device = chan ? chan->device : NULL;
+       struct dma_async_tx_descriptor *tx = NULL;
+       dma_async_tx_callback _callback;
+       void *_callback_param;
+       unsigned long local_flags;
+       int xor_src_cnt;
+       int i = 0, src_off = 0, int_en;
+
+       BUG_ON(src_cnt <= 1);
+
+       while (src_cnt) {
+               local_flags = flags;
+               if (device) { /* run the xor asynchronously */
+                       xor_src_cnt = min(src_cnt, device->max_xor);
+                       /* if we are submitting additional xors
+                        * only set the callback on the last transaction
+                        */
+                       if (src_cnt > xor_src_cnt) {
+                               local_flags &= ~(ASYNC_TX_ACK | 
ASYNC_TX_INT_EN);
+                               _callback = NULL;
+                               _callback_param = NULL;
+                       } else {
+                               _callback = callback;
+                               _callback_param = callback_param;
+                       }
+
+                       int_en = (local_flags & ASYNC_TX_INT_EN) ? 1 : 0;
+
+                       tx = device->device_prep_dma_xor(
+                               chan, xor_src_cnt, len, int_en);
+
+                       if (tx) {
+                               do_async_xor(tx, device, chan, dest,
+                               &src_list[src_off], offset, xor_src_cnt, len,
+                               local_flags, depend_tx, _callback,
+                               _callback_param);
+                       } else /* fall through */
+                               goto xor_sync;
+               } else { /* run the xor synchronously */
+xor_sync:
+                       /* in the sync case the dest is an implied source
+                        * (assumes the dest is at the src_off index)
+                        */
+                       if (flags & ASYNC_TX_XOR_DROP_DST) {
+                               src_cnt--;
+                               src_off++;
+                       }
+
+                       /* process up to 'MAX_XOR_BLOCKS' sources */
+                       xor_src_cnt = min(src_cnt, (unsigned int) 
MAX_XOR_BLOCKS);
+
+                       /* if we are submitting additional xors
+                        * only set the callback on the last transaction
+                        */
+                       if (src_cnt > xor_src_cnt) {
+                               local_flags &= ~(ASYNC_TX_ACK | 
ASYNC_TX_INT_EN);
+                               _callback = NULL;
+                               _callback_param = NULL;
+                       } else {
+                               _callback = callback;
+                               _callback_param = callback_param;
+                       }
+
+                       /* wait for any prerequisite operations */
+                       if (depend_tx) {
+                               /* if ack is already set then we cannot be sure
+                                * we are referring to the correct operation
+                                */
+                               BUG_ON(depend_tx->ack);
+                               if (dma_wait_for_async_tx(depend_tx) == 
DMA_ERROR)
+                                       panic("%s: DMA_ERROR waiting for 
depend_tx\n",
+                                               __FUNCTION__);
+                       }
+
+                       do_sync_xor(dest, &src_list[src_off], offset,
+                               xor_src_cnt, len, local_flags, depend_tx,
+                               _callback, _callback_param);
+               }
+
+               /* the previous tx is hidden from the client,
+                * so ack it
+                */
+               if (i && depend_tx)
+                       async_tx_ack(depend_tx);
+
+               depend_tx = tx;
+
+               if (src_cnt > xor_src_cnt) {
+                       /* drop completed sources */
+                       src_cnt -= xor_src_cnt;
+                       src_off += xor_src_cnt;
+
+                       /* unconditionally preserve the destination */
+                       flags &= ~ASYNC_TX_XOR_ZERO_DST;
+
+                       /* use the intermediate result a source, but remember
+                        * it's dropped, because it's implied, in the sync case
+                        */
+                       src_list[--src_off] = dest;
+                       src_cnt++;
+                       flags |= ASYNC_TX_XOR_DROP_DST;
+               } else
+                       src_cnt = 0;
+               i++;
+       }
+
+       return tx;
+}
+
+static int page_is_zero(struct page *p, size_t len)
+{
+       char *a = page_address(p);
+       return ((*(u32*)a) == 0 &&
+               memcmp(a, a+4, len-4)==0);
+}
+
+/**
+ * async_xor_zero_sum - attempt a xor parity check with a dma engine.
+ * @dest: destination page used if the xor is performed synchronously
+ * @src_list: array of source pages.  The dest page must be listed as a source
+ *     at index zero.  The contents of this array may be overwritten.
+ * @offset: offset in pages to start transaction
+ * @src_cnt: number of source pages
+ * @len: length in bytes
+ * @result: 0 if sum == 0 else non-zero
+ * @flags: ASYNC_TX_ASSUME_COHERENT, ASYNC_TX_ACK
+ * @depend_tx: xor depends on the result of this transaction.
+ * @callback: function to call when the xor completes
+ * @callback_param: parameter to pass to the callback routine
+ */
+struct dma_async_tx_descriptor *
+async_xor_zero_sum(struct page *dest, struct page **src_list,
+       unsigned int offset, unsigned int src_cnt, size_t len,
+       u32 *result, enum async_tx_flags flags,
+       struct dma_async_tx_descriptor *depend_tx,
+       dma_async_tx_callback callback, void *callback_param)
+{
+       struct dma_chan *chan = async_tx_find_channel(depend_tx, DMA_ZERO_SUM);
+       struct dma_device *device = chan ? chan->device : NULL;
+       int int_en = (flags & ASYNC_TX_INT_EN) ? 1 : 0;
+       struct dma_async_tx_descriptor *tx = device ?
+               device->device_prep_dma_zero_sum(chan, src_cnt, len, result,
+                       int_en) : NULL;
+       int i;
+
+       if (tx) {
+               dma_addr_t dma_addr;
+               enum dma_data_direction dir;
+
+               PRINTK("%s: (async) len: %u\n", __FUNCTION__, len);
+
+               dir = (flags & ASYNC_TX_ASSUME_COHERENT) ?
+                       DMA_NONE : DMA_TO_DEVICE;
+
+               for (i = 0; i < src_cnt; i++) {
+                       dma_addr = device->map_page(chan, src_list[i],
+                               offset, len, dir);
+                       device->device_set_src(dma_addr, tx, i);
+               }
+
+               async_tx_submit(chan, tx, flags, depend_tx, callback,
+                       callback_param);
+       } else {
+               unsigned long xor_flags = flags;
+
+               PRINTK("%s: (sync) len: %u\n", __FUNCTION__, len);
+
+               xor_flags |= ASYNC_TX_XOR_DROP_DST;
+               xor_flags &= ~ASYNC_TX_ACK;
+
+               tx = async_xor(dest, src_list, offset, src_cnt, len, xor_flags,
+                       depend_tx, NULL, NULL);
+
+               if (tx) {
+                       if (dma_wait_for_async_tx(tx) == DMA_ERROR)
+                               panic("%s: DMA_ERROR waiting for tx\n",
+                                       __FUNCTION__);
+                       async_tx_ack(tx);
+               }
+
+               *result = page_is_zero(dest, len) ? 0 : 1;
+
+               tx = NULL;
+
+               sync_epilog(flags, depend_tx, callback, callback_param);
+       }
+
+       return tx;
+}
+
+/**
+ * async_memcpy - attempt to copy memory with a dma engine.
+ * @dest: destination page
+ * @src: src page
+ * @offset: offset in pages to start transaction
+ * @len: length in bytes
+ * @flags: ASYNC_TX_ASSUME_COHERENT, ASYNC_TX_ACK, ASYNC_TX_DEP_ACK,
+ *     ASYNC_TX_KMAP_SRC, ASYNC_TX_KMAP_DST
+ * @depend_tx: memcpy depends on the result of this transaction
+ * @callback: function to call when the memcpy completes
+ * @callback_param: parameter to pass to the callback routine
+ */
+struct dma_async_tx_descriptor *
+async_memcpy(struct page *dest, struct page *src, unsigned int dest_offset,
+       unsigned int src_offset, size_t len, enum async_tx_flags flags,
+       struct dma_async_tx_descriptor *depend_tx,
+       dma_async_tx_callback callback, void *callback_param)
+{
+       struct dma_chan *chan = async_tx_find_channel(depend_tx, DMA_MEMCPY);
+       struct dma_device *device = chan ? chan->device : NULL;
+       int int_en = (flags & ASYNC_TX_INT_EN) ? 1 : 0;
+       struct dma_async_tx_descriptor *tx = device ?
+               device->device_prep_dma_memcpy(chan, len,
+               int_en) : NULL;
+
+       if (tx) { /* run the memcpy asynchronously */
+               dma_addr_t dma_addr;
+               enum dma_data_direction dir;
+
+               PRINTK("%s: (async) len: %u\n", __FUNCTION__, len);
+
+               dir = (flags & ASYNC_TX_ASSUME_COHERENT) ?
+                       DMA_NONE : DMA_FROM_DEVICE;
+
+               dma_addr = device->map_page(chan, dest, dest_offset, len, dir);
+               device->device_set_dest(dma_addr, tx, 0);
+
+               dir = (flags & ASYNC_TX_ASSUME_COHERENT) ?
+                       DMA_NONE : DMA_TO_DEVICE;
+
+               dma_addr = device->map_page(chan, src, src_offset, len, dir);
+               device->device_set_src(dma_addr, tx, 0);
+
+               async_tx_submit(chan, tx, flags, depend_tx, callback,
+                       callback_param);
+       } else { /* run the memcpy synchronously */
+               void *dest_buf, *src_buf;
+               PRINTK("%s: (sync) len: %u\n", __FUNCTION__, len);
+
+               /* wait for any prerequisite operations */
+               if (depend_tx) {
+                       /* if ack is already set then we cannot be sure
+                        * we are referring to the correct operation
+                        */
+                       BUG_ON(depend_tx->ack);
+                       if (dma_wait_for_async_tx(depend_tx) == DMA_ERROR)
+                               panic("%s: DMA_ERROR waiting for depend_tx\n",
+                                       __FUNCTION__);
+               }
+
+               if (flags & ASYNC_TX_KMAP_DST)
+                       dest_buf = kmap_atomic(dest, KM_USER0) + dest_offset;
+               else
+                       dest_buf = page_address(dest) + dest_offset;
+
+               if (flags & ASYNC_TX_KMAP_SRC)
+                       src_buf = kmap_atomic(src, KM_USER0) + src_offset;
+               else
+                       src_buf = page_address(src) + src_offset;
+
+               memcpy(dest_buf, src_buf, len);
+
+               if (flags & ASYNC_TX_KMAP_DST)
+                       kunmap_atomic(dest_buf, KM_USER0);
+
+               if (flags & ASYNC_TX_KMAP_SRC)
+                       kunmap_atomic(src_buf, KM_USER0);
+
+               sync_epilog(flags, depend_tx, callback, callback_param);
+       }
+
+       return tx;
+}
+
+/**
+ * async_memset - attempt to fill memory with a dma engine.
+ * @dest: destination page
+ * @val: fill value
+ * @offset: offset in pages to start transaction
+ * @len: length in bytes
+ * @flags: ASYNC_TX_ASSUME_COHERENT, ASYNC_TX_ACK
+ * @depend_tx: memset depends on the result of this transaction
+ * @callback: function to call when the memcpy completes
+ * @callback_param: parameter to pass to the callback routine
+ */
+struct dma_async_tx_descriptor *
+async_memset(struct page *dest, int val, unsigned int offset,
+       size_t len, enum async_tx_flags flags,
+       struct dma_async_tx_descriptor *depend_tx,
+       dma_async_tx_callback callback, void *callback_param)
+{
+       struct dma_chan *chan = async_tx_find_channel(depend_tx, DMA_MEMSET);
+       struct dma_device *device = chan ? chan->device : NULL;
+       int int_en = (flags & ASYNC_TX_INT_EN) ? 1 : 0;
+       struct dma_async_tx_descriptor *tx = device ?
+               device->device_prep_dma_memset(chan, val, len,
+                       int_en) : NULL;
+
+       if (tx) { /* run the memset asynchronously */
+               dma_addr_t dma_addr;
+               enum dma_data_direction dir;
+
+               PRINTK("%s: (async) len: %u\n", __FUNCTION__, len);
+               dir = (flags & ASYNC_TX_ASSUME_COHERENT) ?
+                       DMA_NONE : DMA_FROM_DEVICE;
+
+               dma_addr = device->map_page(chan, dest, offset, len, dir);
+               device->device_set_dest(dma_addr, tx, 0);
+
+               async_tx_submit(chan, tx, flags, depend_tx, callback,
+                       callback_param);
+       } else { /* run the memset synchronously */
+               void *dest_buf;
+               PRINTK("%s: (sync) len: %u\n", __FUNCTION__, len);
+
+               dest_buf = (void *) (((char *) page_address(dest)) + offset);
+
+               /* wait for any prerequisite operations */
+               if (depend_tx) {
+                       /* if ack is already set then we cannot be sure
+                        * we are referring to the correct operation
+                        */
+                       BUG_ON(depend_tx->ack);
+                       if (dma_wait_for_async_tx(depend_tx) == DMA_ERROR)
+                               panic("%s: DMA_ERROR waiting for depend_tx\n",
+                                       __FUNCTION__);
+               }
+
+               memset(dest_buf, val, len);
+
+               sync_epilog(flags, depend_tx, callback, callback_param);
+       }
+
+       return tx;
+}
+
+/**
+ * async_interrupt - cause an interrupt to asynchrounously flush pending
+ *     completion callbacks, or schedule new callback.  Note: this rouine
+ *     assumes that all dma channels have the DMA_INTERRUPT capability
+ * @flags: ASYNC_TX_DEP_ACK
+ * @depend_tx: interrupt depends the result of this transaction
+ * @callback: function to call after the interrupt fires
+ * @callback_param: parameter to pass to the callback routine
+ */
+struct dma_async_tx_descriptor *
+async_interrupt(enum async_tx_flags flags,
+       struct dma_async_tx_descriptor *depend_tx,
+       dma_async_tx_callback callback, void *callback_param)
+{
+       struct dma_chan *chan = async_tx_find_channel(depend_tx, DMA_INTERRUPT);
+       struct dma_device *device = chan ? chan->device : NULL;
+       struct dma_async_tx_descriptor *tx = device ?
+               device->device_prep_dma_interrupt(chan) : NULL;
+
+       if (tx) {
+               PRINTK("%s: (async)\n", __FUNCTION__);
+
+               async_tx_submit(chan, tx, flags, depend_tx, callback,
+                       callback_param);
+       } else {
+               PRINTK("%s: (sync)\n", __FUNCTION__);
+
+               /* wait for any prerequisite operations */
+               if (depend_tx) {
+                       /* if ack is already set then we cannot be sure
+                        * we are referring to the correct operation
+                        */
+                       BUG_ON(depend_tx->ack);
+                       if (dma_wait_for_async_tx(depend_tx) == DMA_ERROR)
+                               panic("%s: DMA_ERROR waiting for depend_tx\n",
+                                       __FUNCTION__);
+               }
+
+               sync_epilog(flags, depend_tx, callback, callback_param);
+       }
+
+       return tx;
+}
+
+/**
+ * async_interrupt_cond - same as async_interrupt except that this will only be
+ *     if next_op must be run on a different channel.  Note: this rouine
+ *     assumes that all dma channels have the DMA_INTERRUPT capability
+ * @next_op: the next operation type to be submitted
+ * @flags: ASYNC_TX_DEP_ACK
+ * @depend_tx: interrupt depends the result of this transaction
+ */
+struct dma_async_tx_descriptor *
+async_interrupt_cond(enum dma_transaction_type next_op,
+       enum async_tx_flags flags, struct dma_async_tx_descriptor *depend_tx)
+{
+       int chan_switch = depend_tx ?
+               !test_bit(next_op, &depend_tx->chan->device->capabilities) : 0;
+       struct dma_chan *chan = chan_switch ? depend_tx->chan : NULL;
+       struct dma_device *device = chan ? chan->device : NULL;
+       struct dma_async_tx_descriptor *tx = device ?
+               device->device_prep_dma_interrupt(chan) : NULL;
+
+       if (!chan_switch)
+               return depend_tx;
+       else if (tx) {
+               PRINTK("%s: (async)\n", __FUNCTION__);
+
+               async_tx_submit(chan, tx, flags, depend_tx, NULL, NULL);
+       } else {
+               PRINTK("%s: (sync)\n", __FUNCTION__);
+
+               /* wait for any prerequisite operations */
+               if (depend_tx) {
+                       /* if ack is already set then we cannot be sure
+                        * we are referring to the correct operation
+                        */
+                       BUG_ON(depend_tx->ack);
+                       if (dma_wait_for_async_tx(depend_tx) == DMA_ERROR)
+                               panic("%s: DMA_ERROR waiting for depend_tx\n",
+                                       __FUNCTION__);
+               }
+
+               sync_epilog(flags, depend_tx, NULL, NULL);
+       }
+
+       return tx;
+}
+
+module_init(async_tx_init);
+module_exit(async_tx_exit);
+
+MODULE_AUTHOR("Intel Corporation");
+MODULE_DESCRIPTION("Asynchronous Bulk Memory Transactions API");
+MODULE_LICENSE("GPL");
+
+EXPORT_SYMBOL_GPL(async_interrupt);
+EXPORT_SYMBOL_GPL(async_interrupt_cond);
+EXPORT_SYMBOL_GPL(async_memcpy);
+EXPORT_SYMBOL_GPL(async_memset);
+EXPORT_SYMBOL_GPL(async_xor);
+EXPORT_SYMBOL_GPL(async_xor_zero_sum);
+EXPORT_SYMBOL_GPL(async_tx_issue_pending_all);
+EXPORT_SYMBOL_GPL(async_tx_ack);
+EXPORT_SYMBOL_GPL(dma_wait_for_async_tx);
+EXPORT_SYMBOL_GPL(async_tx_run_dependencies);
diff --git a/drivers/dma/xor.c b/drivers/dma/xor.c
new file mode 100644
index 0000000..6eb3416
--- /dev/null
+++ b/drivers/dma/xor.c
@@ -0,0 +1,153 @@
+/*
+ * xor.c : Multiple Devices driver for Linux
+ *
+ * Copyright (C) 1996, 1997, 1998, 1999, 2000,
+ * Ingo Molnar, Matti Aarnio, Jakub Jelinek, Richard Henderson.
+ *
+ * Dispatch optimized RAID-5 checksumming functions.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * You should have received a copy of the GNU General Public License
+ * (for example /usr/src/linux/COPYING); if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#define BH_TRACE 0
+#include <linux/module.h>
+#include <linux/raid/md.h>
+#include <linux/raid/xor.h>
+#include <asm/xor.h>
+
+/* The xor routines to use.  */
+static struct xor_block_template *active_template;
+
+void
+xor_block(unsigned int src_count, unsigned int bytes, void *dest, void **srcs)
+{
+       unsigned long *p1, *p2, *p3, *p4;
+
+       p1 = (unsigned long *) srcs[0];
+       if (src_count == 1) {
+               active_template->do_2(bytes, dest, p1);
+               return;
+       }
+
+       p2 = (unsigned long *) srcs[1];
+       if (src_count == 2) {
+               active_template->do_3(bytes, dest, p1, p2);
+               return;
+       }
+
+       p3 = (unsigned long *) srcs[2];
+       if (src_count == 3) {
+               active_template->do_4(bytes, dest, p1, p2, p3);
+               return;
+       }
+
+       p4 = (unsigned long *) srcs[3];
+       active_template->do_5(bytes, dest, p1, p2, p3, p4);
+}
+
+/* Set of all registered templates.  */
+static struct xor_block_template *template_list;
+
+#define BENCH_SIZE (PAGE_SIZE)
+
+static void
+do_xor_speed(struct xor_block_template *tmpl, void *b1, void *b2)
+{
+       int speed;
+       unsigned long now;
+       int i, count, max;
+
+       tmpl->next = template_list;
+       template_list = tmpl;
+
+       /*
+        * Count the number of XORs done during a whole jiffy, and use
+        * this to calculate the speed of checksumming.  We use a 2-page
+        * allocation to have guaranteed color L1-cache layout.
+        */
+       max = 0;
+       for (i = 0; i < 5; i++) {
+               now = jiffies;
+               count = 0;
+               while (jiffies == now) {
+                       mb();
+                       tmpl->do_2(BENCH_SIZE, b1, b2);
+                       mb();
+                       count++;
+                       mb();
+               }
+               if (count > max)
+                       max = count;
+       }
+
+       speed = max * (HZ * BENCH_SIZE / 1024);
+       tmpl->speed = speed;
+
+       printk("   %-10s: %5d.%03d MB/sec\n", tmpl->name,
+              speed / 1000, speed % 1000);
+}
+
+static int
+calibrate_xor_block(void)
+{
+       void *b1, *b2;
+       struct xor_block_template *f, *fastest;
+
+       b1 = (void *) __get_free_pages(GFP_KERNEL, 2);
+       if (! b1) {
+               printk("xor: Yikes!  No memory available.\n");
+               return -ENOMEM;
+       }
+       b2 = b1 + 2*PAGE_SIZE + BENCH_SIZE;
+
+       /*
+        * If this arch/cpu has a short-circuited selection, don't loop through 
all
+        * the possible functions, just test the best one
+        */
+
+       fastest = NULL;
+
+#ifdef XOR_SELECT_TEMPLATE
+               fastest = XOR_SELECT_TEMPLATE(fastest);
+#endif
+
+#define xor_speed(templ)       do_xor_speed((templ), b1, b2)
+
+       if (fastest) {
+               printk(KERN_INFO "xor: automatically using best checksumming 
function: %s\n",
+                       fastest->name);
+               xor_speed(fastest);
+       } else {
+               printk(KERN_INFO "xor: measuring software checksumming 
speed\n");
+               XOR_TRY_TEMPLATES;
+               fastest = template_list;
+               for (f = fastest; f; f = f->next)
+                       if (f->speed > fastest->speed)
+                               fastest = f;
+       }
+
+       printk("xor: using function: %s (%d.%03d MB/sec)\n",
+              fastest->name, fastest->speed / 1000, fastest->speed % 1000);
+
+#undef xor_speed
+
+       free_pages((unsigned long)b1, 2);
+
+       active_template = fastest;
+       return 0;
+}
+
+static __exit void xor_exit(void) { }
+
+EXPORT_SYMBOL(xor_block);
+MODULE_LICENSE("GPL");
+
+module_init(calibrate_xor_block);
+module_exit(xor_exit);
diff --git a/drivers/md/Kconfig b/drivers/md/Kconfig
index 4540ade..96377f9 100644
--- a/drivers/md/Kconfig
+++ b/drivers/md/Kconfig
@@ -108,7 +108,7 @@ config MD_RAID10
 
 config MD_RAID456
        tristate "RAID-4/RAID-5/RAID-6 mode"
-       depends on BLK_DEV_MD
+       depends on BLK_DEV_MD && ASYNC_TX_DMA
        ---help---
          A RAID-5 set of N drives with a capacity of C MB per drive provides
          the capacity of C * (N - 1) MB, and protects against a failure
diff --git a/drivers/md/Makefile b/drivers/md/Makefile
index 34957a6..23c3049 100644
--- a/drivers/md/Makefile
+++ b/drivers/md/Makefile
@@ -17,15 +17,15 @@ raid456-objs        := raid5.o raid6algos.o raid6recov.o 
raid6tables.o \
 hostprogs-y    := mktables
 
 # Note: link order is important.  All raid personalities
-# and xor.o must come before md.o, as they each initialise 
-# themselves, and md.o may use the personalities when it 
+# must come before md.o, as they each initialise
+# themselves, and md.o may use the personalities when it
 # auto-initialised.
 
 obj-$(CONFIG_MD_LINEAR)                += linear.o
 obj-$(CONFIG_MD_RAID0)         += raid0.o
 obj-$(CONFIG_MD_RAID1)         += raid1.o
 obj-$(CONFIG_MD_RAID10)                += raid10.o
-obj-$(CONFIG_MD_RAID456)       += raid456.o xor.o
+obj-$(CONFIG_MD_RAID456)       += raid456.o
 obj-$(CONFIG_MD_MULTIPATH)     += multipath.o
 obj-$(CONFIG_MD_FAULTY)                += faulty.o
 obj-$(CONFIG_BLK_DEV_MD)       += md-mod.o
diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c
index be008f0..68b6fea 100644
--- a/drivers/md/raid5.c
+++ b/drivers/md/raid5.c
@@ -914,25 +914,25 @@ static void copy_data(int frombio, struct bio *bio,
        }
 }
 
-#define check_xor()    do {                                            \
-                          if (count == MAX_XOR_BLOCKS) {               \
-                               xor_block(count, STRIPE_SIZE, ptr);     \
-                               count = 1;                              \
-                          }                                            \
+#define check_xor()    do {                                                   \
+                          if (count == MAX_XOR_BLOCKS) {                      \
+                               xor_block(count, STRIPE_SIZE, dest, ptr);      \
+                               count = 0;                                     \
+                          }                                                   \
                        } while(0)
 
 
 static void compute_block(struct stripe_head *sh, int dd_idx)
 {
        int i, count, disks = sh->disks;
-       void *ptr[MAX_XOR_BLOCKS], *p;
+       void *ptr[MAX_XOR_BLOCKS], *dest, *p;
 
        PRINTK("compute_block, stripe %llu, idx %d\n", 
                (unsigned long long)sh->sector, dd_idx);
 
-       ptr[0] = page_address(sh->dev[dd_idx].page);
-       memset(ptr[0], 0, STRIPE_SIZE);
-       count = 1;
+       dest = page_address(sh->dev[dd_idx].page);
+       memset(dest, 0, STRIPE_SIZE);
+       count = 0;
        for (i = disks ; i--; ) {
                if (i == dd_idx)
                        continue;
@@ -946,8 +946,8 @@ static void compute_block(struct stripe_head *sh, int 
dd_idx)
 
                check_xor();
        }
-       if (count != 1)
-               xor_block(count, STRIPE_SIZE, ptr);
+       if (count)
+               xor_block(count, STRIPE_SIZE, dest, ptr);
        set_bit(R5_UPTODATE, &sh->dev[dd_idx].flags);
 }
 
@@ -955,14 +955,14 @@ static void compute_parity5(struct stripe_head *sh, int 
method)
 {
        raid5_conf_t *conf = sh->raid_conf;
        int i, pd_idx = sh->pd_idx, disks = sh->disks, count;
-       void *ptr[MAX_XOR_BLOCKS];
+       void *ptr[MAX_XOR_BLOCKS], *dest;
        struct bio *chosen;
 
        PRINTK("compute_parity5, stripe %llu, method %d\n",
                (unsigned long long)sh->sector, method);
 
-       count = 1;
-       ptr[0] = page_address(sh->dev[pd_idx].page);
+       count = 0;
+       dest = page_address(sh->dev[pd_idx].page);
        switch(method) {
        case READ_MODIFY_WRITE:
                BUG_ON(!test_bit(R5_UPTODATE, &sh->dev[pd_idx].flags));
@@ -985,7 +985,7 @@ static void compute_parity5(struct stripe_head *sh, int 
method)
                }
                break;
        case RECONSTRUCT_WRITE:
-               memset(ptr[0], 0, STRIPE_SIZE);
+               memset(dest, 0, STRIPE_SIZE);
                for (i= disks; i-- ;)
                        if (i!=pd_idx && sh->dev[i].towrite) {
                                chosen = sh->dev[i].towrite;
@@ -1001,9 +1001,9 @@ static void compute_parity5(struct stripe_head *sh, int 
method)
        case CHECK_PARITY:
                break;
        }
-       if (count>1) {
-               xor_block(count, STRIPE_SIZE, ptr);
-               count = 1;
+       if (count) {
+               xor_block(count, STRIPE_SIZE, dest, ptr);
+               count = 0;
        }
        
        for (i = disks; i--;)
@@ -1035,8 +1035,8 @@ static void compute_parity5(struct stripe_head *sh, int 
method)
                                check_xor();
                        }
        }
-       if (count != 1)
-               xor_block(count, STRIPE_SIZE, ptr);
+       if (count)
+               xor_block(count, STRIPE_SIZE, dest, ptr);
        
        if (method != CHECK_PARITY) {
                set_bit(R5_UPTODATE, &sh->dev[pd_idx].flags);
@@ -1131,7 +1131,7 @@ static void compute_block_1(struct stripe_head *sh, int 
dd_idx, int nozero)
 {
        raid6_conf_t *conf = sh->raid_conf;
        int i, count, disks = conf->raid_disks;
-       void *ptr[MAX_XOR_BLOCKS], *p;
+       void *ptr[MAX_XOR_BLOCKS], *dest, *p;
        int pd_idx = sh->pd_idx;
        int qd_idx = raid6_next_disk(pd_idx, disks);
 
@@ -1142,9 +1142,9 @@ static void compute_block_1(struct stripe_head *sh, int 
dd_idx, int nozero)
                /* We're actually computing the Q drive */
                compute_parity6(sh, UPDATE_PARITY);
        } else {
-               ptr[0] = page_address(sh->dev[dd_idx].page);
-               if (!nozero) memset(ptr[0], 0, STRIPE_SIZE);
-               count = 1;
+               dest = page_address(sh->dev[dd_idx].page);
+               if (!nozero) memset(dest, 0, STRIPE_SIZE);
+               count = 0;
                for (i = disks ; i--; ) {
                        if (i == dd_idx || i == qd_idx)
                                continue;
@@ -1158,8 +1158,8 @@ static void compute_block_1(struct stripe_head *sh, int 
dd_idx, int nozero)
 
                        check_xor();
                }
-               if (count != 1)
-                       xor_block(count, STRIPE_SIZE, ptr);
+               if (count)
+                       xor_block(count, STRIPE_SIZE, dest, ptr);
                if (!nozero) set_bit(R5_UPTODATE, &sh->dev[dd_idx].flags);
                else clear_bit(R5_UPTODATE, &sh->dev[dd_idx].flags);
        }
diff --git a/drivers/md/xor.c b/drivers/md/xor.c
deleted file mode 100644
index 324897c..0000000
--- a/drivers/md/xor.c
+++ /dev/null
@@ -1,154 +0,0 @@
-/*
- * xor.c : Multiple Devices driver for Linux
- *
- * Copyright (C) 1996, 1997, 1998, 1999, 2000,
- * Ingo Molnar, Matti Aarnio, Jakub Jelinek, Richard Henderson.
- *
- * Dispatch optimized RAID-5 checksumming functions.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * You should have received a copy of the GNU General Public License
- * (for example /usr/src/linux/COPYING); if not, write to the Free
- * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-#define BH_TRACE 0
-#include <linux/module.h>
-#include <linux/raid/md.h>
-#include <linux/raid/xor.h>
-#include <asm/xor.h>
-
-/* The xor routines to use.  */
-static struct xor_block_template *active_template;
-
-void
-xor_block(unsigned int count, unsigned int bytes, void **ptr)
-{
-       unsigned long *p0, *p1, *p2, *p3, *p4;
-
-       p0 = (unsigned long *) ptr[0];
-       p1 = (unsigned long *) ptr[1];
-       if (count == 2) {
-               active_template->do_2(bytes, p0, p1);
-               return;
-       }
-
-       p2 = (unsigned long *) ptr[2];
-       if (count == 3) {
-               active_template->do_3(bytes, p0, p1, p2);
-               return;
-       }
-
-       p3 = (unsigned long *) ptr[3];
-       if (count == 4) {
-               active_template->do_4(bytes, p0, p1, p2, p3);
-               return;
-       }
-
-       p4 = (unsigned long *) ptr[4];
-       active_template->do_5(bytes, p0, p1, p2, p3, p4);
-}
-
-/* Set of all registered templates.  */
-static struct xor_block_template *template_list;
-
-#define BENCH_SIZE (PAGE_SIZE)
-
-static void
-do_xor_speed(struct xor_block_template *tmpl, void *b1, void *b2)
-{
-       int speed;
-       unsigned long now;
-       int i, count, max;
-
-       tmpl->next = template_list;
-       template_list = tmpl;
-
-       /*
-        * Count the number of XORs done during a whole jiffy, and use
-        * this to calculate the speed of checksumming.  We use a 2-page
-        * allocation to have guaranteed color L1-cache layout.
-        */
-       max = 0;
-       for (i = 0; i < 5; i++) {
-               now = jiffies;
-               count = 0;
-               while (jiffies == now) {
-                       mb();
-                       tmpl->do_2(BENCH_SIZE, b1, b2);
-                       mb();
-                       count++;
-                       mb();
-               }
-               if (count > max)
-                       max = count;
-       }
-
-       speed = max * (HZ * BENCH_SIZE / 1024);
-       tmpl->speed = speed;
-
-       printk("   %-10s: %5d.%03d MB/sec\n", tmpl->name,
-              speed / 1000, speed % 1000);
-}
-
-static int
-calibrate_xor_block(void)
-{
-       void *b1, *b2;
-       struct xor_block_template *f, *fastest;
-
-       b1 = (void *) __get_free_pages(GFP_KERNEL, 2);
-       if (! b1) {
-               printk("raid5: Yikes!  No memory available.\n");
-               return -ENOMEM;
-       }
-       b2 = b1 + 2*PAGE_SIZE + BENCH_SIZE;
-
-       /*
-        * If this arch/cpu has a short-circuited selection, don't loop through 
all
-        * the possible functions, just test the best one
-        */
-
-       fastest = NULL;
-
-#ifdef XOR_SELECT_TEMPLATE
-               fastest = XOR_SELECT_TEMPLATE(fastest);
-#endif
-
-#define xor_speed(templ)       do_xor_speed((templ), b1, b2)
-
-       if (fastest) {
-               printk(KERN_INFO "raid5: automatically using best checksumming 
function: %s\n",
-                       fastest->name);
-               xor_speed(fastest);
-       } else {
-               printk(KERN_INFO "raid5: measuring checksumming speed\n");
-               XOR_TRY_TEMPLATES;
-               fastest = template_list;
-               for (f = fastest; f; f = f->next)
-                       if (f->speed > fastest->speed)
-                               fastest = f;
-       }
-
-       printk("raid5: using function: %s (%d.%03d MB/sec)\n",
-              fastest->name, fastest->speed / 1000, fastest->speed % 1000);
-
-#undef xor_speed
-
-       free_pages((unsigned long)b1, 2);
-
-       active_template = fastest;
-       return 0;
-}
-
-static __exit void xor_exit(void) { }
-
-EXPORT_SYMBOL(xor_block);
-MODULE_LICENSE("GPL");
-
-module_init(calibrate_xor_block);
-module_exit(xor_exit);
diff --git a/include/linux/async_tx.h b/include/linux/async_tx.h
new file mode 100644
index 0000000..302c4f6
--- /dev/null
+++ b/include/linux/async_tx.h
@@ -0,0 +1,180 @@
+/*
+ * Copyright(c) 2006 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called COPYING.
+ */
+#include <linux/dmaengine.h>
+#include <linux/spinlock.h>
+#include <linux/interrupt.h>
+
+struct dma_chan_ref {
+       struct dma_chan *chan;
+       struct list_head async_node;
+       struct rcu_head rcu;
+};
+
+struct async_iter_percpu {
+       struct list_head *iter;
+       unsigned long local_version;
+};
+
+struct async_channel_entry {
+       struct list_head list;
+       spinlock_t lock;
+       struct async_iter_percpu *local_iter;
+       atomic_t version;
+};
+
+/**
+ * async_tx_flags - modifiers for the async_* calls
+ * @ASYNC_TX_XOR_ZERO_DST: for synchronous xor: zero the destination
+ *     asynchronous assumes a pre-zeroed destination
+ * @ASYNC_TX_XOR_DROP_DST: for synchronous xor: drop source index zero (dest)
+ *     the dest is an implicit source to the synchronous routine
+ * @ASYNC_TX_ASSUME_COHERENT: skip cache maintenance operations
+ * @ASYNC_TX_ACK: immediately ack the descriptor, preclude setting up a
+ *     dependency chain
+ * @ASYNC_TX_DEP_ACK: ack the dependency
+ * @ASYNC_TX_INT_EN: have the dma engine trigger an interrupt on completion
+ * @ASYNC_TX_KMAP_SRC: take an atomic mapping (KM_USER0) on the source page(s)
+ *     if the transaction is to be performed synchronously
+ * @ASYNC_TX_KMAP_DST: take an atomic mapping (KM_USER0) on the dest page(s)
+ *     if the transaction is to be performed synchronously
+ */
+enum async_tx_flags {
+       ASYNC_TX_XOR_ZERO_DST    = (1 << 0),
+       ASYNC_TX_XOR_DROP_DST    = (1 << 1),
+       ASYNC_TX_ASSUME_COHERENT = (1 << 2),
+       ASYNC_TX_ACK             = (1 << 3),
+       ASYNC_TX_DEP_ACK         = (1 << 4),
+       ASYNC_TX_INT_EN          = (1 << 5),
+       ASYNC_TX_KMAP_SRC        = (1 << 6),
+       ASYNC_TX_KMAP_DST        = (1 << 7),
+};
+
+#ifdef CONFIG_DMA_ENGINE
+static inline enum dma_status
+dma_wait_for_async_tx(struct dma_async_tx_descriptor *tx)
+{
+       enum dma_status status;
+       struct dma_async_tx_descriptor *iter;
+
+       if (!tx)
+               return DMA_SUCCESS;
+
+       /* poll through the dependency chain, return when tx is complete */
+       do {
+               iter = tx;
+               while (iter->cookie == -EBUSY)
+                       iter = iter->parent;
+
+               status = dma_sync_wait(iter->chan, iter->cookie);
+       } while (status == DMA_IN_PROGRESS || (iter != tx));
+
+       return status;
+}
+
+extern struct async_channel_entry async_tx_master_list;
+static inline void async_tx_issue_pending_all(void)
+{
+       struct dma_chan_ref *ref;
+
+       rcu_read_lock();
+       list_for_each_entry_rcu(ref, &async_tx_master_list.list, async_node)
+               ref->chan->device->device_issue_pending(ref->chan);
+       rcu_read_unlock();
+}
+
+static inline void
+async_tx_run_dependencies(struct dma_async_tx_descriptor *tx,
+       struct dma_chan *host_chan)
+{
+       struct dma_async_tx_descriptor *dep_tx, *_dep_tx;
+       struct dma_device *dev;
+       struct dma_chan *chan;
+
+       list_for_each_entry_safe(dep_tx, _dep_tx, &tx->depend_list,
+               depend_node) {
+               chan = dep_tx->chan;
+               dev = chan->device;
+               /* we can't depend on ourselves */
+               BUG_ON(chan == host_chan);
+               list_del(&dep_tx->depend_node);
+               dev->device_tx_submit(dep_tx);
+
+               /* we need to poke the engine as client code does not
+                * know about dependency submission events
+                */
+               dev->device_issue_pending(chan);
+       }
+}
+#else
+static inline void
+async_tx_run_dependencies(struct dma_async_tx_descriptor *tx,
+       struct dma_chan *host_chan)
+{
+       do { } while (0);
+}
+
+static inline enum dma_status
+dma_wait_for_async_tx(struct dma_async_tx_descriptor *tx)
+{
+       return DMA_SUCCESS;
+}
+
+static inline void async_tx_issue_pending_all(void)
+{
+       do { } while (0);
+}
+#endif
+
+static inline void
+async_tx_ack(struct dma_async_tx_descriptor *tx)
+{
+       tx->ack = 1;
+}
+
+struct dma_async_tx_descriptor *
+async_xor(struct page *dest, struct page **src_list, unsigned int offset,
+       unsigned int src_cnt, size_t len, enum async_tx_flags flags,
+       struct dma_async_tx_descriptor *depend_tx,
+       dma_async_tx_callback callback, void *callback_param);
+struct dma_async_tx_descriptor *
+async_xor_zero_sum(struct page *dest, struct page **src_list,
+       unsigned int offset, unsigned int src_cnt, size_t len,
+       u32 *result, enum async_tx_flags flags,
+       struct dma_async_tx_descriptor *depend_tx,
+       dma_async_tx_callback callback, void *callback_param);
+struct dma_async_tx_descriptor *
+async_memcpy(struct page *dest, struct page *src, unsigned int dest_offset,
+       unsigned int src_offset, size_t len, enum async_tx_flags flags,
+       struct dma_async_tx_descriptor *depend_tx,
+       dma_async_tx_callback callback, void *callback_param);
+struct dma_async_tx_descriptor *
+async_memset(struct page *dest, int val, unsigned int offset,
+       size_t len, enum async_tx_flags flags,
+       struct dma_async_tx_descriptor *depend_tx,
+       dma_async_tx_callback callback, void *callback_param);
+struct dma_async_tx_descriptor *
+async_interrupt(enum async_tx_flags flags,
+       struct dma_async_tx_descriptor *depend_tx,
+       dma_async_tx_callback callback, void *callback_param);
+struct dma_async_tx_descriptor *
+async_interrupt_cond(enum dma_transaction_type next_op,
+       enum async_tx_flags flags,
+       struct dma_async_tx_descriptor *depend_tx);
diff --git a/include/linux/raid/xor.h b/include/linux/raid/xor.h
index f0d67cb..d151f16 100644
--- a/include/linux/raid/xor.h
+++ b/include/linux/raid/xor.h
@@ -3,9 +3,10 @@
 
 #include <linux/raid/md.h>
 
-#define MAX_XOR_BLOCKS 5
+#define MAX_XOR_BLOCKS 4
 
-extern void xor_block(unsigned int count, unsigned int bytes, void **ptr);
+extern void xor_block(unsigned int count, unsigned int bytes,
+       void *dest, void **srcs);
 
 struct xor_block_template {
         struct xor_block_template *next;
-
To unsubscribe from this list: send the line "unsubscribe linux-raid" in
the body of a message to [EMAIL PROTECTED]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to