2013-11-13 1 views
1

Я пытался сделать следующий тривиальный пример кода работы в моем модуле (версии ядра 2.6.32, 2.6.35):dma_map_single(): минимальные требования на структуру устройства

int rc; 
    struct device dev; 

    dev_set_name(&dev, "mydev"); 
    if ((rc = device_register(&dev)) != 0) 
     goto fail; 

    char *kbuf = kmalloc(size, GFP_KERNEL); 
    dma_addr_t handle = dma_map_single(&dev, kbuf, size, direction); 

    // ... further code omitted 

Проблема заключается в том, что dma_map_single() приводит к oops, который говорит мне, что есть попытка разыменовать нулевой указатель где-то во внутренних функциях функции. Я считаю, что это вызвано недостаточной инициализацией структурных устройств, но я не нашел объяснений относительно настройки правильного устройства структуры (для DMA). device_register() возвращает успех.

Любые подсказки о том, как решить проблему, будут очень признательны.

ответ

1

Прошу прощения, если я не был достаточно ясен о цели этого кода. Я просто хотел попробовать Streaming DMA API, поэтому мне нужно было просто сопоставить/размонтировать буфер памяти ядра (и попытаться получить доступ к нему из ЦП).

Я сделал несколько дальнейших испытаний, пытаясь установить struct device таким образом, чтобы dma_map_single() принял бы ..., что привело к панике ядра. Журналы показали, что паника была вызвана lib/swiotlb_map_page.c (я также забыл упомянуть, что моя аппаратная платформа - x86_64). Я изучил исходный код и выяснил следующее.

Если struct device поставляется dma_map_single() не имеет свой dma_mask набора, основной код будет предположить, что автобус адрес, который ваш буфер ядра был сопоставлен с «не DMA'ble» (он называет dma_capable(), который сравнивает высокую сопоставленный адрес к маске). Если отображаемый диапазон адресов не поддерживает DMA, тогда делается попытка использовать буфер отказов, который может быть доступен для устройства, но поскольку маска не установлена, функция делает вывод о том, что буфер отказов не является DMA'ble либо и паники.

Отметьте, что dma_mask является указателем на u64, поэтому для использования значимого значения у вас должно быть хранилище. Также обратите внимание, что в то время как dma_set_mask устанавливает значение маски, оно не выделяет для него память.Если dma_mask имеет значение NULL, это эквивалентно тому, что маска установлена ​​равной нулю (соответствующий код проверяет dma_mask для NULL перед разыменованием указателя).

Я также заметил, что для некоторых запросов используется код типа «резервный» для x86. Подробнее см. Arch/x86/kernel/pci-dma.c. По существу, у структуры есть coherent_dma_mask, установленное на некоторое значение, а dma_mask просто устанавливается в coherent_dma_mask.

Я смоделировал структуру своего устройства после этой резервной структуры и, наконец, получил dma_map_single() для работы. Обновленный код выглядит следующим образом:

static struct device dev = { 
     .init_name = "mydmadev", 
     .coherent_dma_mask = ~0,    // dma_alloc_coherent(): allow any address 
     .dma_mask = &dev.coherent_dma_mask, // other APIs: use the same mask as coherent 
    }; 

    static void map_single(void) { 
     char *kbuf = kmalloc(size, GFP_KERNEL | GFP_DMA); 
     dma_addr_t dma_addr = dma_map_single(&dev, kbuf, size, direction); 

     if (dma_mapping_error(&dev, dma_addr)) { 
      pr_info("dma_map_single() failed\n"); 
      kfree(kbuf); 
      goto fail; 
     } else { 
      pr_info("dma_map_single() succeeded"); 
     } 

     // the device can be told to access the buffer at dma_addr ... 

     // get hold of the buffer temporarily to do some reads/writes 
     dma_sync_single_for_cpu(&dev, dma_addr, size, direction); 

     // release the buffer to the device again 
     dma_sync_single_for_device(&dev, dma_addr, size, direction); 

     // some further device I/O... 

     // done with the buffer, unmap and free 
     dma_unmap_single(&dev, dma_addr, size, direction); 

     // check/store buffer contents... 

     // free the buffer 
     kfree(kbuf); 
    } 

Конечно, трюк с struct device не может быть портативным, но сделали работу над моей x86_64 и 2.6.32/35 ядер, так что другие могут оказаться полезными, если они хотят экспериментировать с API сопоставления. Передача невозможна без физического устройства, но я смог проверить адреса шины. Dma_map_single() генерирует и получает доступ к буферу после вызова dma_sync_single_for_cpu(), поэтому я думаю, что это стоило исследовать.

Большое спасибо за ваши ответы. Любые дальнейшие предложения/улучшения для вышеуказанного кода приветствуются.

0

Вы должны использовать назначенную инициализацию [1] вашего struct device. Это гарантирует, что все члены, явно не заданные, будут очищены до нуля.

struct device dev = { 
    .parent = aaa, 
    .bus_id = bbb, 
    .bus = ccc, 
    .release = ddd 
}; 

dev_set_name(&dev, "mydev"); 

Linux device drivers книга, p382 показывает следующее:

как минимум, родитель, bus_id, автобус, и высвобождают поля должны быть установлены до того, как структура устройства может быть зарегистрирован.

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

[1] http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1124.pdf, раздел 6.7.8.21

+0

Я забыл упомянуть, что я уже установил член .release для моей функции выпуска. bus_id был удален с момента написания LDD. – amicus99

+0

Моя проблема в том, что у меня нет физического устройства, и теперь мне нужно создать заглушку, чтобы попробовать этот метод сопоставления. Я также опасаюсь, что проблема связана с атрибутами сопоставления по умолчанию (используется неявно с помощью dma_map_single()) или с тем, что виртуальный адрес не выравнивается по границам границ кеша (я запрашиваю 2 * PAGE_SIZE байты). – amicus99

0

dma_map_single возвращает DMA-адрес, который является адресом на шине, что устройство подключено. Другими словами, адрес DMA относится к шине и не имеет никакого смысла без шины.

Вы не можете выполнять DMA без какого-либо реального устройства (которое было инициализировано соответствующим кодом шины).

+0

Я понимаю, что у меня нет реальных передач DMA без устройства (и связанных с процессом прерываний и т. Д.). Я просто хотел поэкспериментировать с API сопоставления, то есть получить буфер с поддержкой DMA и его адрес шины. Настоящие драйверы устройств содержат множество других шаблонов, которые смешиваются с картографическими вызовами (что неизбежно, конечно). Все, что я хотел сделать, это получить усеченный фрагмент кода, который иллюстрирует собственно отображение. – amicus99