scatter/gather I/O

发散/汇聚映射
通用DMA框架还提供了一种特殊类型的流DMA映射机制–发散/汇聚映射。该机制允许一次为多个缓冲区创建DMA映射。其原型如下:
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方式效率高。

猜你喜欢

转载自blog.csdn.net/shenjin_s/article/details/80855762