1:可以根据某个版本进行源码的下载
scatterlist.h
#include
scatterlist.c
lib目录
一个是scatterlist结构体,如下,这个结构体主要是用来记录地址的,
记录什么地址?一个是虚拟地址(内核态虚拟地址或者是用户态虚拟地址),一个是设备地址(设备认识的地址,一般来说就是dma地址/总线地址),怎么使用后面在针对它提供的api接口来说明。
struct scatterlist {
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_table,这个也是一个结构体,如果要表示多个scatterlist结构体的话,一般来说有一个scatterlist类型的指针就可以了,内核为了操作方便,设计了sg_table这个结构体来表示有多个scatterlist结构体,其意思都大差不差。
struct sg_table {
struct scatterlist *sgl; /* the list */
unsigned int nents; /* number of mapped entries */
unsigned int orig_nents; /* original size of list */
};
1:sg_alloc_table和sg_free_table
int sg_alloc_table(struct sg_table *table, unsigned int nents, gfp_t gfp_mask)
{
int ret;
ret = __sg_alloc_table(table, nents, SG_MAX_SINGLE_ALLOC,
NULL, gfp_mask, sg_kmalloc);
if (unlikely(ret))
__sg_free_table(table, SG_MAX_SINGLE_ALLOC, false, sg_kfree);
return ret;
}
EXPORT_SYMBOL(sg_alloc_table);
void sg_free_table(struct sg_table *table)
{
__sg_free_table(table, SG_MAX_SINGLE_ALLOC, false, sg_kfree);
}
EXPORT_SYMBOL(sg_free_table);
如何使用呢?写了个简单的demo,我们来研究下,
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
struct sg_table sgt;
/* 驱动出口函数 */
static int __init ramdisk_init(void)
{
int ret, i;
printk("sg init\n");
ret = sg_alloc_table(&sgt, 150, GFP_KERNEL);
if(ret == 0)
printk("orig_nents = %d, nents = %d, %ld\n", sgt.orig_nents, sgt.nents, SG_MAX_SINGLE_ALLOC);
struct scatterlist *sg;
for_each_sg(sgt.sgl, sg, 150, i) {
if (sg_is_last(sg))
printk("last i = %d, %d\n", i, sg->dma_length);
if (sg_is_chain(sg))
printk("chain i = %d\n", i);
printk("sg->page_link = 0x%lx\n", sg->page_link);
}
return 0;
}
/* 驱动出口函数 */
static void __exit ramdisk_exit(void)
{
printk("sg exit\n");
sg_free_table(&sgt);
}
module_init(ramdisk_init);
module_exit(ramdisk_exit);
MODULE_AUTHOR("Tan xujia");
MODULE_LICENSE("Dual BSD/GPL");
Makefile:
KERNELDIR ?= /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)
obj-m :=sgt.o
all:
make -C $(KERNELDIR) M=$(PWD) modules
clean:
rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.* .tmp_versions *.mod * .order *.symvers *.dwo
这个例子跑的时候看结果感觉不太符合预期,当参数给的150,是大于SG_MAX_SINGLE_ALLOC的,但是遍历的时候,按理127个元素的时候,他的page_link的bit0应该是1的,但是打印出来却不是1,有点奇怪了。
SG_MAX_SINGLE_ALLOC这个宏的数值是128,仔细看源码,起始发现,scatterlist这个结构体申请内存的话,是通过kmalloc_array接口或者是其它的接口,当你申请的scatterlist结构体的个数大于128时,是通过page_link去记录第129个元素的起始地址的,然后到了257个元素时也是一样,以此类推,可以通过打印page_link的第0个bit和第1个bit来判断当前的scatterlist结构体元素在整个的scatterlist结构体指针指向的所有元素当中,是不是最后一个,或者是不是指向下一个链表元素,系统提供了类似的宏做判断,
#define SG_CHAIN 0x01UL
#define SG_END 0x02UL
/*
* We overload the LSB of the page pointer to indicate whether it's
* a valid sg entry, or whether it points to the start of a new scatterlist.
* Those low bits are there for everyone! (thanks mason :-)
*/
#define sg_is_chain(sg) ((sg)->page_link & SG_CHAIN)
#define sg_is_last(sg) ((sg)->page_link & SG_END)
#define sg_chain_ptr(sg) \
((struct scatterlist *) ((sg)->page_link & ~(SG_CHAIN | SG_END)))
这个接口是用来遍历所有的scatterlist元素的,比如在上面的例子当中,可以给sg_table结构体里面的sgl成员到实参去。
/**
* sg_next - return the next scatterlist entry in a list
* @sg: The current sg entry
*
* Description:
* Usually the next entry will be @sg@ + 1, but if this sg element is part
* of a chained scatterlist, it could jump to the start of a new
* scatterlist array.
*
**/
struct scatterlist *sg_next(struct scatterlist *sg)
{
if (sg_is_last(sg))
return NULL;
sg++;
if (unlikely(sg_is_chain(sg)))
sg = sg_chain_ptr(sg);
return sg;
}
EXPORT_SYMBOL(sg_next);
这个结构是统计scatterlist结构体元素个数的,直到通过判断最后一个元素的page_link的第1个bit位来判断是不是遍历到了最后一个元素
/**
* sg_nents - return total count of entries in scatterlist
* @sg: The scatterlist
*
* Description:
* Allows to know how many entries are in sg, taking into acount
* chaining as well
*
**/
int sg_nents(struct scatterlist *sg)
{
int nents;
for (nents = 0; sg; sg = sg_next(sg))
nents++;
return nents;
}
EXPORT_SYMBOL(sg_nents);
这个接口是为了找到从scatterlist结构体开始的起始地址,找到最后一个scatterlist 成员的。
/**
* sg_last - return the last scatterlist entry in a list
* @sgl: First entry in the scatterlist
* @nents: Number of entries in the scatterlist
*
* Description:
* Should only be used casually, it (currently) scans the entire list
* to get the last entry.
*
* Note that the @sgl@ pointer passed in need not be the first one,
* the important bit is that @nents@ denotes the number of entries that
* exist from @sgl@.
*
**/
struct scatterlist *sg_last(struct scatterlist *sgl, unsigned int nents)
{
struct scatterlist *sg, *ret = NULL;
unsigned int i;
for_each_sg(sgl, sg, nents, i)
ret = sg;
BUG_ON(!sg_is_last(ret));
return ret;
}
EXPORT_SYMBOL(sg_last);
关于scatterlist的接口先写这么多,知识一下子总归是吸收不完的,贴出来的代码是4.19.222版本的。