我一直在尝试使以下简单的示例代码在我的模块中运行(内核版本 2.6.32、2.6.35):
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() 返回成功。
任何有关如何解决问题的提示将不胜感激。
最佳答案
如果我对这段代码的目的不够清楚,我深表歉意。我只是想尝试 Streaming DMA API,因此我需要能够简单地映射/取消映射内核内存缓冲区(并尝试从 CPU 访问它)。
我做了一些进一步的测试,试图以 dma_map_single()
接受的方式设置struct device
...这导致了内核 panic 。日志显示 panic 是由 lib/swiotlb_map_page.c 引起的(我还忘了提及我的硬件平台是 x86_64)。我研究了源代码并发现了以下内容。
如果提供给dma_map_single()
的struct device
没有设置dma_mask
,底层代码将假设总线地址是您的内核缓冲区已映射到“非 DMA”(它调用 dma_capable() 来将最高映射地址与掩码进行比较)。如果映射的地址范围不支持 DMA,则尝试使用设备可能可以访问的反弹缓冲区,但由于未设置掩码,该函数得出结论,反弹缓冲区也不支持 DMA和 panic 。
请注意,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() 后访问缓冲区,因此我认为值得研究。
非常感谢您的回答。欢迎对上述代码提出任何进一步的建议/改进。
关于linux-kernel - dma_map_single() : minimum requirements to struct device,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19952968/