为什么会有DMA(直接内存访问)?我们知道通常情况下,内存数据跟外设之间的通信是通过cpu来传递的。cpu运行io指令将数据从内存拷贝到外设的io端口,或者从外设的io端口拷贝到内存。由于外设的访问速度跟内存的访问速度相比非常慢,因此这种访问操作会严重浪费cpu的指令周期,DMA的出现就是为了解决这个问题。支持DMA机制的系统通常会包含一个DMA控制器,下挂支持DMA的外设,外设和内存之间的数据交换通过DMA控制器完成。
那么,如何使用DMA?不同平台提供不同的DMA控制器,其控制器驱动也不尽相同,因此DMA控制器驱动不在夲篇分析。支持DMA的设备一般都会提供至少两个io端口(用于设置DMA地址和大小)和中断请求线,io端口用于指定缓存的物理地址(无iommu)或者总线地址(有iommu)和大小,中断请求线用于当设备完成dma操作后,通知cpu开始下一步操作。DMA控制器的通道请求,源地址和目的地址的设置等操作一般由外设完成,无需用户介入。一个DMA的读操作如下:
(1)cpu分配一块连续的物理内存缓冲区。
(2)cpu将分配好的内存缓冲区的物理地址或者总线地址和大小写入外设的DMA地址和大小io端口。
(3)cpu设置外设的控制端口,启动外设进行DMA操作。然后,cpu清除读取完成标志,并将当前读取进程挂起,调度其他任务运行。
(4)当外设完成了DMA操作之后,通过中断请求线,通知cpu操作完成。
(5)cpu进入中断处理函数,设置读取完成标志,唤醒挂起在等待队列上的进程。
(6)读取进程被唤醒后,检查读取完成标志。如果完成,对缓冲区数据进行下一步处理,否则,继续挂起等待。
DMA的写操作类似这个过程。仔细分析可以发现,用户只需要提供缓存的物理地址或总线地址和大小,注册中断处理函数,并启动设备进行DMA操作即可。那么缓存的物理地址或总线地址是如何得到的呢?DMA缓存的分配是内核DMA编程的核心,下面我们主要分析两种DMA缓存的分配方法:DMA一致性缓存和DMA映射缓存
(1)DMA一致性缓存分配接口 dma_alloc_coherent():
我们知道,cpu访问内存使用的是虚拟地址,而外设访问内存使用的是io地址。当外设所在的总线不支持iommu时,io地址就是内存的物理地址,如果外设所在的总线支持iommu,那么io地址经过iommu映射之后才是内存的物理地址。因此,一个DMA缓存对应两种地址,cpu虚拟地址和外设io地址,而这些地址在不同平台配置也不同。以x86为例,x86没有iommu,其io地