发散/汇聚映射
int dma_map_sg(struct device *dev, struct scatterlist *sg, int nents, enum dma_data_direction direction);
各参数含义如下:
dev:设备数据结构指针 sg:缓冲区列表的第一个缓冲区的指针 nets:sg中有多少个缓冲区 direction:数据流动方向
该函数的返回值是成功映射了多少个缓冲区。如果在分散/汇聚列表中一些缓冲的物理地址或虚拟地址相邻的,且IOMMU可以将它们映射成单个内存块,则返回值可能比输入值nents小。
数据结构scatterlist包含了每个缓冲区的信息,其定义如下:
struct scatterlist { #ifdef CONFIG_DEBUG_SG unsigned long sg_magic; #endif unsigned long page_link; unsigned int offset; unsigned int length; dma_addr_t dma_address; #ifdef CONFIG_NEED_SG_DMA_LENGTH unsigned int dma_length; #endif };
注意如果sg已经映射过了,则不能再对其进行映射,再次映射会损坏sg中的信息。对于sg中的每个缓冲,该函数会正确的为
其产生设备总线地址,驱动应该使用该总线地址,内核提供了两个相关的宏:
dma_addr_t sg_dma_address(struct scatterlist *sg);
用于从scatterlist返回总线( DMA )地址.
unsigned int sg_dma_len(struct scatterlist *sg);
用于返回这个缓冲的长度.
void dma_unmap_sg(struct device *dev, struct scatterlist *list, int nents, enum dma_data_direction direction);
该函数用于取消发散/汇聚映射。netns必须等于传给dma_map_sg的值,而不是dma_map_sg返回的值。
类似于单一映射,如果CPU必须访问已经映射了的缓冲区,则必须先让CPU获取这些缓冲区,对应的API如下:
void dma_sync_sg_for_cpu(struct device *dev, struct scatterlist *sg, int nents, enum dma_data_direction direction); void dma_sync_sg_for_device(struct device *dev, struct scatterlist *sg, int nents, enum dma_data_direction direction);
scatter/gather方式是与block dma方式相对应的一种dma方式。
在dma传输数据的过程中,要求源物理地址和目标物理地址必须是连续的。但在有的计算机体系中,如IA,连续的存储器地址在物理上不一定是连续的,则dma传输要分成多次完成。
如果传输完一块物理连续的数据后发起一次中断,同时主机进行下一块物理连续的传输,则这种方式即为block dma方式。
scatter/gather方式则不同,它是用一个链表描述物理不连续的存储器,然后把链表首地址告诉dma master。
dma master传输完一块物理连续的数据后,就不用再发中断了,而是根据链表传输下一块物理连续的数据,最后发起一次中断。
很显然scatter/gather方式比block dma方式效率高。