88ba2aa586
In support of inter-channel chaining async_tx utilizes an ack flag to gate whether a dependent operation can be chained to another. While the flag is not set the chain can be considered open for appending. Setting the ack flag closes the chain and flags the descriptor for garbage collection. The ASYNC_TX_DEP_ACK flag essentially means "close the chain after adding this dependency". Since each operation can only have one child the api now implicitly sets the ack flag at dependency submission time. This removes an unnecessary management burden from clients of the api. [ Impact: clean up and enforce one dependency per operation ] Reviewed-by: Andre Noll <maan@systemlinux.org> Acked-by: Maciej Sosnowski <maciej.sosnowski@intel.com> Signed-off-by: Dan Williams <dan.j.williams@intel.com>
109 lines
3.1 KiB
C
109 lines
3.1 KiB
C
/*
|
|
* copy offload engine support
|
|
*
|
|
* Copyright © 2006, Intel Corporation.
|
|
*
|
|
* Dan Williams <dan.j.williams@intel.com>
|
|
*
|
|
* with architecture considerations by:
|
|
* Neil Brown <neilb@suse.de>
|
|
* Jeff Garzik <jeff@garzik.org>
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify it
|
|
* under the terms and conditions of the GNU General Public License,
|
|
* version 2, as published by the Free Software Foundation.
|
|
*
|
|
* This program is distributed in the hope 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.,
|
|
* 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
|
|
*
|
|
*/
|
|
#include <linux/kernel.h>
|
|
#include <linux/highmem.h>
|
|
#include <linux/mm.h>
|
|
#include <linux/dma-mapping.h>
|
|
#include <linux/async_tx.h>
|
|
|
|
/**
|
|
* 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_ACK
|
|
* @depend_tx: memcpy depends on the result of this transaction
|
|
* @cb_fn: function to call when the memcpy completes
|
|
* @cb_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 cb_fn, void *cb_param)
|
|
{
|
|
struct dma_chan *chan = async_tx_find_channel(depend_tx, DMA_MEMCPY,
|
|
&dest, 1, &src, 1, len);
|
|
struct dma_device *device = chan ? chan->device : NULL;
|
|
struct dma_async_tx_descriptor *tx = NULL;
|
|
|
|
if (device) {
|
|
dma_addr_t dma_dest, dma_src;
|
|
unsigned long dma_prep_flags = cb_fn ? DMA_PREP_INTERRUPT : 0;
|
|
|
|
dma_dest = dma_map_page(device->dev, dest, dest_offset, len,
|
|
DMA_FROM_DEVICE);
|
|
|
|
dma_src = dma_map_page(device->dev, src, src_offset, len,
|
|
DMA_TO_DEVICE);
|
|
|
|
tx = device->device_prep_dma_memcpy(chan, dma_dest, dma_src,
|
|
len, dma_prep_flags);
|
|
}
|
|
|
|
if (tx) {
|
|
pr_debug("%s: (async) len: %zu\n", __func__, len);
|
|
async_tx_submit(chan, tx, flags, depend_tx, cb_fn, cb_param);
|
|
} else {
|
|
void *dest_buf, *src_buf;
|
|
pr_debug("%s: (sync) len: %zu\n", __func__, len);
|
|
|
|
/* wait for any prerequisite operations */
|
|
async_tx_quiesce(&depend_tx);
|
|
|
|
dest_buf = kmap_atomic(dest, KM_USER0) + dest_offset;
|
|
src_buf = kmap_atomic(src, KM_USER1) + src_offset;
|
|
|
|
memcpy(dest_buf, src_buf, len);
|
|
|
|
kunmap_atomic(dest_buf, KM_USER0);
|
|
kunmap_atomic(src_buf, KM_USER1);
|
|
|
|
async_tx_sync_epilog(cb_fn, cb_param);
|
|
}
|
|
|
|
return tx;
|
|
}
|
|
EXPORT_SYMBOL_GPL(async_memcpy);
|
|
|
|
static int __init async_memcpy_init(void)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static void __exit async_memcpy_exit(void)
|
|
{
|
|
do { } while (0);
|
|
}
|
|
|
|
module_init(async_memcpy_init);
|
|
module_exit(async_memcpy_exit);
|
|
|
|
MODULE_AUTHOR("Intel Corporation");
|
|
MODULE_DESCRIPTION("asynchronous memcpy api");
|
|
MODULE_LICENSE("GPL");
|