[ 3.332196] <1>.(0)[5:kworker/u8:0] ILITEK INFO line = 1789 ilitek_main_probe : !!! ilitek_main_probe enter start !!!, 1789 [ 3.332826] <0>.(1)[1:swapper/0]mtk_charger_parse_dt: battery id:1 [ 3.333588] <1>.(0)[5:kworker/u8:0] ILITEK INFO line = 1791 ilitek_main_probe : default client->addr = 0x41 client->irq = 91 [ 3.334724] <0>.(1)[1:swapper/0]use default PE20_ICHG_LEAVE_THRESHOLD:1000000 [ 3.335589] <1>.(0)[5:kworker/u8:0] ILITEK ERR line = 1772 ilitek_alloc_dma : ilitek [TPD] tpd->dev->dev dma_alloc_coherent error [ 3.336978] <0>.(1)[1:swapper/0]use default TA_START_BATTERY_SOC:0 [ 3.336983] <1>.(1)[1:swapper/0]use default TA_STOP_BATTERY_SOC:85 [ 3.337753] <0>.(0)[5:kworker/u8:0] ILITEK ERR line = 1775 ilitek_alloc_dma : ilitek [TPD] NULL dma_alloc_coherent error static int ilitek_alloc_dma(void) { tpd->dev->dev.coherent_dma_mask = DMA_BIT_MASK(32); I2CDMABuf_va = (u8 *) dma_alloc_coherent(&tpd->dev->dev, 4096, &I2CDMABuf_pa, GFP_KERNEL); if (!I2CDMABuf_va) { tp_log_err("ilitek [TPD] tpd->dev->dev dma_alloc_coherent error\n"); I2CDMABuf_va = (u8 *) dma_alloc_coherent(NULL, 4096, &I2CDMABuf_pa, GFP_KERNEL); if (!I2CDMABuf_va) { tp_log_err("ilitek [TPD] NULL dma_alloc_coherent error\n"); return -1; } } memset(I2CDMABuf_va, 0, 4096); //ilitek_data->client->ext_flag |= I2C_DMA_FLAG; return 0; } int ilitek_main_probe(struct ilitek_ts_data *ilitek_ts_data) { int ret = 0; tp_log_info("!!! %s enter start !!!, %d\n", __func__, __LINE__); if (ilitek_data->client->addr != 0x41) ilitek_data->client->addr = 0x41; mutex_init(&ilitek_data->ilitek_mutex); ilitek_data->unhandle_irq = false; #ifdef ILITEK_TUNING_NODE mutex_init(&ilitek_data->ilitek_debug_mutex); #endif #ifdef ILITEK_ENABLE_DMA ilitek_alloc_dma(); #endif
时间: 2025-07-21 17:02:07 浏览: 7
<think>我们正在处理触摸屏驱动中DMA分配失败的问题,具体错误是`ilitek_alloc_dma`函数中调用`dma_alloc_coherent`时出错。我们需要系统地分析和解决这个问题。
### 1. 理解错误
`dma_alloc_coherent`是Linux内核中用于分配一致(Coherent)DMA内存的函数。它返回两个值:一个是内核虚拟地址,另一个是设备可以访问的DMA地址。当这个函数失败时,通常返回`NULL`(对于虚拟地址)或0(对于DMA地址)。错误可能由多种原因引起,包括内存不足、DMA寻址限制等。
### 2. 常见原因及排查步骤
#### a. 内存不足
- 检查系统内存使用情况,特别是DMA区域(通常为ZONE_DMA)的内存是否耗尽。
- 可以通过`dmesg`或`/var/log/messages`查看内核日志,是否有内存分配失败的信息。
- 使用`free`命令查看系统内存使用情况。
#### b. DMA区域大小限制
- 在32位系统上,DMA区域通常只有16MB(ZONE_DMA)。如果分配的内存大小超过这个限制,或者碎片化严重,可能导致分配失败。
- 检查分配的大小:确保请求的DMA缓冲区大小合理。如果过大,考虑分多次分配或优化使用。
- 查看系统支持的DMA掩码:通过`dma_get_required_mask`函数或检查设备支持的DMA掩码(通常在设备驱动初始化时设置)来确认。
#### c. 设备DMA寻址能力限制
- 设备可能有DMA寻址范围限制(例如,只能访问32位地址)。在调用`dma_alloc_coherent`之前,需要设置设备的DMA掩码。
- 检查驱动代码中是否设置了正确的DMA掩码。例如:
```c
if (dma_set_mask_and_coherent(&client->dev, DMA_BIT_MASK(32))) {
dev_err(&client->dev, "Failed to set DMA mask\n");
return -EIO;
}
```
这里设置设备支持32位DMA地址。如果设备支持64位,则使用`DMA_BIT_MASK(64)`。
#### d. 内存碎片化
- 长时间运行的系统可能导致内存碎片化,使得连续物理内存分配失败。
- 尝试重启设备,看问题是否解决(临时方案)。
- 在内核启动参数中增加`vmalloc`或调整内存管理参数(如`cma`大小)来缓解碎片问题。
#### e. CMA(连续内存分配器)配置
- 如果系统启用了CMA,可以检查CMA区域的大小是否足够。可以通过`/proc/meminfo`查看`CmaTotal`和`CmaFree`。
- 如果CMA区域不足,可以在内核启动参数中增加`cma`大小,例如:`cma=64M`。
#### f. 驱动代码错误
- 检查调用`dma_alloc_coherent`的参数:
- 第一个参数是设备指针(通常是`&client->dev`或`&pdev->dev`)。
- 第二个参数是分配的大小(单位字节)。
- 第三个参数是DMA地址指针(用于返回物理地址)。
- 第四个参数是分配标志(通常用`GFP_KERNEL`或`GFP_ATOMIC`)。
- 确保正确传递了设备指针,因为该指针用于确定DMA操作的设备。
### 3. 调试方法
#### a. 增加内核日志
在驱动中添加调试信息,打印分配大小、返回的地址和错误码(如果有)。例如:
```c
void *dma_buf;
dma_addr_t dma_handle;
dma_buf = dma_alloc_coherent(dev, size, &dma_handle, GFP_KERNEL);
if (!dma_buf) {
dev_err(dev, "Failed to allocate DMA buffer of size %zu\n", size);
return -ENOMEM;
}
```
#### b. 检查DMA掩码
在分配之前,检查设备的DMA掩码是否设置正确。可以通过`dev->dma_mask`和`dev->coherent_dma_mask`来查看。
#### c. 使用`dma_alloc_attrs`
尝试使用`dma_alloc_attrs`并指定`DMA_ATTR_NO_WARN`属性,避免内核日志被警告刷屏,同时检查返回值。
#### d. 内核配置
确认内核配置中是否启用了必要的选项,例如`CONFIG_HAS_DMA`、`CONFIG_DMA_CMA`等。
### 4. 解决方案
根据上述排查步骤,可能的解决方案包括:
1. 减少分配的大小。
2. 设置正确的DMA掩码。
3. 增加CMA区域大小。
4. 优化系统内存,避免碎片化(如重启或调整内核参数)。
5. 检查驱动代码,确保参数传递正确。
### 示例代码修改
假设驱动中设置DMA掩码的代码缺失,可以添加如下代码:
```c
// 在probe函数中,分配DMA之前
if (dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32))) {
dev_err(dev, "No suitable DMA available\n");
return -EIO;
}
```
如果问题由内存碎片引起,可以尝试在分配失败时重试,或者使用`dma_alloc_coherent`的`GFP_ATOMIC`标志(但注意,这可能会在内存紧张时失败)。
### 引用
关于DMA分配失败的问题,可以参考内核文档[^1]和相关的邮件列表讨论[^2]。
§§相关问题§§
1. 如何检查Linux系统的DMA区域使用情况?
2. 如何调整CMA区域的大小?
3. 在Linux驱动中,如何正确设置DMA掩码?
4. 除了`dma_alloc_coherent`,还有哪些DMA内存分配方法?
5. 如何诊断和解决内存碎片化问题?
[^1]: Linux Kernel Documentation - DMA-API.txt
[^2]: Linux Kernel Mailing List archives</think>### 触摸屏驱动中DMA分配失败问题分析(`ilitek_alloc_dma`)
#### 1. **错误本质**
`dma_alloc_coherent`是Linux内核中用于分配**一致性DMA内存**的函数。当返回错误时,通常意味着:
- 系统无法分配请求大小的连续物理内存
- 设备DMA寻址能力不足
- 内存碎片化严重
- CMA(连续内存分配器)区域耗尽
---
#### 2. **关键排查步骤**
##### a. **检查分配参数**
```c
// 典型调用示例
buf = dma_alloc_coherent(dev, size, &dma_handle, GFP_KERNEL);
```
- 确认`size`是否过大(通过`pr_info`打印实际请求值)
- 检查`GFP_KERNEL`是否在中断上下文中错误使用(应改用`GFP_ATOMIC`)
##### b. **验证DMA掩码**
```c
// 在驱动probe函数中添加
if (dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32))) {
dev_err(dev, "DMA mask not supported");
return -EIO;
}
```
- 确认设备树(DT)是否正确定义了`dma-ranges`
- 通过`dma_get_required_mask()`检查实际需求
##### c. **诊断内存状态**
```bash
# 查看CMA状态
$ cat /proc/meminfo | grep Cma
CmaTotal: 65536 kB
CmaFree: 1024 kB # 若Free值过小则需扩容
# 检查内存碎片
$ dmesg | grep -i "page allocation failure"
```
##### d. **内核日志分析**
重点检查以下日志特征:
```
ilitek_alloc_dma: dma_alloc_coherent failed, size=65536
DMA: preallocated 128kB pool for atomic allocation
alloc failed: out of memory
```
---
#### 3. **常见解决方案**
##### 方案1:优化内存分配
- **减小单次分配大小**:拆分大块DMA请求(如分多次分配64KB替代单次1MB)
- **启用CMA调试**(内核配置):
```c
CONFIG_CMA_DEBUG=y
CONFIG_CMA_DEBUGFS=y # 通过/sys/kernel/debug/cma/查看详情
```
##### 方案2:调整内核参数
- **增加CMA区域**(在bootargs中添加):
```bash
cma=256M # 例如原为64MB扩容至256MB
```
- **预留启动内存**(适用于嵌入式设备):
```bash
mem=1G cma=256M@768M # 指定CMA物理地址范围
```
##### 方案3:代码级修复
```c
// 示例:添加重试机制
int retry = 3;
while (retry--) {
buf = dma_alloc_coherent(dev, size, &handle, GFP_KERNEL | __GFP_RETRY_MAYFAIL);
if (buf) break;
msleep(10);
}
```
##### 方案4:驱动兼容性处理
```c
// 检查设备兼容性列表
static const struct of_device_id ilitek_dt_ids[] = {
{ .compatible = "ilitek,ili9881" },
{}
};
MODULE_DEVICE_TABLE(of, ilitek_dt_ids);
```
---
#### 4. **进阶调试工具**
- **DMA映射追踪**:
```bash
echo 1 > /sys/kernel/debug/tracing/events/dma/dma_alloc_coherent/enable
cat /sys/kernel/debug/tracing/trace
```
- **内存泄漏检测**:
```bash
valgrind --tool=memcheck --leak-check=full <driver_module>
```
> **关键引用**:Linux DMA-API文档明确要求驱动必须处理`dma_alloc_coherent`的NULL返回值[^1],Android HAL层对触摸屏DMA有严格响应时间限制[^2]。
---
阅读全文
相关推荐



















