Linux swiotlb技术解析

本文介绍了Linux系统中swiotlb技术,一种针对32位DMA寻址限制的软件地址映射方法。swiotlb通过维护低地址buffer,解决超过寻址范围的问题。在物理页映射、数据同步和解除映射过程中,它使用映射表记录原始地址和分配地址的关系,并进行数据同步。文章详细分析了swiotlb的初始化、映射、同步和解除映射的代码流程。
摘要由CSDN通过智能技术生成

概述


swiotlb技术是一种纯软件的地址映射技术,主要为寻址能力受限的DMA提供软件上的地址映射,听起来比较玄乎,实际上其原理非常简单。我们下面先来谈一下该技术提出的背景。
我们假设一个64位系统,其内存的基地址是0x80000000,内存大小是4G,则内存的物理地址范围是0x80000000-0x180000000,同时该系统中某个外设的DMA只能按32 bits地址寻址,即寻址范围为0-0xffffffff,如果恰好非给该外设DMA的内存地址超过了这个范围,该外设DMA实际无法使用该地址。那么怎么办呢?此时swiotlb就登上了舞台!
swiotlb维护了一块低地址的buffer,该buffer的大小可以由bootloader通过swiotlb参数传递给内核,也可以使用默认值,默认值是64M。
如果给DMA分配的物理地址(这里记做phyaddr1)超过了其寻址能力,那么swiotlb技术将从其buffer中分配同样大小的一块空间,给DMA使用,其物理地址记做phyaddr2,swiotlb会维护phyaddr1和phyaddr2的映射关系。
这里有两个问题比较关键:
第一个问题是:如何确保swiotlb维护的buffer在低地址呢?
这个问题很好回答,只要尽可能早地分配该buffer就可以了,只要分配的足够早,想要哪一块buffer还不是一句话的事!在ARM64平台上在函数mem_init的一开始,就调用swiotlb_init做swiotlb的初始化,swiotlb_init中就会分配所需要的buffer。
第二个问题是:一般来说先分配page,然后映射其物理地址(这里我们暂时认为是物理地址,其实是DMA地址),如果在映射物理地址的时候发现物理地址超过DMA的寻址能力,就会偷偷从buffer中申请一块等大小的内存,来替换掉原来内存地址,给DMA使用。但是此时物理page和DMA实际使用的物理地址不一致,也就是说DMA看到内存和CPU看到的内存实际上是两块内存,这两块内存没有任何关系。
以上分析除了最后一句之外,全部是正确的。这两个内存是有关系的,他们的关系就是swiotlb维护了一张io_tlb_orig_addr表,这张表维护了原始物理地址和swiotlb分配物理地址之间的映射关系。当DMA将数据写入swiotlb分配的物理地址后,一般会以中断的方式通知CPU,CPU在查看DMA写到内存的数据之前,需要先做一个sync,该sync操作就会触发swiotlb将数据拷贝到原始物理地址处,这样就保证了CPU能够看到正确的数据,不过显而易见,这种方法效率非常低下。

代码分析


swiotlb初始化

mem_init->swiotlb_init
void  __init
swiotlb_init(int verbose)
{
	size_t default_size = IO_TLB_DEFAULT_SIZE;
	unsigned char *vstart;
	unsigned long bytes;

	if (!io_tlb_nslabs) {
		io_tlb_nslabs = (default_size >> IO_TLB_SHIFT);
		io_tlb_nslabs = ALIGN(io_tlb_nslabs, IO_TLB_SEGSIZE);
	}

	bytes = io_tlb_nslabs << IO_TLB_SHIFT;

	/* Get IO TLB memory from the low pages */
	vstart = memblock_virt_alloc_low_nopanic(PAGE_ALIGN(bytes), PAGE_SIZE);
	if (vstart && !swiotlb_init_with_tbl(vstart, io_tlb_nslabs, verbose))
		return;

	if (io_tlb_start)
		memblock_free_early(io_tlb_start,
				    PAGE_ALIGN(io_tlb_nslabs << IO_TLB_SHIFT));
	pr_warn("Cannot allocate SWIOTLB buffer");
	no_iotlb_memory = true;
}
  • 这里一个slab是2K,io_tlb_nslabs表示有多少个slab,也即buffer是多大,默认buffer size是IO_TLB_DEFAULT_SIZE,即64M,也可以由bootloader通过swiotlb参数设置io_tlb_nslabs,io_tlb_nslabs会换算成buffer size。
  • memblock_virt_alloc_low_nopanic用于从低地址物理页分配内存空间。
  • swiotlb_init_with_tbl进一步初始化
mem_init->swiotlb_init->swiotlb_init_with_tbl
int __init swiotlb_init_with_tbl(char *tlb, unsigne
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值