linux scatterlist源码阅读记录

准备工作

1:可以根据某个版本进行源码的下载

源码位置

scatterlist.h
#include <linux/scatterlist.h>
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 */
};

主要API学习1

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 <linux/init.h>    
#include <linux/module.h>    
#include <linux/kernel.h>    
#include <linux/fs.h>  
#include <asm/uaccess.h>  
#include <linux/spinlock.h>  
#include <linux/sched.h>  
#include <linux/types.h>  
#include <linux/fcntl.h>  
#include <linux/hdreg.h>  
#include <linux/genhd.h>  
#include <linux/blkdev.h>  
#include <linux/scatterlist.h>

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)))

sg_next接口学习

这个接口是用来遍历所有的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);

sg_nents接口学习

这个结构是统计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);

sg_last接口学习

这个接口是为了找到从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版本的。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值