2012年(7)
分类: C/C++
2012-06-11 22:35:24
/**
* async_memcpy - attempt to copy memory with a dma engine.
* @dest: destination page
* @src: src page
* @dest_offset: offset into 'dest' to start transaction
* @src_offset: offset into 'src' to start transaction
* @len: length in bytes
* @submit: submission / completion modifiers
*
* honored flags: ASYNC_TX_ACK
*/
首先关注一下入参,类似memcpy的用法,目的page,源page
注意,如果你要copy的数据,不满一个4k页面,就需要指定长度,目的偏移和源地址偏移
需要copy的长度,
唯一和一般memcpy操作不同的是最后一个异步提交管理结构体
struct dma_async_tx_descriptor *
async_memcpy(struct page *dest, struct page *src, unsigned int dest_offset,
unsigned int src_offset, size_t len,
struct async_submit_ctl *submit)
{
struct dma_chan *chan = async_tx_find_channel(submit, DMA_MEMCPY,
&dest, 1, &src, 1, len);
struct dma_device *device = chan ? chan->device : NULL;
struct dma_async_tx_descriptor *tx = NULL;
if (device && is_dma_copy_aligned(device, src_offset, dest_offset, len)) {
dma_addr_t dma_dest, dma_src;
unsigned long dma_prep_flags = 0;
if (submit->cb_fn)
dma_prep_flags |= DMA_PREP_INTERRUPT;
if (submit->flags & ASYNC_TX_FENCE)
dma_prep_flags |= DMA_PREP_FENCE;
/* 如果设置了回调,或者FENCE标志,则需要设置不同的标志位 */
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, submit);
} else {
void *dest_buf, *src_buf;
pr_debug("%s: (sync) len: %zu\n", __func__, len);
/* wait for any prerequisite operations */
async_tx_quiesce(&submit->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(submit);
}
return tx;
}
首先注意一下,整个大的流程分成2部分,在接口中首先尝试获取dma驱动注册的设备,如果设备不存在,则使用同步方式也就是用cpu来进行搬运
换句话说,这个时候的async_memcpy和memcpy没有区别。分水岭就在判断tx是否为空上
最开始的时候,先通过async_tx_find_channel查找dma通道,注意这里是抽象概念,原来我们提到过,cpu厂商会提供几个硬件加速的能力
抽象成了通道,而有的厂商通过设置不同的寄存器,可以让通道完成不一样的功能。也有的厂商会写死通道能力,比如0号通道提供xor能力,其他的
通道提供内存加速能力,这些都要去看cpu手册。
然后通过通道找设备,如果设备在的话,注意这里有个判断is_dma_copy_aligned 地址是否对齐。也就是说使用的时候,如果cpu要求对其,则需要小心对齐问题,不然就不会用DMA搬运,而还是cpu搬运。
这里可以看到async_xxx接口屏蔽了cpu的能力,让上层应用,这里主要指的是raid,不需要关注cpu是否有DMA能力。只需要调用async接口就可以
应用处理可以简单很多。
dma_map_page起到的作用是,将应用给的地址,映射到dma设备可以看见的地址。
device_prep_dma_memcpy是调用dma驱动的接口,目的是做一些驱动内部的DMA准备,分配一些资源等等。这个和cpu有关,不一样的cpu,就有不一样的DMA加速驱动,内部实现机制也不同,有的是用中断,有的是发送消息等等,这里让驱动自己去准备。
当一切都正常后,这个接口返回一个dma_async_tx_descriptor,描述符
通过intel在内核中的驱动,Dma_v2.c,我们可以看到这个描述符tx,通常在一个DMA驱动的管理结构体的内部,通过这个tx,驱动可以找到内部管理结构。
当我们判断出tx不是空的时候,最后调用async_tx_submit来提交