能否将kmalloc分配的内存映射到用户态以用于TCP的sendmsg MSG_ZEROCOPY?
问题:kmalloc分配的内存映射到用户态后无法用于sendmsg的MSG_ZEROCOPY模式
我正在开发一个项目,内核模块使用kmalloc()分配物理连续内存,并通过remap_pfn_range()将其映射到用户态。但remap_pfn_range()会将vm_area_struct的vm_flags字段设置为VM_IO | VM_PFNMAP(还有其他无关标志)。当我使用mmap()返回的地址调用带MSG_ZEROCOPY选项的sendmsg()时,调用失败并返回errno=EFAULT。
内核错误追踪
我定位到内核中首次返回-EFAULT的位置在mm/gup.c的check_vma_flags()函数,问题根源就是vm_flags的上述标志:
static int check_vma_flags(struct vm_area_struct *vma, unsigned long gup_flags) { vm_flags_t vm_flags = vma->vm_flags; int write = (gup_flags & FOLL_WRITE); int foreign = (gup_flags & FOLL_REMOTE); if (vm_flags & (VM_IO | VM_PFNMAP)) return -EFAULT; // <= 该错误会传播到用户态。 }
背景信息
- 分配的内存用于Cyclone V SOC-FPGA的CPU与FPGA之间共享,FPGA会向缓冲区写入大量数据,要求内存物理连续
- 当前使用Linux内核版本为v5.3-rc8
- 这种内存映射在不使用零拷贝的常规
send()调用中工作正常;而用malloc()分配的常规用户态内存测试时,sendmsg()的零拷贝模式能让传输速率提升高达50%,因此需要启用该模式优化性能
尝试过的无效方法
我曾在remap_pfn_range()调用后手动清除vm_area_struct.vm_flags中的VM_IO和VM_PFNMAP位,此时sendmsg()看似“成功”——系统调用返回期望发送的缓冲区大小,但客户端实际未收到数据,且重启内核时串口控制台会打印大量错误。显然这种操作不符合内核规范,vm_flags的设置有其必要性,remap_pfn_range()也没有提供阻止设置这些标志的参数。
核心疑问
有没有办法将kmalloc()获取的内存映射到用户态,使其能被传入带MSG_ZEROCOPY选项的sendmsg()?
更新:
我找到了一些相关讨论,会尝试其中的建议并后续反馈:
- 零拷贝用户态TCP发送dma_mmap_coherent映射的内存
- 将物理内存映射为用户态常规的、由struct page支持的映射
内容的提问来源于stack exchange,提问作者K Aronsen




