2017-02-19 3 views
4

В моем драйвере у меня есть определенное количество физически смежных буферов DMA (например, 4 МБ каждый) для приема данных с устройства. Они обрабатываются аппаратными средствами с использованием списка SG. Поскольку полученные данные будут подвергнуты интенсивной обработке, я не хочу отключать кеш, и буду использовать dma_sync_single_for_cpu после того, как каждый буфер будет заполнен DMA.Linux: как смоделировать последовательность физически смежных областей в пространстве пользователя?

Чтобы упростить обработку данных, я хочу, чтобы эти буферы отображались в виде единого огромного непрерывного кругового буфера в пользовательском пространстве. В случае одного буфера я просто использую remap_pfn_range или dma_mmap_coherent. Однако я не могу использовать эти функции несколько раз для сопоставления последовательных буферов.

Конечно, можно реализовать ошибки операцию в vm_operations так, что он находит PFN соответствующей страницы в правом буфере, и вставляет его в ВМА с vm_insert_pfn.

Приобретение будет очень быстрым, поэтому я не могу обрабатывать отображение, когда поступают реальные данные. Но это можно легко решить. Чтобы все сопоставление было готово до начала сбора данных, я могу просто прочитать весь mmapped буфер в своем приложении до начала сбора, так что все страницы уже вставлены при поступлении первых данных.

Tha ошибка основанный трюк должен работать, но, может быть, есть что-то более элегантное? Только одна функция, которая может быть вызвана несколько раз для постепенного построения всего отображения?

Дополнительная сложность заключается в том, что решение должно быть применимо (с минимальными корректировками) к ядрам, начиная с 2.6.32 до новейшего.

PS. Я видел это annoying post. Есть ли опасность, что если приложение попытается написать что-то в mmapped-буфере (просто сделав на месте обработки данных), мое тщательно построенное отображение будет уничтожено COW?

+1

Я никогда не использовал 'vm_insert_pfn'. Обычно я нахожу логический (ядро виртуальный) адрес в правом буфере, передаю его на 'virt_to_page', чтобы получить' struct page * ', затем вызывается' get_page' на странице и задает 'vmf-> страницу' на странице. Это работает, когда буферная память выделяется '__get_free_pages', но, вероятно, недействительна, когда буферная память выделяется функциями' dma_alloc _...'. В этом случае вы можете вызвать 'dma_mmap_attrs' в цикле из самого обработчика _mmap_, настроив' vma-> vm_start', 'vma-> vm_end' и' vma-> vm_pgoff' перед каждым вызовом и восстановить их в конец. –

+1

Пока ваш _mmap_-обработчик разрешает только общие сопоставления 'vma-> vm_flags & VM_SHARED', я не думаю, что вам нужно беспокоиться о COW. –

+0

К сожалению, * dma_mmap_attrs * впервые появился в 3.5. Я не могу использовать его в совместимом с 2.6.32 драйвером. Я вижу два драйвера в 2.6.32, которые используют * vm_insert_pfn * - i915_gem.c и b3dfg.c. В обоих случаях * get_page * не вызывается перед вставкой. – wzab

ответ

0

Ниже представлено мое решение, которое работает для буферов, выделенных 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; 
} 
Смежные вопросы