Прошу прощения, если я не был достаточно ясен о цели этого кода. Я просто хотел попробовать 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()
, поэтому я думаю, что это стоило исследовать.
Большое спасибо за ваши ответы. Любые дальнейшие предложения/улучшения для вышеуказанного кода приветствуются.
Я забыл упомянуть, что я уже установил член .release для моей функции выпуска. bus_id был удален с момента написания LDD. – amicus99
Моя проблема в том, что у меня нет физического устройства, и теперь мне нужно создать заглушку, чтобы попробовать этот метод сопоставления. Я также опасаюсь, что проблема связана с атрибутами сопоставления по умолчанию (используется неявно с помощью dma_map_single()) или с тем, что виртуальный адрес не выравнивается по границам границ кеша (я запрашиваю 2 * PAGE_SIZE байты). – amicus99