Linux:如何将物理连续区域的序列映射到用户空间?

在我的驱动程序中,我有一定数量的物理上连续的DMA缓冲区(例如,每个4MB长)从设备接收数据。 它们由硬件使用SG列表处理。 由于收到的数据将经过深入处理,我不想closurescaching,我将在每个缓冲区填充DMA后使用dma_sync_single_for_cpu

为了简化数据处理,我希望这些缓冲区在用户空间中显示为一个巨大的,连续的循环缓冲区。 在单个缓冲区的情况下,我只需使用remap_pfn_rangedma_mmap_coherent 。 但是,我不能多次使用这些函数来映射连续的缓冲区。

当然,我可以在vm_operations中执行错误操作,find右边缓冲区中相应页面的pfn,并用vm_insert_pfn把它插入到vma中 。

收购将会非常快,所以当真正的数据到达时,我无法处理映射。 但这可以很容易地解决。 为了在数据采集开始之前准备好所有映射,我可以在开始采集之前简单地读取应用程序中的整个mmapped缓冲区,以便在第一个数据到达时已经插入所有页面。

基于故障的技巧应该可以工作,但也许有更优雅的东西? 只需要一个函数,可以多次调用来逐步构build整个映射?

额外的困难是解决scheme应该适用于(从最小的调整)到从2.6.32到最新的内核。

PS。 我已经看到那个讨厌的职位 。 如果应用程序试图向mmapped缓冲区写入数据(仅执行数据处理),有没有危险,我的精心构build的映射将被COW销毁?

Solutions Collecting From Web of "Linux:如何将物理连续区域的序列映射到用户空间?"

以下是我的解决方案适用于使用dmam_alloc_noncoherent分配的缓冲区。

缓冲区的分配:

 [...] for(i=0;i<DMA_NOFBUFS;i++) { ext->buf_addr[i] = dmam_alloc_noncoherent(&my_dev->dev, DMA_BUFLEN, &my_dev->buf_dma_t[i],GFP_USER); if(my_dev->buf_addr[i] == NULL) { res = -ENOMEM; goto err1; } //Make buffer ready for filling by the device dma_sync_single_range_for_device(&my_dev->dev, my_dev->buf_dma_t[i],0,DMA_BUFLEN,DMA_FROM_DEVICE); } [...] 

缓冲区的映射

 void swz_mmap_open(struct vm_area_struct *vma) { } void swz_mmap_close(struct vm_area_struct *vma) { } static int swz_mmap_fault(struct vm_area_struct *vma, struct vm_fault *vmf) { long offset; char * buffer = NULL; int buf_num = 0; //Calculate the offset (according to info in https://lxr.missinglinkelectronics.com/linux+v2.6.32/drivers/gpu/drm/i915/i915_gem.c#L1195 it is better not ot use the vmf->pgoff ) offset = (unsigned long)(vmf->virtual_address - vma->vm_start); buf_num = offset/DMA_BUFLEN; if(buf_num > DMA_NOFBUFS) { printk(KERN_ERR "Access outside the buffer\n"); return -EFAULT; } offset = offset - buf_num * DMA_BUFLEN; buffer = my_dev->buf_addr[buf_num]; vm_insert_pfn(vma,(unsigned long)(vmf->virtual_address),virt_to_phys(&buffer[offset]) >> PAGE_SHIFT); return VM_FAULT_NOPAGE; } struct vm_operations_struct swz_mmap_vm_ops = { .open = swz_mmap_open, .close = swz_mmap_close, .fault = swz_mmap_fault, }; static int char_sgdma_wz_mmap(struct file *file, struct vm_area_struct *vma) { vma->vm_ops = &swz_mmap_vm_ops; vma->vm_flags |= VM_IO | VM_RESERVED | VM_CAN_NONLINEAR | VM_PFNMAP; swz_mmap_open(vma); return 0; }