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

使用通用设备的动态DMA映射

           ============================================

    James E.J. Bottomley <James.Bottomley@HansenPartnership.com>

本文档描述了DMA API。 对于API的更温和的介绍(以及实际的例子),请参见Documentation/DMA-API-HOWTO.txt。
这个API被分成两部分。 第一部分描述了基本的API。第二部分描述了支持非一致性内存机的扩展。
除非你知道你的驱动程序绝对要支持非一致性平台(这通常只是传统平台),否则你应该只使用第一部分描述的API。

第一部分 - dma_ API

为了获得dma_ API,你必须#include <linux/dma-mapping.h>。 这提供了dma_addr_t和下面描述的接口。
dma_addr_t可以容纳平台上任何有效的DMA地址。 它可以被赋予一个设备作为DMA源或目标使用。
CPU不能直接引用dma_addr_t,因为它的物理地址空间和DMA地址空间之间可能存在转换。

第一部分a - 使用大型DMA相干缓冲区

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

一致性内存是指设备或处理器写入的内存可以立即被处理器或设备读取,而不必担心缓存影响。
(然而,你可能需要确保在告诉设备读取该内存之前刷新处理器的写缓冲区)。
这个例程分配了一个字节的一致性内存区域。
它返回一个指向所分配区域的指针(在处理器的虚拟地址空间),如果分配失败则返回NULL。
它还返回一个<dma_handle>,这个<dma_handle>可以被转换为一个与总线宽度相同的无符号整数,
并被赋予设备作为该区域的DMA地址基。

注意:一致性内存在某些平台上可能很昂贵,最小分配长度可能有一页那么大,
所以你应该尽可能地合并你对一致性内存的请求。最简单的方法是使用dma_pool调用(见下文)。

标志参数(仅dma_alloc_coherent())允许调用者为分配指定GFP_标志(见kmalloc())
(实现可以选择忽略那些影响返回内存位置的标志如GFP_DMA)。

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

包裹dma_alloc_coherent(),如果分配尝试成功,也将返回的内存清零。

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

释放一个你之前分配的一致的内存区域。dev、size和dma_handle都必须与传递给dma_alloc_coherent()的相同。
cpu_addr必须是由dma_alloc_coherent()返回的虚拟地址。

请注意,与它们的同级分配调用不同,这些例程只能在启用IRQ时调用。

第Ib部分 - 使用小的DMA相干缓冲区

为了获得这部分的dma_ API,你必须#include <linux/dmapool.h>。

许多驱动程序需要很多小的DMA-coherent内存区域作为DMA描述符或I/O缓冲区。
与其使用dma_alloc_coherent()以一页或更多为单位进行分配,你可以使用DMA池。
这些池子的工作方式很像kmem_cache结构,除了它们使用DMA-coherent分配器,而不是__get_free_pages()。
另外,它们理解常见的硬件限制,比如队列头需要在N字节边界上对齐。

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

dma_pool_create()初始化了一个DMA-coherent缓冲区池,用于给定设备。
它必须在一个可以睡眠的上下文中调用。

"name"是用来诊断的(就像struct kmem_cache的名字);
dev和size就像你传递给dma_alloc_coherent()的东西。
设备对这种类型的数据的硬件对齐要求是 “align”(用字节表示,必须是2的幂)。
如果你的设备没有跨越边界的限制,那么给alloc传0;
传4096表示从这个池子分配的内存不能跨越4KByte的边界。

	void *dma_pool_zalloc(struct dma_pool *pool, gfp_t mem_flags,
			      dma_addr_t *handle)

包裹dma_pool_alloc(),如果分配尝试成功,也将返回的内存清零。

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

这是从池中分配内存;返回的内存将满足创建时指定的大小和对齐要求。
传递GFP_ATOMIC以防止阻塞,或者如果允许阻塞(不在_中断中,不持有SMP锁),
传递GFP_KERNEL以允许阻塞。 和dma_alloc_coherent()一样,
它返回两个值:一个是CPU可用的地址,另一个是池子的设备可用的DMA地址。

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

