函数pci_map_sg完成分散/集中映射,其返回值是要传送的 DMA 缓冲区数;它可能会小于nents(也就是传入的分散表项的数量),因为可能有的缓冲区地址上是相邻的。一旦传输完成,分散/集中映射通过调用函数pci_unmap_sg 来撤销映射。
函数pci_map_sg分析如下(在include/asm-generic/pci-dma-compat.h中):
static inline int pci_map_sg(struct pci_dev *hwdev, struct scatterlist *sg,
int nents, int direction)
{
return dma_map_sg(hwdev == NULL ? NULL : &hwdev->dev, sg, nents,
(enum dma_data_direction)direction);
}
include/asm-i386/dma-mapping.h
static inline int dma_map_sg(struct device *dev, struct scatterlist *sg,
int nents, enum dma_data_direction direction)
{
int i;
BUG_ON(direction == DMA_NONE);
for (i = 0; i < nents; i++ ) {
BUG_ON(!sg.page);
//将页及页偏移地址转化为物理地址
sg.dma_address = page_to_phys(sg.page) + sg.offset;
}
//可能有些数据还会保留在处理器的高速缓冲存储器中,因此必须显式刷新
flush_write_buffers();
return nents;
}
DMA池
许多驱动程序需要又多又小的一致映射内存区域给DMA描述子或I/O缓存buffer,这使用DMA池比用dma_alloc_coherent分配的一页或多页内存区域好,DMA池用函数dma_pool_create创建,用函数dma_pool_alloc从DMA池中分配一块一致内存,用函数dmp_pool_free放内存回到DMA池中,使用函数dma_pool_destory释放DMA池的资源。
结构dma_pool是DMA池描述结构,列出如下:
struct dma_pool { /* the pool */
struct list_head page_list;//页链表
spinlock_t lock;
size_t blocks_per_page;//每页的块数
size_t size; //DMA池里的一致内存块的大小
struct device *dev; //将做DMA的设备
size_t allocation; //分配的没有跨越边界的块数,是size的整数倍
char name [32];//池的名字
wait_queue_head_t waitq; //等待队列
struct list_head pools;
};
函数dma_pool_create给DMA创建一个一致内存块池,其参数name是DMA池的名字,用于诊断用,参数dev是将做DMA的设备,参数size是DMA池里的块的大小,参数align是块的对齐要求,是2的幂,参数allocation返回没有跨越边界的块数(或0)。
函数dma_pool_create返回创建的带有要求字符串的DMA池,若创建失败返回null。对被给的DMA池,函数dma_pool_alloc被用来分配内存,这些内存都是一致DMA映射,可被设备访问,且没有使用缓存刷新机制,因为对齐原因,分配的块的实际尺寸比请求的大。如果分配非0的内存,从函数dma_pool_alloc返回的对象将不跨越size边界(如不跨越4K字节边界)。这对在个体的DMA传输上有地址限制的设备来说是有利的。
函数dma_pool_create分析如下(在drivers/base/dmapool.c中):
struct dma_pool *dma_pool_create (const char *name, struct device *dev,
size_t size, size_t align, size_t allocation)
{
struct dma_pool *retval;
if (align == 0)
align = 1;
if (size == 0)
return NULL;
else if (size < align)
size = align;
else if ((size % align) != 0) {//对齐处理
size += align + 1;
size &= ~(align - 1);
}
//如果一致内存块比页大,是分配为一致内存块大小,否则,分配为页大小
if (allocation == 0) {
if (PAGE_SIZE < size)//页比一致内存块小
allocation = size;
else
allocation = PAGE_SIZE;//页大小
// FIXME: round up for less fragmentation
} else if (allocation < size)
return NULL;
//分配dma_pool结构对象空间
if (!(retval = kmalloc (sizeof *retval, SLAB_KERNEL)))
return retval;
strlcpy (retval->name, name, sizeof retval->name);
retval->dev = dev;
|