簡體   English   中英

Linux:如何將一系列物理上連續的區域映射到用戶空間?

[英]Linux: How to mmap a sequence of physically contiguous areas into user space?

在我的驅動程序中,我有一定數量的物理上連續的DMA緩沖區(例如每個4MB長)以從設備接收數據。 它們由使用SG列表的硬件處理。 由於接收到的數據將受到密集處理,我不想關閉緩存,並且在DMA填充每個緩沖區后我將使用dma_sync_single_for_cpu

為了簡化數據處理,我希望這些緩沖區在用戶空間中顯示為一個巨大的,連續的循環緩沖區。 在單個緩沖區的情況下,我只使用remap_pfn_rangedma_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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM