dma_alloc_coherent分配内存经mmap映射到用户空间在5.14内核出现异常的问题
dma_alloc_coherent分配内存经mmap映射到用户空间在5.14内核出现异常的问题
碰到过类似的内核版本差异导致的映射问题,给你梳理几个大概率的排查方向和解决思路:
可能的核心原因与排查步骤
1. 内存页权限与映射标志的变化
5.14内核对用户空间内存映射的权限校验比4.18更严格,你可以先检查mmap处理逻辑里的页保护属性和VM标志:
- 确认在调用
remap_pfn_range前,有没有给vma->vm_page_prot设置正确的权限,比如用pgprot_readwrite结合pgprot_noncached(DMA内存通常需要非缓存属性); - 必须加上
VM_SHARED标志,否则用户空间和内核空间的内存不会同步,你看到的0xFFFFFF大概率是未同步的缓存脏数据或者默认的填充值; - 建议同时加上
VM_IO | VM_DONTEXPAND | VM_DONTDUMP这类针对设备内存的标志,避免内核对这块内存做不必要的优化或回收。
2. DMA内存地址的处理方式差异
4.18里直接用virt_to_phys转虚拟地址为物理地址再算PFN的方式,在5.14可能不再可靠:
- 优先用
virt_to_pfn直接从dma_alloc_coherent返回的虚拟地址获取页帧号,而不是自己算phys >> PAGE_SHIFT; - 如果是用
dma_alloc_coherent返回的dma_addr_t(DMA总线地址),要改用dma_to_pfn来转换,5.14内核对DMA地址的管理逻辑有调整,直接转物理地址可能拿到错误的PFN。
3. 缓存一致性问题
DMA内存的缓存策略在5.14内核有调整,默认的缓存属性可能导致用户空间读不到内核写入的数据:
- 在内核写入DMA内存后,显式调用
dma_wmb()(写内存屏障)确保数据刷到内存; - 在用户空间读取前,内核侧可以调用
flush_dcache_page()刷新对应页的缓存,或者在mmap时直接设置非缓存属性从根源避免一致性问题。
4. 内核日志的隐藏线索
别漏了看dmesg输出,5.14内核会输出很多之前4.18不会打印的调试信息,比如页权限不足、映射时的页表项错误,这些信息能直接定位问题。
修正后的示例代码片段
给你一个调整后的mmap处理函数参考,适配5.14内核的要求:
static int my_drv_mmap(struct file *filp, struct vm_area_struct *vma) { struct my_drv_data *drv_data = filp->private_data; unsigned long pfn; unsigned long map_size = vma->vm_end - vma->vm_start; // 校验映射大小不超过分配的DMA内存 if (map_size > drv_data->dma_buf_size) { pr_err("mmap size exceed allocated DMA buffer\n"); return -EINVAL; } // 从DMA虚拟地址获取正确的PFN pfn = virt_to_pfn(drv_data->dma_virt_addr); // 设置非缓存的可读可写页保护 vma->vm_page_prot = pgprot_noncached(pgprot_readwrite(vma->vm_page_prot)); // 加上必要的VM标志 vma->vm_flags |= VM_SHARED | VM_IO | VM_DONTEXPAND | VM_DONTDUMP; // 执行映射 if (remap_pfn_range(vma, vma->vm_start, pfn, map_size, vma->vm_page_prot)) { pr_err("remap_pfn_range failed for DMA buffer\n"); return -EAGAIN; } return 0; }
快速验证方法
先在内核里往DMA内存写入一段测试数据(比如0x12345678),然后直接读取内核虚拟地址确认数据正确,排除是dma_alloc_coherent本身的问题;再通过mmap到用户空间读取,如果还是读不到,就肯定是映射逻辑的权限或地址转换问题。
备注:内容来源于stack exchange,提问作者Michele
相关产品推荐
相关产品推荐

