浅析Linux内核scatter-gather list实现

文章目录

概述

Linux Scatter-Gather List(离散/聚合列表,简称SGL)是Linux内核中用于描述物理内存不连续内存块的数据结构,主要用于DMA传输和IO操作。现代的DMA控制器普遍都支持Scatter-Gather方式进行数据传输,通过SGL直接描述数据分布,可以避免单独申请物理连续内存拷贝数据,减少CPU参于的数据搬运,提升IO效率。

数据结构

Linux内核使用struct sg_table结构描述Scatter-Gather List,其定义如下;

复制代码
struct sg_table {
	struct scatterlist *sgl;	  // SGL表项数组首地址
	unsigned int nents;    // 已映射的SGL表项数量
	unsigned int orig_nents;	// SGL表项总数量
};

sg_table结构记录了SGL表项数组的首地址,具体的SGL表项信息由struct scatterlsit结构进行描述,当数据块较多,单个SGL表项数组无法记录时,数组内的最后一个SGL表项会指向下一个SGL表项数组,构成多级SGL,如下:

单个SGL表项数组支持的数量由SG_MAX_SINGLE_ALLOC进行定义,并与物理内存页面的大小强相关,如下:

复制代码
#define SG_MAX_SINGLE_ALLOC		(PAGE_SIZE / sizeof(struct scatterlist))

struct scatterlist

struct scatterlist是Linux内核SGL的核心数据结构,用于描述一个物理地址连续的内存块,它的定义如下:

复制代码
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
#ifdef CONFIG_NEED_SG_DMA_FLAGS
	unsigned int    dma_flags;
#endif
};

数据结构主要字段意义如下:

  • page_link:记录数据块所在物理页面信息,其中page_link低2位保留,有特殊含义:
    • bit 0:SG_CHAIN,记录下一个 SGL表项数组的首地址;
    • bit 1:SG_END,指示当前SGL表项是SGL中的最后一个表项。
  • offset:记录数据块在物理页面中起始偏移;
  • length:记录数据块大小;
  • dma_address:DMA地址,设备传输数据使用。

API操作接口

申请分配SGL

复制代码
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, 0, gfp_mask, sg_kmalloc);
	if (unlikely(ret))
		sg_free_table(table);
	return ret;
}

遍历SGL表项

for_each_sg遍历SGL中所有的有效scatterlist:

复制代码
#define for_each_sg(sglist, sg, nr, __i)	\
	for (__i = 0, sg = (sglist); __i < (nr); __i++, sg = sg_next(sg))

sg_next返回当前SGL表项的下一个表项,通常下一个表项位于SGL表项数组的下一项,在多级SGL的情况下,可能跳到下一级SGL表项数组的首项。

复制代码
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;
}

SGL数据拷贝

SGL API提供了sg_copy_from_buffersg_copy_to_buffer接口,用于从线性缓冲区拷贝数据到分离的数据块,或反之。sg_copy_xxx_buffer调用sg_copy_buffer进行实现:

复制代码
size_t sg_copy_buffer(struct scatterlist *sgl, unsigned int nents, void *buf,
		      size_t buflen, off_t skip, bool to_buffer)
{
	unsigned int offset = 0;
	struct sg_mapping_iter miter;
	unsigned int sg_flags = SG_MITER_ATOMIC;

	if (to_buffer)
		sg_flags |= SG_MITER_FROM_SG;
	else
		sg_flags |= SG_MITER_TO_SG;

	sg_miter_start(&miter, sgl, nents, sg_flags);

	if (!sg_miter_skip(&miter, skip))
		return 0;

	while ((offset < buflen) && sg_miter_next(&miter)) {
		unsigned int len;

		len = min(miter.length, buflen - offset);

		if (to_buffer)
			memcpy(buf + offset, miter.addr, len);
		else
			memcpy(miter.addr, buf + offset, len);

		offset += len;
	}

	sg_miter_stop(&miter);

	return offset;
}

相关参考

相关推荐
柏木乃一14 小时前
进程(6)进程切换,Linux中的进程组织,Linux进程调度算法
linux·服务器·c++·算法·架构·操作系统
Jelly-小丑鱼14 小时前
Linux搭建SQLserver数据库和Orical数据库
linux·运维·数据库·sqlserver·oracal·docker容器数据库
CAU界编程小白14 小时前
Linux编程系列之权限理解和基础开发工具的使用(下)
linux
Run_Teenage14 小时前
Linux:进程等待
linux·运维·服务器
Trouvaille ~14 小时前
【Linux】从磁盘到文件系统:深入理解Ext2文件系统
linux·运维·网络·c++·磁盘·文件系统·inode
wdfk_prog14 小时前
[Linux]学习笔记系列 -- [fs]file
linux·笔记·学习
___波子 Pro Max.14 小时前
Linux ps命令-ef参数详解
linux
春日见14 小时前
眼在手上外参标定保姆级教学(vscode + opencv)
linux·运维·服务器·数码相机·opencv·ubuntu·3d
xwill*15 小时前
python 字符串拼接
linux·windows·python
TracyGC16 小时前
Linux环境-RTX5080显卡CUDA12.8下安装mmcv/mmdetection3d
linux·运维·服务器