dma mapping

 

mips架构:

void *dma_alloc_coherent(struct device *dev, size_t size,
 dma_addr_t * dma_handle, gfp_t gfp)
{
 void *ret;

 if (dma_alloc_from_coherent(dev, size, dma_handle, &ret))
  return ret;

 gfp = massage_gfp_flags(dev, gfp);

 ret = (void *) __get_free_pages(gfp, get_order(size));

 if (ret) {
  memset(ret, 0, size);
  *dma_handle = plat_map_dma_mem(dev, ret, size);

  if (!plat_device_is_coherent(dev)) {
   dma_cache_wback_inv((unsigned long) ret, size);
   ret = UNCAC_ADDR(ret);
  }
 }

 return ret;
}

EXPORT_SYMBOL(dma_alloc_coherent);

参数dma_handle保存了获取到地址的物理地址,对mips架构而言,该函数的返回值(也就是返回的内核虚拟地址)位于unmapped & uncached的kseg1。

 

 在arm 平台上dma_alloc_coherent会得到禁止页表项中的 C (Cacheable) 域以及 B (Bufferable)域。

/*
 * Allocate DMA-coherent memory space and return both the kernel remapped
 * virtual and bus address for that space.
 */
void *
dma_alloc_coherent(struct device *dev, size_t size, dma_addr_t *handle, gfp_t gfp)
{
 void *memory;

 if (dma_alloc_from_coherent(dev, size, handle, &memory))
  return memory;

 return __dma_alloc(dev, size, handle, gfp,
      pgprot_dmacoherent(pgprot_kernel),
      __builtin_return_address(0));
}
EXPORT_SYMBOL(dma_alloc_coherent);

其中:

#ifdef CONFIG_ARM_DMA_MEM_BUFFERABLE
#define pgprot_dmacoherent(prot) \
 __pgprot_modify(prot, L_PTE_MT_MASK, L_PTE_MT_BUFFERABLE | L_PTE_XN)
#define __HAVE_PHYS_MEM_ACCESS_PROT
struct file;
extern pgprot_t phys_mem_access_prot(struct file *file, unsigned long pfn,
         unsigned long size, pgprot_t vma_prot);
#else
#define pgprot_dmacoherent(prot) \
 __pgprot_modify(prot, L_PTE_MT_MASK, L_PTE_MT_UNCACHED | L_PTE_XN)
#endif

 

由此,再去理解 LDD3 上讲解的一致性 DMA 映射 与 流式 DMA 映射就比较容易了,一致性 DMA 映射调用的是上面的函数, 由于关闭了 cache/buffer, 性能自然比较低。 而流式则通过复杂的同步机制,没有付出性能的代价。

所以我们要尽量使用流式 DMA 来编程。

 

 

另外说明一下:

在linux-3.4\arch\mips\include\asm\Dma-mapping.h 中有:

#include <asm-generic/dma-mapping-common.h>
所以mips架构dma_map_single就位于dma-mapping-common.h中:

#define dma_map_single(d, a, s, r) dma_map_single_attrs(d, a, s, r, NULL)

对arm架构而言,dma_map_single的实现在linux-3.4\arch\arm\include\asm\dma-mapping.h中:

static inline dma_addr_t dma_map_single(struct device *dev, void *cpu_addr,
  size_t size, enum dma_data_direction dir)
{
 unsigned long offset;
 struct page *page;
 dma_addr_t addr;

 BUG_ON(!virt_addr_valid(cpu_addr));
 BUG_ON(!virt_addr_valid(cpu_addr + size - 1));
 BUG_ON(!valid_dma_direction(dir));

 page = virt_to_page(cpu_addr);
 offset = (unsigned long)cpu_addr & ~PAGE_MASK;
 addr = __dma_map_page(dev, page, offset, size, dir);
 debug_dma_map_page(dev, page, offset, size, dir, addr, true);

 return addr;
}

 

linux-3.4\documentation\DMA-API-HOWTO.txt解释是:

   If your architecture needs to support multiple types of IOMMUs, you can use include/linux/asm-generic/dma-mapping-common.h. It's a library to support the DMA API with multiple types of IOMMUs. Lots of architectures (x86, powerpc, sh, alpha, ia64, microblaze and sparc) use it. Choose one to see how it can be used. If you need to support multiple types of IOMMUs in a single system, the example of x86 or powerpc helps.

