读书人

linux之DMA API - 通用设备的动态DMA照

发布时间: 2012-09-12 09:21:30 作者: rapoo

linux之DMA API -- 通用设备的动态DMA映射

通用设备的动态DMA映射

void *
dma_zalloc_coherent
(struct device *dev, size_t size,
dma_addr_t *dma_handle, gfp_t flag)

void
dma_free_coherent
(struct device *dev, size_t size, void *cpu_addr,
dma_addr_t dma_handle)

struct dma_pool *
dma_pool_create
(const char *name, struct device *dev,
size_t size, size_t align, size_t alloc);

void *
dma_pool_alloc
(struct dma_pool *pool, gfp_t gfp_flags,
dma_addr_t *dma_handle);

void
dma_pool_free
(struct dma_pool *pool, void *vaddr,
dma_addr_t addr);

void
dma_pool_destroy
(struct dma_pool *pool);

int
dma_supported
(struct device *dev, u64 mask)

int
dma_set_mask
(struct device *dev, u64 mask)

int
dma_set_coherent_mask
(struct device *dev, u64 mask)

u64
dma_get_required_mask
(struct device *dev)

dma_addr_t
dma_map_single
(struct device *dev, void *cpu_addr, size_t size,
enum dma_data_direction direction)

void
dma_unmap_single
(struct device *dev, dma_addr_t dma_addr, size_t size,
enum dma_data_direction direction)

dma_addr_t
dma_map_page(struct device *dev, struct page *page,
unsigned long offset, size_t size,
enum dma_data_direction direction)

void
dma_unmap_page
(struct device *dev, dma_addr_t dma_address, size_t size,
enum dma_data_direction direction)

int
dma_mapping_error
(struct device *dev, dma_addr_t dma_addr)

int
dma_map_sg(struct device *dev, struct scatterlist *sg,
int nents, enum dma_data_direction direction)

void
dma_unmap_sg
(struct device *dev, struct scatterlist *sg,
int nhwentries, enum dma_data_direction direction)

void dma_sync_single_for_cpu(struct device *dev, dma_addr_t dma_handle, size_t size,
enum dma_data_direction direction)

void dma_sync_single_for_device(struct device *dev, dma_addr_t dma_handle, size_t size,
enum dma_data_direction direction)

void dma_sync_sg_for_cpu(struct device *dev, struct scatterlist *sg, int nelems,
enum dma_data_direction direction)

void dma_sync_sg_for_device(struct device *dev, struct scatterlist *sg, int nelems,
enum dma_data_direction direction)

dma_addr_t
dma_map_single_attrs
(struct device *dev, void *cpu_addr, size_t size,
enum dma_data_direction dir,
struct dma_attrs *attrs)

void
dma_unmap_single_attrs
(struct device *dev, dma_addr_t dma_addr,
size_t size, enum dma_data_direction dir,
struct dma_attrs *attrs)

int
dma_map_sg_attrs
(struct device *dev, struct scatterlist *sgl,
int nents, enum dma_data_direction dir,
struct dma_attrs *attrs)

void
dma_unmap_sg_attrs
(struct device *dev, struct scatterlist *sgl,
int nents, enum dma_data_direction dir,
struct dma_attrs *attrs)

void *
dma_alloc_noncoherent(struct device *dev, size_t size,
dma_addr_t *dma_handle, gfp_t flag)

void
dma_free_noncoherent(struct device *dev, size_t size, void *cpu_addr,
dma_addr_t dma_handle)

int
dma_get_cache_alignment(void)

void
dma_cache_sync(struct device *dev, void *vaddr, size_t size,
enum dma_data_direction direction)

int
dma_declare_coherent_memory(struct device *dev, dma_addr_t bus_addr,
dma_addr_t device_addr, size_t size, int flags)

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:
<IRQ> [<ffffffff80240b22>] warn_slowpath+0xf2/0x130
[<ffffffff80647b70>] _spin_unlock+0x10/0x30
[<ffffffff80537e75>] usb_hcd_link_urb_to_ep+0x75/0xc0
[<ffffffff80647c22>] _spin_unlock_irqrestore+0x12/0x40
[<ffffffff8055347f>] ohci_urb_enqueue+0x19f/0x7c0
[<ffffffff80252f96>] queue_work+0x56/0x60
[<ffffffff80237e10>] enqueue_task_fair+0x20/0x50
[<ffffffff80539279>] usb_hcd_submit_urb+0x379/0xbc0
[<ffffffff803b78c3>] cpumask_next_and+0x23/0x40
[<ffffffff80235177>] find_busiest_group+0x207/0x8a0
[<ffffffff8064784f>] _spin_lock_irqsave+0x1f/0x50
[<ffffffff803c7ea3>] check_unmap+0x203/0x490
[<ffffffff803c8259>] debug_dma_unmap_page+0x49/0x50
[<ffffffff80485f26>] nv_tx_done_optimized+0xc6/0x2c0
[<ffffffff80486c13>] nv_nic_irq_optimized+0x73/0x2b0
[<ffffffff8026df84>] handle_IRQ_event+0x34/0x70
[<ffffffff8026ffe9>] handle_edge_irq+0xc9/0x150
[<ffffffff8020e3ab>] do_IRQ+0xcb/0x1c0
[<ffffffff8020c093>] ret_from_intr+0x0/0xa
<EOI> <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=<drivername>”作为参数。它会在系统启动时使能驱动过滤器。调试代码只会打印和该驱动相关的错误信息。过滤器可以通过debugfs来关闭或者改变。

如果该调试功能在系统运行时自动关闭,则可能是超出了dma_debug_entries的最大限制。这些debug条目在启动时就分配好了,条目数量由每个体系结构自己定义。你可以在启动时使用“dma_debug_entries=<your_desired_number>”来重写该值。

参考文献

[1] documentation/DMA-API.txt

读书人网 >UNIXLINUX

热点推荐