第二部分  高级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(下)