通过Linux用户空间中的/ dev / mem与PCIe设备进行双向通信?

时间:2016-01-19 22:12:38

标签: linux-kernel driver linux-device-driver embedded-linux pci-e

非常确定我已经知道这个问题的答案,因为已经有关于SO的相关问题(hereherehere,而this很有用),,,但我想在进入内核空间驱动程序之前绝对肯定(之前从未有过)。

我有一个PCIe设备,我需要与linux用户空间中的应用程序进行通信(反之亦然)。通过打开/dev/mem,然后mmap,我已经能够编写一个构建在pciutils之上的用户空间驱动程序,它允许我mmap个BAR并成功将数据写入设备。现在,我们需要comm从另一个方向,从PCIe设备到linux用户应用程序。为了实现这一点,我们相信我们需要一个大块(~100MB)的物理连续内存,永远不会被分页/交换。一旦分配,该地址将需要传递给PCIe设备,因此它知道在哪里写入其数据(因此我不知道这可能是虚拟的,可交换的内存)。没有内核空间驱动程序,有没有办法做到这一点?这里有一个想法是浮动的,也许我们可以打开/dev/mem然后用ioctl命令来分配我们需要的东西?如果可能的话,我还没有在网上找到任何例子,需要更多地研究它。

假设我们需要内核空间驱动程序,最好在启动时分配我们的大卡盘,然后使用ioremap获取内核虚拟地址,然后mmap从那里到用户空间,正确?从我在kmalloc上看到的内容来看,使用该调用时我们不会接近100MB,而vmalloc因为虚拟内存而无法获得好处。为了在启动时分配,驱动程序应该静态链接到内核中,对吗?这基本上是一个嵌入式应用程序,因此可移植性对我来说不是一个大问题。一个模块而不是一个静态链接的驱动程序可能会工作,但我担心内存碎片可能会阻止找到一个物理上连续的区域,所以我想尽快分配它。有什么反馈吗?

EDIT1:我的CPU是ARM7架构。

2 个答案:

答案 0 :(得分:1)

<强>大页面-1G

当前的x86_64处理器不仅支持4k和2M,还支持1G页面(/ proc / cpuinfo中的标志pdpe1gb表示支持)。

必须在内核启动时保留这些1G页面,因此必须指定启动参数hugepagesz=1GB hugepages=1

然后,必须安装hugetlbfs:

mkdir /hugetlb-1G
mount -t hugetlbfs -o pagesize=1G none /hugetlb-1G

然后打开一些文件并对其进行mmap:

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找到实际地址。

答案 1 :(得分:0)

实际上Ctx关于memmap的评论让我走上了正确的道路。为了保留内存,我给了memmap=[size]$[location]一个bootloader参数,我发现here。不同的符号意味着不同的东西,它们并不完全直观。只是另一个小的修正,标志是CONFIG_STRICT_DEVMEM,我的内核没有编译。

还有一些谜团。例如,[location]参数中的memmap似乎毫无意义。无论我为该位置设置什么,linux都会将[size]中没有保留的所有内容放在一个连续的块中,而我保留的空间就在最后。唯一的迹象是/proc/iomem。我保留的空间量与linux内存空间的末尾和系统内存空间的末尾之间的差距相匹配。我找不到任何指示linux说“我看到你保留的块,我不会触摸它”,除了它不是由/proc/iomem中的linux采取的。但是FPGA已经写入这个空间好几天了,对linux没有明显的不良影响,所以我想我们都很好!我可以直接mmap到那个位置并读取数据(这很有用,因为linux并没有表明存在这种情况,但很高兴它确实存在)。谢谢您的帮助!如果我去内核驱动程序空间,我会回到你的评论。