首页 理论教育Linux内核与设备驱动中DMA使用和管理框架详解

Linux内核与设备驱动中DMA使用和管理框架详解

【摘要】:由于DMA需要访问内存,所以在早期的Linux内核提供的使用和管理框架主要是内存管理的框架,这些框架用于分配和管理DMA要使用的内存资源,规范DMA和CPU访问内存区域的流程。相应的限制会和设备绑定,因为DMA的最终使用者会以物理设备和逻辑设备的形式存在,这样的限制和设备绑定更合理。相应的详细接口如下:5DMA engine框架以上都是和DMA操作的内存管理相关的框架和接口,并没有涉及DMA操作的统一接口和DMA自身资源管理部分。

由于DMA和芯片体系相关,各个芯片厂商的设计接口参数是不同的。由于DMA需要访问内存,所以在早期的Linux内核提供的使用和管理框架主要是内存管理的框架,这些框架用于分配和管理DMA要使用的内存资源,规范DMA和CPU访问内存区域的流程。所有这些管理方式都在dma-mapping.h中有声明。这里用mapping的概念表示内存资源的管理也是合适的,毕竟这些内存无论从CPU还是DMA访问都相当于一个映射的过程。

1ᤫ一致性DMA映射管理

首先常用的是一致性DMA映射,这里的一致性主要是指CPU和DMA访问的一致性,其中相应的内存区域一般都是关闭cache的,这样数据可以保持一致性。另外相应的区域CPU和设备是可以同时访问的,这是建立在一致性基础上的。对系统来说,该特性还是比较重要的,这样减少了同步的操作。

Linux内核提供了一组DMA一致映射的接口,具体如下:

这里保留相应的注释,主要是相应的注释都很明确地表述了相应接口的功能。对这些接口的使用,通常是在要使用DMA的设备驱动初始化时分配相应的空间,在驱动退出时释放相应的空间。如果驱动支持在相应空间被应用程序访问,则通过dma_mmap_coherent来进行映射。需要注意的是,特殊的设备在映射时,相同的物理内存空间对于CPU和DMA是不同的地址,相应的分配接口是通过传址参数来返回DMA视角的地址,通过返回值返回CPU视角的地址,这样在系统级别达到一致性。

一致性映射的空间在整个驱动的生命周期中都被使用,可以说是驱动的一个组成部分,所以是可以对应用层开放并提供映射接口的。

一致性接口的粒度比较大,是以页为单位的,并且不能提供大量的DMA传输需要的空间。

参考kmem_cache的方式,DMA也可以将所有的一定大小的空间需求组织在一起,进行整体的物理内存分配,而该大小的单个DMA分配需求,作为细粒度的DMA操作,通过相应的接口在整体申请的DMA空间中进行分配和操作。这样就形成一个池的功能,而相应的接口就是dma_pool。详细的说明如下:

整体的dma pool创建和销毁可使用dma_pool_create和dma_pool_destroy,而细粒度的分配和释放可使用dma_pool_alloc和dma_pool_free。

2ᤫ流式DMA映射管理

一致性映射是消耗比较大的映射,主要是一旦分配后只能在驱动退出时才能释放,这样相当于驱动一直占用内存资源,而并不管是否要进行DMA相关的操作。由于驱动并不是一直都需要使用相应的内存资源进行DMA操作,这在一定程度上造成了比较大的开销。为了解决该问题,Linux内核提供了流式DMA映射方式,相应的内存只有在需要进行DMA操作之前才进行DMA相关的映射,DMA操作完之后进行映射的释放,这样在进行DMA操作之外的时间由CPU进行处理。这样保证相应的空间,只在DMA操作期间才归驱动的DMA操作所有,之外CPU可以进行任何处理,释放修改等都可以,比较适合用户空间通过驱动传输数据的操作。相应的接口细节如下:

可见这些接口有一段数据的映射也有物理页的映射,这里接口并不负责分配空间,而是解决内存所有者的问题,并附加进行方向的检查,保证数据的一致性以及和CPU的同步。

3ᤫ散列式DMA映射管理

流式DMA映射处理器和DMA一次只能交互单次的DMA传输数据,相对来说效率低一些。在内核中很多模块及层次都是需要批量处理数据的能力,这就要求进行DMA相关映射管理的时候,要能进行数据批量的交互,批量交给驱动进行DMA传输工作,驱动接收到批量数据后再由DMA进行循环操作完成批量传输。相关的接口细节如下:(www.chuimin.cn)

相应的批量映射的区域由参数sg表示,在进行DMA操作之前,将dma_map_sg映射给驱动,驱动批量DMA操作结束后,调用dma_unmap_sg,将相关区域归还,上层框架会继续处理相关区域。

4ᤫ体系结构相关接口

在体系结构中,具体的设备允许通过DMA访问的地址空间可能是受限的,这就需要对相应的设备进行标注,从而在上面的映射接口中进行正确的操作。相应的限制会和设备绑定,因为DMA的最终使用者会以物理设备和逻辑设备的形式存在,这样的限制和设备绑定更合理。相应的详细接口如下:

5ᤫDMA engine框架

以上都是和DMA操作的内存管理相关的框架和接口,并没有涉及DMA操作的统一接口和DMA自身资源管理部分。有统一的接口可以使得驱动的抽象程度更高,减少不同芯片相同功能驱动的差异,提高可移植性,可以提供统一的IP级别的驱动。但是这在早期的Linux内核中都没有提供,新的Linux内核才通过DMA engine框架提供了该部分功能。DMA engine是上层框架,主要是对DMA的操作和属性进行管理和抽象,其中将DMA作为设备来管理。要实现完整的功能还需要各种芯片的支持以及各种设备驱动的移植。由于是新内核才提供的功能,并不是所有的芯片都支持该功能,所以下面只是对接口进行简单的说明。

分配DMA内部channel的接口:

DMA特殊属性配置的接口:

DMA传输的描述结构是struct dma_async_tx_descriptor。

设备中会有如下的接口来绑定物理内存和设备的DMA操作,并返回抽象的DMA传输描述结构:

发起传输的接口:

dma_cookie_t dmaengine_submit(struct dma_async_tx_descriptor ∗ desc)

另外还有DMA传输的控制接口:

以上接口可以说已经抽象出各种DMA操作及控制,是很好的上层框架。