第二部分 高级DMA使用方法
警告:下面这些DMA API在大多数情况下不应该被使用。因为它们为一些特殊的需求而准备的,大部分驱动程序并没有这些需求。
如果你不清楚如何确保桥接处理器和I/O设备之间的高速缓存行的一致性,你就根本不应该使用该部分所提到的API。
void *dma_alloc_noncoherent(struct device *dev, size_t size,
dma_addr_t *dma_handle, gfp_t flag)
平台会根据自身适应条件来选择返回一致性或非一致性内存,其他和dma_alloc_coherent()相同。在使用该函数时,你应该确保在驱动程序中对该内存做了正确的和必要的同步操作。
注意,如果返回一致性内存,则它会确保所有同步操作都变成空操作。
警告:处理非一致性内存是件痛苦的事情。如果你确信你的驱动要在非常罕见的平台上(通常是非PCI平台)运行,这些平台无法分配一致性内存时,你才可以使用该API。
voiddma_free_noncoherent(struct device *dev, size_t size, void *cpu_addr,
dma_addr_t dma_handle)
释放由非一致性API申请的内存。
int
dma_get_cache_alignment(void)
返回处理器高速缓存对齐值。应该注意在你打算映射内存或者做局部映射时,该值为最小对齐值。
注意:该API可能返回一个比实际缓存行的大的值。通常为了方便对齐,该值为2的幂次方。
void
dma_cache_sync(struct device *dev, void *vaddr, size_t size,
enum dma_data_direction direction)
对由dma_alloc_noncoherent()申请的内存做局部映射,其实虚拟地址为vaddr。在做该操作时,请注意缓存行的边界。
int
dma_declare_coherent_memory(struct device *dev, dma_addr_t bus_addr,
dma_addr_t device_addr, size_t size, int flags)
当设备需要一段一致性内存时,申请由dma_alloc_coherent分配的一段内存区域。
flag 可以由下面这些标志位进行或操作。
DMA_MEMORY_MAP 请求由dma_alloc_coherent()申请的内存为直接可写。
DMA_MEMORY_IO 请求由dma_alloc_coherent()申请的内存可以通过read/write/memcpy_toio等函数寻址到。
flag必须包含上述其中一个或者两个标志位。
DMA_MEMORY_INCLUDES_CHILDREN
DMA_MEMORY_EXCLUSIVE
为了使操作简单化,每个设备只能申申明一个该内存区域。
处于效率考虑的目的,大多数平台选择页对齐的区域。对于更小的内存分配,可以使用dma_pool() API。
void
dma_release_declared_memory(struct device *dev)
从系统中移除先前申明的内存区域。该函数不会检测当前区域是否在使用。确保该内存区域当前没有被使用这是驱动程序的事情。
void *
dma_mark_declared_memory_occupied(struct device *dev,
dma_addr_t device_addr, size_t size)
该函数用于覆盖特殊内存区域(dma_alloc_coherent()会分配出第一个可用内存区域)。
返回值为指向该内存的处理器虚拟地址,或者如果其中福分区域被覆盖,则返回一个错误(通过PRT_ERR())。
第三部分 调试驱动程序对DMA-API的使用情况
DMA-API如前文所述有一些限制。在支持硬件IOMMU的系统中,驱动程序不能违反这些限制将变得更加重要。最糟糕的情况是,如果违反了这些限制准则,会导致数据出错知道摧毁文件系统。
为了debug驱动程序及发现使用DMA-API时的bug,检测代码可以编译到kernel中,它们可以告诉开发者那些违规行为。如果你的体系结构支持,你可以选择编译选项“Enable debugging of DMA-API usage”,使能这个选项会影响系统性能,所以请勿在产品内核中加入该选项。
如果你用使能debug选项的内核启动,那么它会记录哪些设备会使用什么DMA内存。如果检测到错误信息,则会在内核log中打印一些警告信息。下面是一个警告提示的例子:
------------[ cut here ]------------
WARNING: at /data2/repos/linux-2.6-iommu/lib/dma-debug.c:448
check_unmap+0x203/0x490()
Hardware name:
forcedeth 0000:00:08.0: DMA-API: device driver frees DMA memory with wrong
function [device address=0x00000000640444be] [size=66 bytes] [mapped as
single] [unmapped as page]
Modules linked in: nfsd exportfs bridge stp llc r8169
Pid: 0, comm: swapper Tainted: G W 2.6.28-dmatest-09289-g8bb99c0 #1
Call Trace:
[] warn_slowpath+0xf2/0x130
[] _spin_unlock+0x10/0x30
[] usb_hcd_link_urb_to_ep+0x75/0xc0
[] _spin_unlock_irqrestore+0x12/0x40
[] ohci_urb_enqueue+0x19f/0x7c0
[] queue_work+0x56/0x60
[] enqueue_task_fair+0x20/0x50
[] usb_hcd_submit_urb+0x379/0xbc0
[] cpumask_next_and+0x23/0x40
[] find_busiest_group+0x207/0x8a0
[] _spin_lock_irqsave+0x1f/0x50
[] check_unmap+0x203/0x490
[] debug_dma_unmap_page+0x49/0x50
[] nv_tx_done_optimized+0xc6/0x2c0
[] nv_nic_irq_optimized+0x73/0x2b0
[] handle_IRQ_event+0x34/0x70
[] handle_edge_irq+0xc9/0x150
[] do_IRQ+0xcb/0x1c0
[] ret_from_intr+0x0/0xa
<4>---[ end trace f6435a98e2a38c0e ]---
驱动开发者可以通过DMA-API的栈回溯信息找出什么导致这些警告。
默认情况下只有第一个错误会打印警告信息,其他错误不会打印警告信息。这种机制保证当前警告打印信息不会冲了你的内核信息。为了debug设备驱动,可以通过debugfs禁止该功能。请看下面详细的defbugfs接口文档。
调试DMA-API代码的debugfs目录叫dma-api/。下列文件存在于该个目录下:
dma-api/all_errors 该文件节点包含一个数值。如果该值不为零,则调试代码会在遇到每个错误的时候都打印警告信息。请注意这个选项会轻易覆盖你的内核信息缓冲区。
dma-api/disabled 只读文件节点,如果禁止调试代码则显示字符“Y”。当系统没有足够内存或者在系统启动时禁止调试功能时,该节点显示“Y”。
dma-api/error_count 只读文件节点,显示发现错误的次数。
dma-api/num_errors 该文件节点显示在打印停止前一共打印多少个警告信息。该值在系统启动时初始化为1,通过写该文件节点来设置该值。
dma-api/min_free_entries 只读文件节点,显示分配器记录的可用dma_debug_entries的最小数目。如果该值变为零,则禁止调试代码。
dma-api/num_free_entries 当前分配器可用dma_debug_entries的数目。
dma-api/driver-filter 通过向该文件节点写入驱动的名字来限制特定驱动的调试输出。如果向该节点输入空字符,则可以再次看到全部错误信息。
如果这些代码默认编译到你的内核中,该调试功能被默认打开。如果在启动时你不想使用该功能,则可以设置“dma_debug=off”作为启动参数,该参数会禁止该功能。如果你想在系统启动后再次打开该功能,则必须重启系统。
如果你指向看到特定设备驱动的调试信息,则可以设置“dma_debug_driver=”作为参数。它会在系统启动时使能驱动过滤器。调试代码只会打印和该驱动相关的错误信息。过滤器可以通过debugfs来关闭或者改变。
如果该调试功能在系统运行时自动关闭,则可能是超出了dma_debug_entries的最大限制。这些debug条目在启动时就分配好了,条目数量由每个体系结构自己定义。你可以在启动时使用“dma_debug_entries=”来重写该值。
参考文献
[1] documentation/DMA-API.txt
更多推荐
linux dma 程序例子,Linux之DMA API(下)
发布评论