这将内存放回池中。 池子是传递给dma_pool_alloc()的东西;
CPU(vaddr)和DMA地址是该例程分配被释放的内存时返回的东西。

	void dma_pool_destroy(struct dma_pool *pool);

dma_pool_destroy()释放了池子的资源。 它必须在一个可以睡眠的上下文中调用。
确保在你销毁池子之前,你已经释放了所有分配的内存回到池子里。

第一部分c - DMA寻址限制

int
dma_set_mask_and_coherent(struct device *dev, u64 mask)

检查掩码是否可行,如果可行则更新设备流和相干DMA掩码参数。

返回:如果成功则返回0,如果不成功则返回一个负数错误。

int
dma_set_mask(struct device *dev, u64 mask)

检查掩码是否可行,如果可行则更新设备参数。

如果成功,返回:0;如果不成功,返回负数错误。

int
dma_set_coherent_mask(struct device *dev, u64 mask)

检查掩码是否可能,如果可能则更新设备参数。

返回:如果成功则返回0,如果不成功则返回一个负数错误。

u64
dma_get_required_mask(struct device *dev)

这个API返回平台需要的掩码,以便有效地操作。 通常这意味着返回的掩码是覆盖所有内存的最小要求。
检查所需的掩码给具有可变描述符大小的驱动程序提供了机会,以便在必要时使用较小的描述符。
请求所需的掩码并不改变当前的掩码。 如果你想利用它,你应该发出dma_set_mask()调用,将掩码设置为返回的值。

第Id部分 - 流式DMA映射

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

映射一块处理器虚拟内存,使其可以被设备访问,并返回该内存的DMA地址。

两个API的方向都可以通过铸造自由转换。然而,dma_ API为其方向使用了一个强类型的枚举器。

DMA_NONE			:	没有方向(用于调试)。
DMA_TO_DEVICE 		:	数据从内存到设备。
DMA_FROM_DEVICE 	:	数据从设备到内存。
DMA_BIDIRECTIONAL	:	方向不详

注意。 并非机器中所有的内存区域都能被这个API映射。而且,连续的内核虚拟空间不一定是连续的物理内存。
因为这个API不提供任何分散/收集(scatter/gather)的能力,如果用户试图映射一个非物理连续的内存,它将会失败。
出于这个原因,要被这个API映射的内存应该从保证它是物理连续的来源获得(比如kmalloc)。

此外,内存的DMA地址必须在设备的dma_mask之内(dma_mask是设备可寻址区域的位掩码,
即如果内存的DMA地址与dma_mask相加仍然等于DMA地址,那么设备可以对内存执行DMA)。
为了确保kmalloc分配的内存在dma_mask之内,驱动程序可以指定各种与平台相关的标志,
以限制分配的DMA地址范围(例如,在x86上,GFP_DMA保证在可用DMA地址的前16MB之内,这是ISA设备的要求)。

还要注意的是,如果平台有IOMMU(一个将I/O DMA地址映射到物理内存地址的设备),
上述关于物理连性性和dma_mask的约束可能不适用。
然而,为了便于移植,设备驱动程序编写者可能假设存在这样的IOMMU。

警告:内存一致性的操作粒度称为cache line宽度。 为了使这个API映射的内存能够正常运行,
映射的区域必须正好开始于一个cache line的边界,并且正好结束于一个cache line
(以防止两个单独映射的区域共享一个cache line)。
由于在编译时可能不知道cache line的大小,API不会强制执行这一要求。
因此,建议驱动程序编写者在运行时不特别注意确定cache line的大小,
只映射在页面边界(保证也是cache line的边界)开始和结束的虚拟区域。

DMA_TO_DEVICE: DMA_TO_DEVICE同步必须在软件最后一次修改内存区域后,在它被移交给设备前完成。
一旦使用此基元,设备应将此基元覆盖的内存视为只读。
如果设备可能在任何时候对其进行写入,那么它应该是DMA_BIDIRECTIONAL(见下文)。

