ScatterList基础
scatterlist, 全称物理内存散列表, 当存在一片连续的虚拟内存区域时, 由于该虚拟内存可能对应着多个非连续的物理内存区域, Linux内核使用scatterlist来表示表示va和pa之间的映射关系. 一个scatterlist结构体对象就表示其中的一片连续的物理内存区域:
struct scatterlist {
unsigned long page_link; //该内存区域所在的虚拟页面地址。bit0和bit1有特殊用途(可参考后面的介绍),因此要求page最低4字节对齐
unsigned int offset; //指示该内存区域在页面中的偏移(起始位置)
unsigned int length; //该内存区域的长度。
dma_addr_t dma_address; // 内存区域的物理起始地址
#ifdef CONFIG_NEED_SG_DMA_LENGTH
unsigned int dma_length;
#endif
};
在实际的应用场景中,单个的scatterlist对象是没有多少意义的,我们需要多个scatterlist组成一个链表数组,以表示在物理上不连续的虚拟地址空间. kernel中可以用sg_table表示这种数组(也可以不用):
struct sg_table {
struct scatterlist *sgl; /* the list */
unsigned int nents; /* number of mapped entries */
unsigned int orig_nents; /* original size of list */
};
其中sgl
表示一个scatterlist的数组, orig_nents为该数组的大小, nents为该数组中有效元素的个数.
而scatterlist数组中到底有多少有效的scatterlist, 则是由以下2个规则确定:
-
如果scatterlist数组中某个scatterlist的page_link的bit0为1,表示该scatterlist不是一个有效的内存块,而是一个chain(铰链),指向另一个scatterlist数组。通过这种机制,可以将不同的scatterlist数组链在一起,因为scatterlist也称作chain scatterlist。
-
如果scatterlist数组中某个scatterlist的page_link的bit1为1,表示该scatterlist是scatterlist数组中最后一个有效内存块(后面的就忽略不计了)。
API
sg_init_table
static inline void sg_init_table(struct scatterlist *sgl, unsigned int nents)
初始化一个散列表, nents为有效的内存块个数.