[英]Linux: How to mmap a sequence of physically contiguous areas into user space?
在我的驅動程序中,我有一定數量的物理上連續的DMA緩沖區(例如每個4MB長)以從設備接收數據。 它們由使用SG列表的硬件處理。 由於接收到的數據將受到密集處理,我不想關閉緩存,並且在DMA填充每個緩沖區后我將使用dma_sync_single_for_cpu 。
為了簡化數據處理,我希望這些緩沖區在用戶空間中顯示為一個巨大的,連續的循環緩沖區。 在單個緩沖區的情況下,我只使用remap_pfn_range或dma_mmap_coherent 。 但是,我不能多次使用這些函數來映射連續的緩沖區。
當然,這樣它在找到正確的緩沖區中的相應頁面的PFN,並將其插入到與VMA我可以實現在vm_operations 故障運行vm_insert_pfn 。
收購將非常快,所以當真實數據到達時我無法處理映射。 但這很容易解決。 要在數據采集開始之前准備好所有映射,我可以在開始采集之前簡單地讀取應用程序中的整個mmapped緩沖區,以便在第一個數據到達時已插入所有頁面。
基於故障的技巧應該有效,但也許有更優雅的東西? 只是一個函數,可以多次調用以逐步構建整個映射?
另外的困難是該解決方案應該適用於(從最小的調整)到從2.6.32到最新的內核的內核。
PS。 我看過那個惱人的帖子 。 如果應用程序試圖向mmapped緩沖區寫入內容(只是對數據進行處理),是否有危險,COW會破壞我精心構建的映射?
下面是我的解決方案,適用於使用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;
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.