我正在移植/调试设备驱动程序(由另一个内核模块使用)并面临死胡同,因为dma_sync_single_for_device()因内核oops而失败.
我不知道该功能应该做什么,谷歌搜索并没有真正帮助,所以我可能需要更多地了解这些功能.
问题是,从哪里开始?
哦,是的,如果它是相关的,代码应该在PowerPC上运行(而linux是OpenWRT)
编辑:优先考虑在线资源(书籍需要几天才能交付:)
我正在研究用于Linux内核的FPGA驱动程序.代码似乎在x86上工作正常,但在x86_64上我遇到了一些问题.我实现了流式DMA.所以它就像
get_user_pages(...);
for (...) {
sg_set_page();
}
pci_map_sg();
Run Code Online (Sandbox Code Playgroud)
但pci_map_sg返回的地址就像是0xbd285800,它们没有对齐PAGE_SIZE,所以我无法发送完整的第一页,因为PCIE规范说
"请求不得指定地址/长度组合,这会导致内存空间访问跨越4 KB边界."
有没有办法获得对齐的地址,还是我错过了一些重要的东西?
我想分配一个大的DMA缓冲区,大小约为40 MB.当我使用时dma_alloc_coherent(),它失败了,我看到的是:
------------[ cut here ]------------
WARNING: at mm/page_alloc.c:2106 __alloc_pages_nodemask+0x1dc/0x788()
Modules linked in:
[<8004799c>] (unwind_backtrace+0x0/0xf8) from [<80078ae4>] (warn_slowpath_common+0x4c/0x64)
[<80078ae4>] (warn_slowpath_common+0x4c/0x64) from [<80078b18>] (warn_slowpath_null+0x1c/0x24)
[<80078b18>] (warn_slowpath_null+0x1c/0x24) from [<800dfbd0>] (__alloc_pages_nodemask+0x1dc/0x788)
[<800dfbd0>] (__alloc_pages_nodemask+0x1dc/0x788) from [<8004a880>] (__dma_alloc+0xa4/0x2fc)
[<8004a880>] (__dma_alloc+0xa4/0x2fc) from [<8004b0b4>] (dma_alloc_coherent+0x54/0x60)
[<8004b0b4>] (dma_alloc_coherent+0x54/0x60) from [<803ced70>] (mxc_ipu_ioctl+0x270/0x3ec)
[<803ced70>] (mxc_ipu_ioctl+0x270/0x3ec) from [<80123b78>] (do_vfs_ioctl+0x80/0x54c)
[<80123b78>] (do_vfs_ioctl+0x80/0x54c) from [<8012407c>] (sys_ioctl+0x38/0x5c)
[<8012407c>] (sys_ioctl+0x38/0x5c) from [<80041f80>] (ret_fast_syscall+0x0/0x30)
---[ end trace 4e0c10ffc7ffc0d8 ]---
Run Code Online (Sandbox Code Playgroud)
我尝试了不同的值,它看起来dma_alloc_coherent()不能分配超过2 ^ 25字节(32 MB).
如何分配如此大的DMA缓冲区?
我想dma使用dmaengine.c文件(linux/drivers/dma)中的dma_async_memcpy_buf_to_buf函数.为此,我在dmatest.c文件(linux/drivers/dma)中添加了一个函数,如下所示:
void foo ()
{
int index = 0;
dma_cookie_t cookie;
size_t len = 0x20000;
ktime_t start, end, end1, end2, end3;
s64 actual_time;
u16* dest;
u16* src;
dest = kmalloc(len, GFP_KERNEL);
src = kmalloc(len, GFP_KERNEL);
for (index = 0; index < len/2; index++)
{
dest[index] = 0xAA55;
src[index] = 0xDEAD;
}
start = ktime_get();
cookie = dma_async_memcpy_buf_to_buf(chan, dest, src, len);
while (dma_async_is_tx_complete(chan, cookie, NULL, NULL) == DMA_IN_PROGRESS)
{
dma_sync_wait(chan, cookie);
}
end = ktime_get();
actual_time = ktime_to_ns(ktime_sub(end, start));
printk("Time taken …Run Code Online (Sandbox Code Playgroud) 我正在查看Linux环回和IP网络数据处理,似乎没有代码可以覆盖不同套接字上的2个CPU通过环回传递数据的情况.
我认为应该可以检测到这种情况,然后在可用时应用硬件DMA,以避免NUMA争用将数据复制到接收器.
我的问题是:
我正在尝试在FPGA和x86_64 Linux机器之间进行DMA传输.
在PC端我正在做这个初始化:
//driver probe
...
pci_set_master(dev); //set endpoint as master
result = pci_set_dma_mask(dev, 0xffffffffffffffff); //set as 64bit capable
...
//read
pagePointer = __get_free_page(__GFP_HIGHMEM); //get 1 page
temp_addr = dma_map_page(&myPCIDev->dev,pagePointer,0,PAGE_SIZE,DMA_TO_DEVICE);
printk(KERN_WARNING "[%s]Page address: 0x%lx Bus address: 0x%lx\n",DEVICE_NAME,pagePointer,temp_addr);
writeq(cpu_to_be64(temp_addr),bar0Addr); //send address to FPGA
wmb();
writeq(cpu_to_be64(1),bar1Addr); //start trasnfer
wmb();
Run Code Online (Sandbox Code Playgroud)
总线地址是64位地址.在FPGA端,我发送的TLP读取1 DW:
Fmt: "001"
Type: "00000"
R|TC|R|Attr|R|TH : "00000000"
TD|EP|Attr|AT : "000000"
Length : "0000000001"
Requester ID
Tag : "00000000"
Byte Enable : "00001111";
Address : (address from dma map page)
Run Code Online (Sandbox Code Playgroud)
我从PC上回来的完成是:
Fmt: …Run Code Online (Sandbox Code Playgroud) 我想在内核空间中编写一个驱动程序:
我需要一些类似的例子来指导我。有谁知道我在哪里可以找到一些来源?
目前正在研究 PCI 设备驱动程序。设备的编程如下:
当 DMA 传输完成时,设备向 PC 发送 MSI 中断,其中包含 MSI 数据“001”二进制。
现在我正在为这个 pci 设备编写一个驱动程序,对于 MSI 部分,我有一些问题。
在维基百科中,它说:
MSI允许设备将少量中断描述数据写入特殊的内存映射I/O地址,然后芯片组将相应的中断传递给处理器。
Q1:那么就我而言,是small amount of interrupt-describing data从"001"pci 设备发送到 PC 的吗?
在我的驱动程序代码中,MSI irq 的注册方式如下:
err = pci_enable_msi(my_pci_dev);
err = request_irq(my_pci_dev->irq, irq_handler, 0, "PCI_FPGA_CARD", NULL);
Run Code Online (Sandbox Code Playgroud)
的irq_handler定义如下:
static irqreturn_t irq_handler(int irq, void *dev_id)
{
printk(KERN_INFO "(irq_handler): Called\n");
return IRQ_HANDLED;
}
Run Code Online (Sandbox Code Playgroud)
Q2:有了上面的3个内核函数,我们如何获取消息呢"001"?
Q3: PCI 设备最多支持 8 个 MSI 矢量,因此要使用所有这 8 个矢量,我应该使用下面的代码,否则都不正确:
err = pci_enable_msi_block(my_pci_dev,8);
err = request_irq(my_pci_dev->irq, irq_handler, 0, …Run Code Online (Sandbox Code Playgroud) 我试图在 linux 内核模块中获取系统中所有可用内存的物理地址范围。
我看到 cat /proc/iomem 并且看到物理内存本身不是连续的。
我知道对于 32 位系统兼容性,PCI 和其他外围内存需要在 4GB 地址范围内。也是 DOS 的 640 kB 初始值。以下输出来自 x86_64 系统
00000000-00000fff : reserved
00001000-0009d7ff : System RAM //640kB here
0009d800-0009ffff : reserved
000a0000-000bffff : PCI Bus 0000:00
000c0000-000cedff : Video ROM
000e0000-000fffff : reserved
000f0000-000fffff : System ROM
00100000-daa85fff : System RAM //~3.5 gb here
01000000-0177acb8 : Kernel code
0177acb9-01d1b53f : Kernel data
01e79000-01fbdfff : Kernel bss
daa86000-daa87fff : reserved
daa88000-dad0efff : System RAM //some RAM here
dad0f000-dae75fff : reserved …Run Code Online (Sandbox Code Playgroud) 是的,我最终会将它用于 DMA,但暂时将一致性放在一边。我有 64 位 BAR 寄存器,因此,AFAIK,所有 RAM(例如高于 4G)都可用于 DMA。
我正在寻找大约 64MB 的连续 RAM。是的,很多。
Ubuntu 16 和 18 具有CONFIG_CMA=y但未CONFIG_DMA_CMA在内核编译时设置。
我注意到,如果两者都设置(在内核构建时),我可以简单地调用dma_alloc_coherent,但是,出于逻辑原因,重新编译内核是不可取的。
这些机器将始终具有至少 32GB 的 RAM,不运行任何占用大量内存的东西,并且内核模块将在启动后不久加载,然后 RAM 变得明显碎片化,而且,AFAIK,没有其他东西使用 CMA。
我已经设置了内核参数 CMA=1G。(并尝试过 256M 和 512M)
# dmesg | grep cma
[ 0.000000] Command line: BOOT_IMAGE=/boot/vmlinuz-4.4.170 root=UUID=2b25933c-e10c-4833-b5b2-92e9d3a33fec ro cma=1G
[ 0.000000] Kernel command line: BOOT_IMAGE=/boot/vmlinuz-4.4.170 root=UUID=2b25933c-e10c-4833-b5b2-92e9d3a33fec ro cma=1G
[ 0.000000] Memory: 65612056K/67073924K available (8604K kernel code, 1332K rwdata, 3972K rodata, 1484K init, 1316K bss, 1461868K reserved, 0K cma-reserved)
Run Code Online (Sandbox Code Playgroud)
我试过了 …