"分散-聚集DMA"以及"scatterlist"

来源:互联网 发布:linux复制所有文件 编辑:程序博客网 时间:2024/05/17 10:43

“分散-聚集DMA”以及”scatterlist”

我们知道对磁盘的每个IO操作就是在磁盘与一些内存单元之间相互传送一些相邻扇区的内容。块设备驱动程序只要向磁盘控制器发送一些适当的命令(如前面我们所讲的scsi命令)就可以触发一次数据传送;一旦完成数据的传送,控制器就会发出一个中断通知块设备驱动程序。大多数情况,磁盘控制器直接采用DMA方式进行数据传送。

DMA

DMA全称就是直接内存访问,是一种硬件机制,它允许外围设备和主内存直接直接进行数据传输而不需要cpu的参与。

分散-聚集DMA

老式磁盘控制器在DMA传送时,磁盘必须与连续的内存单元相互传送数据。
新的磁盘控制器支持所谓的分散-聚集DMA传送方式:此种方式,磁盘可以与一些非连续的内存区相互传送数据。

启动一次分散-聚集DMA传送,块设备驱动需要向磁盘控制器发送:

  • 要传送的起始磁盘扇区号和总的扇区数
  • 内存区的描述符链表,其中链表的每项包含一个地址和一个长度(在内存页中位置以及大小)

分散/聚集映射

如果块设备驱动程序在实际传输过程中需要使用DMA,那么它可以遍历bio结构,并为
每个bio结构创建DMA映射,并将结果传递给设备。如果设备支持”分散-聚集DMA“
则有一个更简便高效的方法:
int blk_rq_map_sg(struct request_queue *q, struct request *rq, struct scatterlist *sglist)
从指定请求获得全部的段(段指bio中的每一个segment),然后把它们填写到给定表(list)中。内存中相邻的段将被合并在一个scatterlist中,返回值表示的是表中入口项的个数(表中有效元素个数),该函数使用第三个参数返回一个分散表。
在调用blk_rq_map_sg前,必须为分散表(list)分配存储空间,至少与request请求所拥有的(内存)物理段同样多的数量,即req->nr_phys_segments。

许多设备都能接受一个指针数组的分散表,以及它的长度,然后在一次DMA操作中把它们全部传输走。
映射分散表的第一步是建立并填充一个描述 被传送缓冲区的 scatterlist结构的数组。
scatterlist结构的主要成员:

  • unsigned long page_link;//一般指在scatter/gather操作用到缓冲区对应的page结构指针
  • unsigned int offset;
  • unsigned int length;
    //在页内缓冲区的长度和偏移量

为了映射一个分散/聚集DMA操作,驱动程序应为传输的每个缓冲区在scatterlist结构对应入口项上设置page_link,offset和length成员。(这一步由上述blk_rq_map_sg完成)
然后调用:
int dma_map_sg(struct device *dev, struct scatterlist *sg, int nents,
enum dma_data_direction direction);
这里的nents是传入分散表sg入口的数量。返回值是要传送DMA缓冲区数,可能小于nents。
对在分散表中每一个缓冲区,dma_map_sg返回了指定设备的正确总线地址,总线地址和每个缓冲区长度被保存在scatterlist结构中,驱动程序应该传输由dma_map_sg返回的每个缓冲区。
一旦传输完毕,使用dma_unmap_sg解除分散/聚集映射。