DMA_FROM_DEVICE: 在驱动程序访问可能被设备更改的数据之前,必须进行 DMA_FROM_DEVICE 同步。
这个内存应该被驱动程序视为只读的。 如果驱动程序在任何时候需要对其进行写入,应该是DMA_BIDIRECTIONAL(见下文)。

DMA_BIDIRECTIONAL需要特殊处理:它意味着驱动不确定内存在被移交给设备之前是否被修改过,
也不确定设备是否也会修改它。 因此,你必须对双向内存进行两次同步:
一次是在内存被移交给设备之前(以确保所有的内存变化都从处理器中刷新),
另一次是在数据被设备使用后可能被访问之前(以确保任何处理器的缓存行被更新为设备可能已改变的数据)。

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

解除先前映射的区域。 所有传入的参数必须与映射API传入(和返回)的参数相同。

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)

用于页面的映射和解映射的API。 所有其他映射API的注意事项和警告都适用于此。
另外,尽管和参数被提供来做部分页面映射,但建议你永远不要使用这些参数,除非你真的知道缓存的宽度是多少。

dma_addr_t
dma_map_resource(struct device *dev, phys_addr_t phys_addr, size_t size,
		 enum dma_data_direction dir, unsigned long attrs)

void 
dma_unmap_resource(struct device *dev, dma_addr_t addr, size_t size,
		   enum dma_data_direction dir, unsigned long attrs)

用于MMIO资源的映射和解映射的API。所有其他映射API的注意事项和警告都适用于此。
该API应该只用于映射设备MMIO资源,不允许映射RAM。

int
dma_mapping_error(struct device *dev, dma_addr_t dma_addr)

在某些情况下,dma_map_single()、dma_map_page()和dma_map_resource()将无法创建一个映射。
驱动程序可以通过使用dma_mapping_error()测试返回的DMA地址来检查这些错误。
一个非零的返回值意味着映射不能被创建,驱动应该采取适当的措施(比如减少当前DMA映射的使用或延迟,以后再试)。

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

返回:被映射的DMA地址段的数量(如果散布/收集(scatter/gather)列表的一些元素在物理上或实际上是相邻的,
并且IOMMU用一个条目来映射它们,这可能比传入的短)。

请注意,如果sg已经被映射过一次,就不能再被映射了。映射过程被允许破坏sg中的信息。

与其他映射接口一样,dma_map_sg()也可能失败。当它失败时,会返回0,驱动程序必须采取适当的行动。
驱动程序必须做一些事情,在块状驱动的情况下,中止请求或者甚至Oops也比什么都不做和破坏文件系统要好。

对于散列表(scatterlists),你可以这样使用产生的映射。

	int i, count = dma_map_sg(dev, sglist, nents, direction);
	struct scatterlist *sg;

	for_each_sg(sglist, sg, count, i) {
		hw_address[i] = sg_dma_address(sg);
		hw_len[i] = sg_dma_len(sg);
	}

其中nents是sglist中的条目数。

实现可以自由地将几个连续的sglist条目合并成一个(例如,用IOMMU,或者如果几个页面刚好在物理上是连续的),
并返回它映射到它们的实际sg条目数。如果失败,则返回0。

然后你应该循环count次(注意:可以少于nents次)并使用sg_dma_address()和sg_dma_len()宏,
如上图所示,你以前访问sg->address和sg->length。

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

解除先前映射的散点/集合(scatter/gather)列表的映射。所有的参数必须与传递给散点/集合映射API的参数相同。

注意:必须是你传入的数字,不是返回的DMA地址项的数量。

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 nents,
		    enum dma_data_direction direction)

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

为CPU和设备同步一个单一的连续或分散/聚集(scatter/gather)映射。
使用 sync_sg API,所有的参数必须与传入单一映射API的参数相同。
使用sync_single API,你可以使用dma_handle和size参数,
这些参数与传入单一映射API的参数不完全相同,以进行部分同步。

注意: 你必须这样做。

  • 在从设备上读取已经由DMA写入的值之前(使用DMA_FROM_DEVICE方向)。
  • 在写完将用DMA写入设备的值之后(使用DMA_TO_DEVICE)方向
  • 如果内存是DMA_BIDIRECTIONAL,在将内存交给设备之前之后。

