【问题标题】:Two-way communication to PCIe device via /dev/mem in Linux user-space?在 Linux 用户空间中通过 /dev/mem 与 PCIe 设备进行双向通信?
【发布时间】:2016-04-25 14:02:12
【问题描述】:

很确定我已经知道这个问题的答案,因为已经有关于 SO 的相关问题(hereherehere,和 this 很有用),,,但我想在我深入内核空间驱动程序领域之前绝对确定(以前从未去过那里)。

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

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

EDIT1:我的 CPU 是 ARM7 架构。

【问题讨论】:

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


    【解决方案1】:

    实际上,Ctx 关于memmap 的评论让我走上了正确的道路。为了保留内存,我给出了一个引导加载程序参数memmap=[size]$[location],我找到了here。不同的符号意味着不同的东西,它们并不完全直观。再稍微更正一下,标志是CONFIG_STRICT_DEVMEM,我的内核没有用它编译。

    还有一些谜团。例如,memmap 参数中的[location] 似乎毫无意义。无论我为该位置设置什么,linux 都会将所有未使用[size] 保留的内容放在一个连续的块中,而我保留的空间位于最后。唯一的迹象是查看/proc/iomem。我保留的空间量与 linux 内存空间结束和系统内存空间结束之间的差距相匹配。除了在/proc/iomem 中没有被linux 采用之外,我在任何地方都找不到任何迹象表明linux 说“我看到了你保留的块,我不会碰它”。但是 FPGA 已经写入这个空间好几天了,对 linux 没有明显的不良影响,所以我想我们都很好!我可以只映射到那个位置并读取数据(很惊讶这很有效,因为 linux 没有表明它存在,但很高兴它确实存在)。谢谢您的帮助! Ian 如果我转到内核驱动程序空间,我会回复您的评论。

    【讨论】:

      【解决方案2】:

      Hugepages-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
      

      然后打开一些文件并映射它:

      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找到物理地址。

      【讨论】:

      • 拍摄,可能应该提到我正在使用的 CPU(至少现在)是 ARM7 架构。不确定这是否会使您的答案变得毫无意义。非常感谢您提供的信息,我一定会在未来几天探索这种方法。
      • 我认为,32 位 arm 没有 1G-hugepages(虽然可能更少)。另一种方法是保留部分内存(例如内核引导参数memmap=100M@0x10000000 在 1G 物理内存处保留 100MB)并将其从 /dev/mem 映射到您的进程内存中。您必须确保内核没有使用 CONFIG_DEVMEM_STRICT 编译,但是
      • 对于内核空间驱动程序,dma_alloc_coherent 可以分配适合设备和 CPU 访问的内存。驱动程序对mmap 的文件操作处理程序可以调用dma_mmap_coherent 将其映射到用户的地址空间。困难在于您可以使用dma_alloc_coherent 分配的内存大小通常非常有限。您可以使用cma 内核引导参数来克服这个问题,例如cma=100M。在实践中,您通常需要使 cma 参数大于您的需要,因为其他驱动程序可能会在您的驱动程序之前窃取其中的一部分。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2017-12-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-11-09
      相关资源
      最近更新 更多