В документе DMA-API-HOWTO говорится, что dma_alloc_coherent () возвращает два значения: виртуальный адрес, который вы можете использовать для доступа к нему из ЦП, и dma_handle
, который вы передаете карте.
Разве виртуальный адрес и dma_handle
не должны отображаться на один и тот же физический адрес? Это не то, что я вижу.
Ожидается ли, что dma_handle
совпадает с его физическим адресом?
Я использую систему x86_64 с включенным IOMMU. Операционная система - Ubuntu 18.04, а ядро - 4.15.0-47-generic.
Мой код:
ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64));
dmabuffp = dma_alloc_coherent (dev_ptr, (size_t)4*1024*1024, &dma_hndl, GFP_KERNEL | GFP_DMA);
printk(KERN_INFO "init: dma_hndl = 0x%llX\n", dma_hndl);<br />
printk(KERN_INFO "init: dmabuffp VA = 0x%llX\n", (uint64_t)dmabuffp);<br />
printk(KERN_INFO "init: dma_hndl VA = 0x%llX\n", (uint64_t)bus_to_virt(dma_hndl));
printk(KERN_INFO "init: dmabuffp PA = 0x%010llX\n", (uint64_t)virt_to_phys(dmabuffp));<br />
printk(KERN_INFO "init: dma_hndl PA = 0x%010llX\n",(uint64_t)virt_to_phys(bus_to_virt(dma_hndl)));
После загрузки модуля dmesg
показывает это:
init: dma_hndl = 0xFFC00000<br />
init: dmabuffp VA = 0xFFFF888E9B000000<br />
init: dma_hndl VA = 0xFFFF88873FC00000<br />
init: dmabuffp PA = 0x085B000000<br />
init: dma_hndl PA = 0x00FFC00000
Устройство может писать и читать, используя dma_hndl
, но значение его пишет не в * buffptr.
Что я пропускаю и / или делаю неправильно?
Дополнительное тестирование показывает, что dma_hndl
сопоставляется с тем же физическим адресом, что и dmabuffp
. По-видимому, virt_to_phys(bus_to_virt(dma_hndl))
не разрешается должным образом и вместо этого является круглым, возвращая исходное значение dma_hndl
вместо физического адреса, который отображается IOMMU.
dma_addr_t
- это Виртуальный адрес ввода-вывода (IOVA) - это адрес, который устройство использует для доступа к буферу, возвращаемому dma_alloc_coherent
.
void * dma_alloc_coherent(struct device *dev,
size_t size,
dma_addr_t *dma_handle,
gfp_t flag)
Причина
dma_addr_t
не такая же, какvirt_to_phys (dmabuffp)
, потому что IOMMU включен.
Только устройство использует dma_addr_t
для доступа DMA. Для доступа к процессору необходимо использовать возвращенный void *
или какую-то его версию virt_to_phys ()
, чтобы пользовательское пространство могло отображать его через devmem.
Без IOMMU dma_addr_t
представляет собой просто virt_to_phy ()
/ virt_to_bus ()
перевод буфера void *
, поэтому его можно использовать для прямого доступа к процессору, но это неправильное использование DMA API.