也请看dma_map_single()。

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

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

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

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

上面的四个函数与没有 _attrs 后缀的对应函数一样,只是它们传递了一个可选的 dma_attrs。

对 DMA 属性的解释是特定于架构的,每个属性应该在 Documentation/DMA-attributes.txt 中记录。

如果 dma_attrs 为 0,这些函数的语义与没有 _attrs 后缀的相应函数的语义相同。
因此 dma_map_single_attrs() 通常可以取代 dma_map_single() 等。

作为*_attrs函数使用的一个例子,在为DMA映射内存时,你可以这样传递一个属性DMA_ATTR_FOO。

#include <linux/dma-mapping.h>
/* DMA_ATTR_FOO should be defined in linux/dma-mapping.h and
 * documented in Documentation/DMA-attributes.txt */
...

	unsigned long attr;
	attr |= DMA_ATTR_FOO;
	....
	n = dma_map_sg_attrs(dev, sg, nents, DMA_TO_DEVICE, attr);
	....

关心DMA_ATTR_FOO的架构将在他们的映射和解映射例程的实现中检查它的存在,例如。

void whizco_dma_map_sg_attrs(struct device *dev, dma_addr_t dma_addr,
			     size_t size, enum dma_data_direction dir,
			     unsigned long attrs)
{
	....
	if (attrs & DMA_ATTR_FOO)
		/* twizzle the frobnozzle */
	....

第二部分 - 高级 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()相同,只是平台会根据自己的需要选择返回一致或不一致的内存。
通过使用这个API,你向平台保证,如果它选择返回非一致的内存,你在驱动中为这个内存准备了所有正确和必要的同步点。

注意:如果平台可以返回一致的内存,它将保证同步点成为nops。

警告:处理非一致的内存是一个真正的痛苦。
只有当你肯定地知道你的驱动程序需要在极少数(通常是非PCI)架构上工作时,
你才应该使用这个API,这些架构根本无法生成一致性内存。

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

释放由非一致性API分配的内存。所有参数必须与传入的参数相同(以及由dma_alloc_noncoherent()返回的)。

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, phys_addr_t phys_addr,
			    dma_addr_t device_addr, size_t size, int
			    flags)

声明内存区域,当dma_alloc_coherent()被要求为这个设备提供相干内存时它将被递出。

