通过 Linux user-space 中的 /dev/mem 与 PCIe 设备的双向通信?
Two-way communication to PCIe device via /dev/mem in Linux user-space?
很确定我已经知道这个问题的答案,因为已经有关于 SO 的相关问题(here, here, and here,, and this 很有用),但是我想在深入内核之前绝对确定-space driver land(之前没去过)
我有一个 PCIe 设备,我需要通过 linux 用户 space 中的应用与之通信(反之亦然)。通过打开 /dev/mem
,然后 mmap
'ing,我已经能够编写一个基于 pciutils 的用户 space 驱动程序,它允许我 mmap
BAR 并成功将数据写入设备。现在,我们需要从 PCIe 设备到 linux 用户应用程序的另一个方向进行通信。为了让它工作,我们相信我们将需要一大块 (~100MB) 的物理连续内存,永远不会 paged/swapped。分配后,该地址将需要传递给 PCIe 设备,以便它知道将其数据写入何处(因此我看不出这怎么可能是虚拟的、可交换的内存)。如果没有内核 space 驱动程序,有没有办法做到这一点?这里提出了一个想法,也许我们可以打开 /dev/mem
然后给它一个 ioctl
命令来分配我们需要的东西?如果可能的话,我还没有在网上找到任何例子,需要更深入地研究它。
假设我们需要一个内核 space 驱动程序,最好在启动期间分配我们的大卡盘,然后使用 ioremap
获取内核虚拟地址,然后 mmap
从用户-space,对吗?根据我在 kmalloc
上阅读的内容,使用该调用我们不会获得接近 100MB 的空间,并且 vmalloc
也不好,因为那是虚拟内存。为了在启动时分配,驱动程序应该静态链接到内核中,对吗?这基本上是一个嵌入式应用程序,所以可移植性对我来说不是一个大问题。一个模块而不是静态链接的驱动程序可能可以工作,但我担心内存碎片可能会阻止找到物理上连续的区域,所以我想在开机后尽快分配它。有任何反馈吗?
EDIT1:我的 CPU 是 ARM7 架构。
Hugepages-1G
目前x86_64-processors不仅支持4k和2M,还支持1G-pages(/proc/cpuinfo中的flag pdpe1gb表示支持)。
这些 1G 页面必须在内核启动时保留,因此必须指定 boot-parameters hugepagesz=1GB hugepages=1
。
然后,必须挂载 hugetlbfs:
mkdir /hugetlb-1G
mount -t hugetlbfs -o pagesize=1G none /hugetlb-1G
然后打开一些文件并映射它:
fd = open("/hugetlb-1G/page-1", O_CREAT | O_RDWR, 0755);
addr = mmap(NULL, SIZE_1G, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
您现在可以在 addr
访问 1G 的物理连续内存。为确保它不会被换出,您可以使用 mlock(但这对于大页面来说甚至根本没有必要)。
即使你的进程崩溃了,巨大的页面也会像上面一样被保留用于映射它,所以pci-e设备不会将流氓写入系统或进程内存。
您可以通过阅读/proc/pid/pagemap
找到物理地址。
实际上,Ctx 关于 memmap
的评论让我走上了正确的道路。为了保留内存,我将引导加载程序参数设置为 memmap=[size]$[location]
,我发现它是 here。不同的符号表示不同的事物,而且它们并不完全直观。只是另一个轻微的更正,标志是 CONFIG_STRICT_DEVMEM
,我的内核没有用它编译。
还有一些未解之谜。例如,memmap
参数中的 [location]
似乎没有意义。无论我为该位置设置什么,linux 将所有未与 [size]
保留的内容放在一个连续的块中,而我保留的 space 在最后。唯一的迹象是查看 /proc/iomem
。我保留的 space 数量与 linux 内存末尾 space 和系统内存末尾 space 之间的间隙相匹配。我在任何地方都找不到 linux 说 "I see your reserved chunk and I won't touch it" 的迹象,除了它不是 linux 在 /proc/iomem
中使用的。但是 FPGA 已经写入 space 好几天了,对 linux 没有明显的不良影响,所以我想我们都很好!我可以直接映射到那个位置并读取数据(这让我感到惊讶,因为 linux 并不表示它存在,但很高兴它确实存在)。谢谢您的帮助! Ian 如果我转到内核驱动程序 space.
,我会回复你的评论
很确定我已经知道这个问题的答案,因为已经有关于 SO 的相关问题(here, here, and here,, and this 很有用),但是我想在深入内核之前绝对确定-space driver land(之前没去过)
我有一个 PCIe 设备,我需要通过 linux 用户 space 中的应用与之通信(反之亦然)。通过打开 /dev/mem
,然后 mmap
'ing,我已经能够编写一个基于 pciutils 的用户 space 驱动程序,它允许我 mmap
BAR 并成功将数据写入设备。现在,我们需要从 PCIe 设备到 linux 用户应用程序的另一个方向进行通信。为了让它工作,我们相信我们将需要一大块 (~100MB) 的物理连续内存,永远不会 paged/swapped。分配后,该地址将需要传递给 PCIe 设备,以便它知道将其数据写入何处(因此我看不出这怎么可能是虚拟的、可交换的内存)。如果没有内核 space 驱动程序,有没有办法做到这一点?这里提出了一个想法,也许我们可以打开 /dev/mem
然后给它一个 ioctl
命令来分配我们需要的东西?如果可能的话,我还没有在网上找到任何例子,需要更深入地研究它。
假设我们需要一个内核 space 驱动程序,最好在启动期间分配我们的大卡盘,然后使用 ioremap
获取内核虚拟地址,然后 mmap
从用户-space,对吗?根据我在 kmalloc
上阅读的内容,使用该调用我们不会获得接近 100MB 的空间,并且 vmalloc
也不好,因为那是虚拟内存。为了在启动时分配,驱动程序应该静态链接到内核中,对吗?这基本上是一个嵌入式应用程序,所以可移植性对我来说不是一个大问题。一个模块而不是静态链接的驱动程序可能可以工作,但我担心内存碎片可能会阻止找到物理上连续的区域,所以我想在开机后尽快分配它。有任何反馈吗?
EDIT1:我的 CPU 是 ARM7 架构。
Hugepages-1G
目前x86_64-processors不仅支持4k和2M,还支持1G-pages(/proc/cpuinfo中的flag pdpe1gb表示支持)。
这些 1G 页面必须在内核启动时保留,因此必须指定 boot-parameters hugepagesz=1GB hugepages=1
。
然后,必须挂载 hugetlbfs:
mkdir /hugetlb-1G
mount -t hugetlbfs -o pagesize=1G none /hugetlb-1G
然后打开一些文件并映射它:
fd = open("/hugetlb-1G/page-1", O_CREAT | O_RDWR, 0755);
addr = mmap(NULL, SIZE_1G, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
您现在可以在 addr
访问 1G 的物理连续内存。为确保它不会被换出,您可以使用 mlock(但这对于大页面来说甚至根本没有必要)。
即使你的进程崩溃了,巨大的页面也会像上面一样被保留用于映射它,所以pci-e设备不会将流氓写入系统或进程内存。
您可以通过阅读/proc/pid/pagemap
找到物理地址。
实际上,Ctx 关于 memmap
的评论让我走上了正确的道路。为了保留内存,我将引导加载程序参数设置为 memmap=[size]$[location]
,我发现它是 here。不同的符号表示不同的事物,而且它们并不完全直观。只是另一个轻微的更正,标志是 CONFIG_STRICT_DEVMEM
,我的内核没有用它编译。
还有一些未解之谜。例如,memmap
参数中的 [location]
似乎没有意义。无论我为该位置设置什么,linux 将所有未与 [size]
保留的内容放在一个连续的块中,而我保留的 space 在最后。唯一的迹象是查看 /proc/iomem
。我保留的 space 数量与 linux 内存末尾 space 和系统内存末尾 space 之间的间隙相匹配。我在任何地方都找不到 linux 说 "I see your reserved chunk and I won't touch it" 的迹象,除了它不是 linux 在 /proc/iomem
中使用的。但是 FPGA 已经写入 space 好几天了,对 linux 没有明显的不良影响,所以我想我们都很好!我可以直接映射到那个位置并读取数据(这让我感到惊讶,因为 linux 并不表示它存在,但很高兴它确实存在)。谢谢您的帮助! Ian 如果我转到内核驱动程序 space.