如果架构支持多种IOMMU,可以使用dma-mapping-common.h。

 

http://blog.chinaunix.net/uid-27123777-id-3279668.html

mips一致性DMA映射、流式DMA映射的使用

http://www.cnblogs.com/hoys/archive/2012/06/21/2557889.html

内核使用硬件ip的dma,dma_alloc_coherent 与 dma_alloc_writecombine (转)

http://blog.chinaunix.net/uid-9185047-id-445099.html

dma_alloc_coherent 与 dma_alloc_writecombine区别 (2010-08-02 16:02)

http://www.360doc.com/content/11/1014/12/1317564_156096743.shtml

DMA及cache一致性学习心得

linux之dma api -- 通用设备的动态dma映射

http://www.58os.com/BBS/OSChinaBlog/Article-858.html

 

下面这篇文章是内核文档DMA-API-HOWTO.txt的翻译,可结合来看。

 DMA动态映射指南

http://www.tuicool.com/articles/biAvi2

 

<think>我们正在解决用户关于`dma_mapping_error`的问题,并且用户提到了`of_dma_configure`。因此,我们需要将两者联系起来分析。用户的问题:查找关于`dma_mapping_error`的解决方案或原因分析,并且与`of_dma_configure`相关。首先,我们需要明确`dma_mapping_error`的作用:它是一个宏或函数,用于检查DMA映射操作(如`dma_map_single`,`dma_map_page`等)是否失败。如果失败,返回非0值。常见原因:1.DMA映射失败可能是因为DMA地址空间不足(例如,DMA掩码设置不当,导致请求的物理地址无法映射到DMA地址空间)。2.设备未正确配置DMA(例如,未调用`of_dma_configure`或未设置DMA掩码)。3.设备树中缺少必要的DMA属性,导致DMA配置不正确。由于用户提到了`of_dma_configure`,因此我们需要强调该函数在DMA配置中的重要性。根据引用[1],`of_dma_configure`会从设备树中读取`dma-coherent`等属性,并设置设备的DMA相关字段(如`dma_coherent`,`dma_mask`等)。如果这一步缺失或配置错误,后续的DMA映射操作可能会失败。因此,我们可以将原因和解决方法分为几个部分:一、`dma_mapping_error`的常见原因二、与`of_dma_configure`相关的配置问题三、解决方法另外,我们还可以给出代码示例,展示如何正确使用`of_dma_configure`以及如何检查DMA映射错误。最后,生成相关问题。注意:用户要求使用中文回答,并且按照指定的格式(包括引用和LaTeX)。</think>### `dma_mapping_error` 原因分析及解决方案(与`of_dma_configure`关联) #### 一、`dma_mapping_error` 的核心作用 在Linux DMA子系统中,`dma_mapping_error()` 用于**检测DMA映射操作是否失败**: ```c dma_addr_t dma_handle = dma_map_single(dev, cpu_ptr, size, dir); if (dma_mapping_error(dev, dma_handle)) { dev_err(dev, "DMA映射失败!"); // 错误处理 } ``` 当返回非零值时,表明`dma_map_single()`、`dma_map_page()`等映射操作失败[^1]。 --- ### 二、根本原因分析(关联`of_dma_configure`) #### 1. **DMA掩码未正确配置**(最常见) `of_dma_configure()` 未调用或调用错误 → 设备DMA掩码未初始化: ```c // 错误示例:未调用of_dma_configure dma_addr_t handle = dma_map_single(dev, buf, size, DMA_TO_DEVICE); if (dma_mapping_error(dev, handle)) // 因dev->dma_mask未设置而失败 ``` > 引用[1]:`of_dma_configure()` 负责设置`dev->dma_mask`和`dev->coherent_dma_mask`[^1]。 #### 2. **物理地址超出DMA范围** 设备树`dma-ranges`定义的范围过小: ```dts // 设备树示例:仅允许0x0-0x10000000的DMA访问 my_device { compatible = "vendor,device"; dma-ranges = <0 0 0x0 0x10000000>; }; ``` 若尝试映射`0x20000000`的物理地址 → 触发`dma_mapping_error`[^2]。 #### 3. **IOMMU配置错误** ```c // 未正确处理IOMMU映射 dma_addr_t handle = iommu_map(domain, iova, paddr, size, prot); if (dma_mapping_error(dev, handle)) // IOMMU映射失败 ``` #### 4. **内存区域不支持DMA** ```c void *buf = vmalloc(SIZE); // vmalloc内存默认不可DMA映射 dma_addr_t handle = dma_map_single(dev, buf, size, dir); // 必然失败 ``` --- ### 三、解决方案(结合`of_dma_configure`) #### 1. **正确初始化DMA配置** ```c static int driver_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; // 关键:先调用of_dma_configure of_dma_configure(dev, dev->of_node, true); // 第三个参数force=true // 显式设置DMA掩码(32位示例) if (!dev->coherent_dma_mask) dev->coherent_dma_mask = DMA_BIT_MASK(32); // 后续DMA操作 dma_addr_t handle = dma_map_single(dev, buf, size, dir); if (dma_mapping_error(dev, handle)) { dev_err(dev, "DMA映射失败, 地址掩码:0x%llx", dev->coherent_dma_mask); } } ``` #### 2. **检查设备树配置** ```dts my_device { compatible = "vendor,device"; // 必需属性 dma-coherent; // 声明硬件一致性 dma-ranges = <0 0 0x0 0x40000000>; // 扩展DMA地址范围 }; ``` #### 3. **使用DMA兼容的内存** ```c // 正确:分配DMA友好内存 void *buf = kmalloc(SIZE, GFP_DMA); // GFP_DMA确保低端内存 dma_addr_t handle = dma_map_single(dev, buf, size, dir); // 替代方案:dma_alloc_coherent dma_addr_t dma_handle; void *cpu_addr = dma_alloc_coherent(dev, size, &dma_handle, GFP_KERNEL); ``` #### 4. **IOMMU异常处理** ```c if (dev->iommu_group) { // 检查IOMMU状态 dma_addr_t handle = iommu_map(domain, iova, paddr, size, prot); if (dma_mapping_error(dev, handle)) { dev_err(dev, "IOMMU映射失败,检查domain配置"); } } ``` --- ### 四、调试技巧 1. **动态检测**:启用`CONFIG_DMA_API_DEBUG` ```bash echo 1 > /sys/kernel/debug/dma-api/dump # 查看DMA映射泄漏 ``` 2. **地址范围检查**: ```c dev_info(dev, "DMA掩码: 0x%llx, 物理地址: 0x%lx", *dev->dma_mask, virt_to_phys(buf)); ``` 3. **内核日志过滤**: ```bash dmesg | grep -i "dma mapping error" # 定位错误上下文 ``` --- ### 五、根本原因总结 | **错误原因** | **关联函数** | **解决方案** | |----------------------------|-----------------------|-----------------------------| | DMA掩码未初始化 | `of_dma_configure()`缺失 | 在probe()首部调用该函数 | | 物理地址超出`dma-ranges` | 设备树配置错误 | 扩展设备树中的地址范围 | | IOMMU映射失败 | `iommu_map()` | 验证IOMMU domain和映射权限 | | 非DMA安全内存 | `vmalloc()`/`kmap()` | 使用`kmalloc(GFP_DMA)`或`dma_alloc_coherent` | > 引用[1]:未正确调用`of_dma_configure`是DMA映射失败的**最常见根本原因**[^1]。 --- **相关问题** 1. 如何验证设备树中的`dma-ranges`是否生效? 2. `dma_map_single()`和`dma_alloc_coherent()`的性能差异如何? 3. ARM64架构下IOMMU与DMA的交互机制是怎样的? 4. 如何检测Linux内核中的DMA地址映射泄漏? [^1]: `of_dma_configure()`负责初始化设备DMA掩码,缺失将导致映射失败 [^2]: 设备树`dma-ranges`定义设备可访问的物理地址范围
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值