DMA scatterlist
DMA scatterlist
DMA直接以物理地址形式访问memory,软件进行一系列操作之后,只要把存储空间交给DMA控制器,最终由DMA控制器将数据搬移给某个外设是,由于DMA控制器只能访问物理地址,只能以不连续的物理内存块为单位递交。
所以需要用一个数据结构来描述一个个不连续的物理内存,这个数据结构就是scatterlist
。scatterlist
本身并不是一种链表的结构,只是用来描述一个单独的内存块。多个scatterlist
组合在一起形成一个表,可以是scatterlist
类型的数组,也可以是内核抽象出来的sg_table
scatterlist
就是DMA物理地址映射虚拟地址的数据结构,多个scatterlist
API描述
struct scatterlist
用于描述在一个物理地址上连续的内存块(以page为单位)
1 | /* |
struct sg_table
在实际的应用场景中,单个的scatterlist
是没有多少意义的,我们需要多个scatterlist
组成一个数组,以表示在物理上不连续的虚拟地址空间。通常情况下,使用scatterlist
功能的模块,会自行维护这个数组(指针和长度)。另外kernel抽象出来了一个简单的数据结构:struct sg_table
,帮忙保存scatterlist
的数组指针和长度:
1 | struct sg_table { |
创建scatterlist
链表
创建一个scatterlist链有两种方式,一种是静态声明,并调用API进行初始化
1 | struct scatterlist sgt[8]; |
sg_init_table()函数定义如下
1 | /** |
传入的参数sgl是scatterlist的数组地址,nents是数组长度,函数内部将数组内存初始化,然后调用了sg_mark_end
将数组的最后一个元素的page_link
标记为尾节点
另一种方法是动态创建
1 | struct sg_table sgt; |
sg_alloc_table()函数定义如下
1 | /** |
其内部通过__sg_alloc_table()
来动添申请内存创建。这里的SG_MAX_SINGLE_ALLOC
是一个由于各种原因的限制,即动态申请的连续内存的sg_table
必须在一个页面中,默认一个页面4096的话,scatterlist
结构体大小20字节,那么一个页面最大能够创建大小为204的scatterlist
链,而如果要创建更大数目的话,需要通过铰链的方式将多个分散在不同页面的scatterlist
链串联起来,这在__sg_alloc_table()
内部已经实现,用户不需要关心
使用
通常的应用场景是将应用程序分配的buffer映射到sg table上
1 | void map_user_buf_to_sgl(struct sg_table *sgt, void __user *buf, int len) |
首先根据buf和len得到页面数量,然后根据页面数量创建对应长度的sg_table
,然后获取buf对应的页面,最后遍历sg_table
,通过sg_set_page()
将页面设置到scatterlist上
其他的一些API
sg_alloc_table_from_pages
:从一组页面创建sg_table
sg_nents
:获取sg的entity数量sg_copy_from_buffer
:从一个连续的buffer拷贝到sg_list
sg_chain
:将两个单独的sg_list
串联