DMA设备驱动的常见问题
DMA (Direct Memory Access) 是一种提高计算机系统并发能力的技术。简单地说,它允许外围设备以异步方式操作内存,从而减少了CPU在I/O操作中的参与。
目前的微机和PC服务器都广泛采用了 DMA 技术。由于 DMA 是一种异步操作,因此在撰写驱动时,有很多需要注意的问题。
第一类比较常见的问题是,并不是所有的 DMA 控制器或设备都有能力访问全部物理内存,或对访问有限制。例如,许多低端存储设备和网卡往往只能访问物理内存的前4GB,甚至更小的范围。而另一些设备可能只能同时访问同一段整4GB内存,例如,它可能只能访问物理地址为 0~4G-1,或4G~8G-1的内存,而不能同时访问两部分内存。还有一种比较常见的限制是设备只能按照整数次方幂边界来访问内存,例如它可能要求映射长度为4K的DMA起始地址为4K的整数倍,等等。
这一类问题比较隐蔽。如果驱动程序没有考虑这些问题,导致的结果往往是在运行一段时间之后突然发现数据损坏等问题。如果硬件手册中没有特别指出硬件的限制,我们往往可以通过设计一些特别的用例来强制系统映射位于某些可能导致问题的边缘内存来发现这类问题。
针对这些限制,操作系统往往提供了一些绕过限制的方法,例如比较常见的bouncing page,即在较低的物理内存地址进行DMA映射,然后由OS在DMA完成之后将这些数据复制到其他地方。这些方法都会导致性能下降,因此,对于希望承担高性能任务的系统而言,应尽量避免使用这样的硬件。
第二类比较常见的问题是,驱动程序(CPU)在不适当的时候读写内存。许多设备都支持主动发起DMA(Bus Mastering),这种时候,CPU可能没有办法知道设备是不是正在写内存。解决这种问题的方法是引入内存栅(Memory Barrier),即驱动程序在读写内存前后通知硬件自己将要执行的操作,并由硬件来确保相应的结果。例如,网卡驱动在读写映射环或映射链的时候,应在读前以及写前后分别进行内存栅操作,确保自己没有读到过期数据,并确保设备没有读到过期数据。
这一类问题也非常隐蔽。对于负载不高的情形,很可能驱动程序的开发者不会注意到任何问题。甚至,对于负载较高的情形,这类问题会表现为响应慢而不是不稳定或数据损坏。