phys_addr :是当前分配给内存的CPU物理地址(这将被ioremapped,以便CPU可以访问该区域)。
device_addr :是设备需要编程的DMA地址,以实际寻址该内存(这将在dma_alloc_coherent()中作为dma_addr_t被送出。
size :是该区域的大小(必须是PAGE_SIZE的倍数)。

下面这些标志可以OR在一起:

DMA_MEMORY_MAP - 要求从dma_alloc_coherent()返回的内存是可直接写入的。
DMA_MEMORY_IO - 要求从dma_alloc_coherent()返回的内存可以使用read()/write()/memcpy_toio()等进行寻址。

这些标志中的一个或两个必须存在。

DMA_MEMORY_INCLUDES_CHILDREN - 使声明的内存被这个设备的任何子设备的dma_alloc_coherent分配(对于驻留在桥上的内存)。

DMA_MEMORY_EXCLUSIVE - 只从声明的区域分配内存。不允许dma_alloc_coherent()在声明区域的内存耗尽时回落到系统内存。

返回值将是DMA_MEMORY_MAP或DMA_MEMORY_IO,并且必须与传入的标志相对应
(即如果只传入DMA_MEMORY_MAP,则不返回DMA_MEMORY_IO)表示成功,或 0 表示失败。

注意:对于DMA_MEMORY_IO的返回,由dma_alloc_coherent()返回的所有后续内存不能再被直接访问,
而是必须使用正确的总线函数来访问。 如果你的驱动程序没有准备好处理这个突发事件,
它就不应该在输入标志中指定DMA_MEMORY_IO。

作为对平台的简化,每个设备只能声明一个这样的内存区域。

出于效率的考虑,大多数平台只选择以页的粒度来跟踪声明的区域。对于较小的分配,你应该使用dma_pool() API。

void
dma_release_declared_memory(struct device *dev)

从系统中删除先前声明的内存区域。这个API对这个区域不进行no使用中的检查,
并将无条件地返回已经移除所有需要的结构。驱动程序的工作是确保这个内存区域的任何部分目前都没有被使用。

void *
dma_mark_declared_memory_occupied(struct device *dev,
				  dma_addr_t device_addr, size_t size)

这是用来占用声明空间的特定区域(dma_alloc_coherent()会把它找到的第一个空闲区域发放出去)。

device_addr是请求区域的设备地址。

size是大小(应该是一个页面大小的倍数)。

返回值将是一个指向内存的处理器虚拟地址的指针,或者是一个错误(通过PTR_ERR()),如果该区域的任何部分被占用。

第三部分 - 调试驱动程序对DMA-API的使用

上述的DMA-API有一些限制。例如,DMA地址必须用相同大小的相应函数来释放。
随着硬件IOMMU的出现,驱动程序不违反这些约束变得越来越重要。
在最坏的情况下,这种违反会导致数据损坏,甚至破坏文件系统。

为了调试驱动程序和发现DMA-API使用中的错误,可以将检查代码编译到内核中,告诉开发者这些违反行为。
如果你的架构支持它,你可以在你的内核配置中选择 "Enable debugging of DMA-API usage "选项。
启用这个选项会影响性能。请不要在生产内核中启用它。

如果你启动后,内核会包含一些代码,这些代码会对哪些DMA内存被分配给哪些设备做一些记录。
如果这段代码检测到一个错误,它会在内核日志中打印出一个警告信息和一些细节。一个警告信息的例子可能是这样的。

------------[ 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调用的堆栈跟踪。

默认情况下,只有第一个错误会导致一个警告信息。所有其他的错误只会被默默地计算在内。
这个限制的存在是为了防止代码充斥你的内核日志。为了支持对设备驱动的调试,可以通过debugfs禁用这个功能。
详情见下面的debugfs接口文档。

用于DMA-API调试代码的debugfs目录被称为dma-api/。在这个目录中,目前可以找到以下文件。

dma-api/all_errors 这个文件包含一个数字值。 
			如果这个值不等于0,调试代码将为它发现的每个错误打印一个警告到内核日志。
			对这个选项要小心,因为它很容易淹没你的日志。

dma-api/disabled 这个只读文件包含字符'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-API的调试功能。
注意,你不能在运行时再次启用它。你必须重新启动才能这样做。

如果你想只看到一个特殊设备驱动程序的调试信息,你可以指定dma_debug_driver=参数。
这将在启动时启用驱动程序过滤器。之后的调试代码将只打印该驱动程序的错误。
这个过滤器可以在以后使用debugfs禁用或改变。

当代码在运行时禁用自己时,这很可能是因为它用完了dma_debug_entries。这些条目是在启动时预分配的。
预先分配的条目数量在每个架构中都有定义。如果它对你来说太少,
可以用 "dma_debug_entries=<your_desired_number>"来覆盖架构的默认值。

void debug_dmap_mapping_error(struct device *dev, dma_addr_t dma_addr) 。

dma-debug接口debug_dma_mapping_error()用来调试那些未能检查dma_map_single()
和dma_map_page()接口返回的地址上的DMA映射错误的驱动程序。
这个接口清除了由debug_dma_map_page()设置的标志,表明dma_mapping_error()已经被驱动调用。
当驱动程序取消映射时,debug_dma_unmap()检查该标志,如果该标志仍然被设置,
则打印警告信息,包括导致取消映射的调用跟踪。这个接口可以从dma_mapping_error()例程中调用,
以实现DMA映射的错误检查调试。

参考资料
kernel/Documentation/DMA-API.txt
kernel/Documentation/DMA-API-HOWTO.txt
kernel/Documentation/DMA-attributes.txt

  • 2
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值