2015-05-01 6 views
3

Возможно ли, чтобы приложение пользовательского пространства Linux использовало собственную память для DMA без блокировки IOMMU и без использования VFIO?Пользовательское пространство Linux DMA с iommu = вкл. И без VFIO

Наше приложение отлично работает, когда iommu отключен (intel_iommu = off) или в режиме перехода (intel_iommu = passthrough). Однако это не работает, когда включен IOMMU (intel_iommu = on), поскольку память, которую мы выделяем в пользовательском пространстве, не допускается для DMA.

Официальным решением было бы использовать интерфейс VFIO Linux для управления IOMMU, однако мы считаем, что функция VFIO не очень зрелая и предпочла бы найти более простое решение.

Можем ли мы как-то поручить IOMMU разрешить DMA для физической памяти, которую мы выделили? Это было бы замечательно, потому что тогда нам не пришлось бы инструктировать наших пользователей изменять свои параметры загрузки ядра.

Если есть простое и надежное решение на основе VFIO, это также было бы интересно.

См связанного предыдущий вопрос подробность о том, как мы выделяем память: mremap(2) with HugeTLB to change virtual address?

ответ

1

Вы можете написать простой драйвер символьного устройства, чтобы сделать это.

Внутри драйвера (псевдо-код):

static ssize_t device_write(struct file *filp, const char __user *buf, size_t count, loff_t * ppos) 
{ 
    struct page *page; 
    dma_addr_t DmaBusAddress; 

    copy_from_user(addrstr, buf, sizeof(addrstr)); 

    uaddr = simple_strtoul(addrstr, NULL, 0); 

    /* Get page structure which describes your user space 
     memory area 
    */ 
    res = get_user_pages(...,uaddr,...&page); 

    /* (optional)Get the kernel virtual address for your user space page 
    */ 
    kernel_virtual_address = kmap(page); 

    DmaBusAddress = dma_map_page(...,page) 

    /* 
    or the function below if you use address instead of page 

    DmaBusAddress = dma_map_single(...kernel_virtual_address,count...); 

    */ 

    dev->dma_dir = DMA_TO_DEVICE;/* Write operation */ 
    dev->dma_size = count; 
    dev->dma_addr = DmaBusAddress; 

    /* Assume you have already memory map your DMA controller's I/O space 
     with remap_pfn_range() */ 

    writeRegister(DMA_ADDRESS,dev->dma_addr); 
    ........................ 

    writeRegister(START_DMA_TRANSFER,1); /* enable DMA controller */ 

} 

Код выше показывает, как использовать общий слой DMA.

Цитата ldd3

«IOMMU может организовать любую физическая память, чтобы появиться в диапазоне адресов доступного устройства, и это может привести к физически рассеянные буферам смотреть прилегающие к устройству. Making использование IOMMU требует использования общего DMA слоя; virt_to_bus не до задача»

и

«Общий уровень DMA имеет большую длину, чтобы гарантировать, что вещи работают правильно на всех архитектурах, но, как мы увидим, правильное поведение требует соблюдения небольшого набора правил».

Глава «Карта памяти и DMA» может ответить на все ваши вопросы.

Здесь ссылка: http://free-electrons.com/doc/books/ldd3.pdf

+0

Спасибо за подробный ответ. На самом деле это не подходит для нас, потому что мы хотим, чтобы наше приложение строго занимало пространство пользователя и не зависело от новых модулей ядра. –