【问题标题】:Linux device driver DMA memory buffer not seen in order by PCIe hardwarePCIe 硬件未按顺序查看 Linux 设备驱动程序 DMA 内存缓冲区
【发布时间】:2014-02-07 16:49:05
【问题描述】:

我正在为 Xilinx Virtex 6 PCIe 定制板开发设备驱动程序。 在进行 DMA 写入(从主机到设备)时,会发生以下情况:

用户空间应用:

a. fill buffer with the following byte pattern (tested up to 16kB)
    00 00 .. 00 (64bytes)
    01 01 .. 00 (64bytes)
    ...
    ff ff .. ff (64bytes)
    00 00 .. 00 (64bytes)
    01 01 .. 00 (64bytes)
    etc

b. call custom ioctl to pass pointer to buffer and size

内核空间:

a. retrieve buffer (bufp) with 
    copy_from_user(ptdev->kbuf, bufp, cnt)
b. setup and start DMA 
    b1. //setup physical address
        iowrite32(cpu_to_be32((u32) ptdev->kbuf_dma_addr),
            ptdev->region0 + TDO_DMA_HOST_ADDR);
    b2. //setup transfer size
        iowrite32(cpu_to_be32( ((cnt+3)/4)*4 ), 
            ptdev->region0 + TDO_DMA_BYTELEN);
    b3. //memory barrier to make sure kbuf is in memorry
        mb(); 
    //start dma
    b4. iowrite32(cpu_to_be32(TDO_DMA_H2A | TDO_DMA_BURST_FIXED | TDO_DMA_START),
                ptdev->region0 + TDO_DMA_CTL_STAT);
c. put process to sleep
    wait_res = wait_event_interruptible_timeout(ptdev->dma_queue, 
                            !(tdo_dma_busy(ptdev, &dma_stat)), 
                            timeout);
d. check wait_res result and dma status register and return

Note that the kernel buffer is allocated once at device probe with:
ptdev->kbuf = pci_alloc_consistent(dev, ptdev->kbuf_size, --512kB
                                &ptdev->kbuf_dma_addr);

设备 pcie TLP 转储(通过 Xilinx 内核后的逻辑分析仪获得):

a. TLP received (by the device)
 a1. 40000001 0000000F F7C04808 37900000 (MWr corresponds to b1 above)
 a1. 40000001 0000000F F7C0480C 00000FF8 (MWr corresponds to b2 above)
 a1. 40000001 0000000F F7C04800 00010011 (MWr corresponds to b4 above)

b. TLP sent (by the device)
 b1. 00000080 010000FF 37900000 (MRd 80h DW @ addr 37900000h)
 b2. 00000080 010000FF 37900200 (MRd 80h DW @ addr 37900200h)
 b3. 00000080 010000FF 37900400 (MRd 80h DW @ addr 37900400h)
 b4. 00000080 010000FF 37900600 (MRd 80h DW @ addr 37900600h)
...

c. TLP received (by the device)
 c1. 4A000020 00000080 01000000 00 00 .. 00 01 01 .. 01 CplD 128B
 c2. 4A000020 00000080 01000000 02 02 .. 02 03 03 .. 03 CplD 128B
 c3. 4A000020 00000080 01000000 04 04 .. 04 05 05 .. 05 CplD 128B 
 c4. 4A000020 00000080 01000000 06 06 .. 0A 0A 0A .. 0A CplD 128B  <= 
 c5. 4A000010 00000040 01000040 07 07 .. 07             CplD  64B  <= 
 c6. 4A000010 00000040 01000040 0B 0B .. 0B             CplD  64B  <= 
 c7. 4A000020 00000080 01000000 08 08 .. 08 09 09 .. 09 CplD 128B  <= 
 c8. 4A000020 00000080 01000000 0C 0C .. 0C 0D 0D .. 0D CplD 128B 
.. the remaining bytes are transfered correctly and 
the total number of bytes (FF8h) matches the requested size
signal interrupt

现在这种明显的内存排序错误发生的概率很高(0.8

编辑:请注意,上面的 c4 点表明内核驱动程序没有以正确的顺序填充内存(我想内存控制器用连续内存填充 TLP)。 64B 是缓存行大小,这可能与缓存操作有关。

当我禁用内核缓冲区上的缓存时,

echo "base=0xaf180000 size=0x00008000 type=uncachable" > /proc/mtrr

错误仍然发生,但很少发生(p

这只发生在基于 i7-4770 (Haswell) 的机器上(在 3 台相同的机器上测试,带有 3 个板)。 我尝试了内核 2.6.32 (RH6.5)、stock 3.10.28 和 stock 3.13.1,结果相同。

我在基于 i7-610 QM57 的机器和 Xeon 5400 机器上尝试了代码和设备,没有任何问题。

欢迎提出任何想法/建议。

最好的问候

克劳迪奥

【问题讨论】:

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


    【解决方案1】:

    我知道这是一个旧线程,但“错误”的原因是完成重新排序。不必按顺序回答多个未完成的读取请求。完成只是为了相同的请求。 最重要的是:总是为请求分配相同的标签,如果请求同时处于活动状态,则这是非法的。

    【讨论】:

      【解决方案2】:

      在示例中,所有 MemRd TLP 都具有相同的 TAG。如果您还没有收到与此 TAG 对应的最后一个 CplD,则您不能使用相同的 TAG。因此,如果您发送 MemRd,请等到您获得带有此标签的 CplD 并再次触发 MemRd,您的所有数据都会井然有序(但在这种情况下,总线利用率会很低,您无法获得高带宽占用)。

      另请阅读pci_alloc_consistent uncached memory。它不喜欢作为您平台上的缓存问题。我最好调试设备内核。

      【讨论】:

        【解决方案3】:

        QM57 支持 PCIe 2.0

        而我认为 i7-4770 机器的主板支持 PCIe 3.0

        我怀疑 PCIe 3.0 主板和您的 V6 设备(也是 PCIe 2.0)之间可能存在某种协商失败

        【讨论】:

        • 感谢您的回答。 lspci 给了我以下信息:
          LnkSta: Speed 2.5GT/s, Width x8, TrErr- Train- SlotClk- DLActive- BWMgmt- ABWMgmt-.
          似乎它协商了一个 PCIe 2.0 (2.5GT/s) 链接。
        • 我记得如果扩展位打开,PCIe 支持重新排序 TLP。在pci_regs.h 中是PCI_EXP_DEVCTL_RELAX_EN。你能检查这是否与你的问题有关吗? cf. github.com/torvalds/linux/blob/master/drivers/net/ethernet/…
        • @nodaki。事实上,TLP 有一个与排序和缓存一致性相关的属性(2 位)。这些是 TLP 标头的字节 2 中的位 5:4。位 5:宽松排序位(如果 1 允许宽松排序,如果 0 使用严格排序) 位 4:无监听位(如果 1 禁用此访问的监听,如果启用 0 监听)在 b[1,2,3,. ..] 我的设计发送 00000080 : MRd TC=0 EP=0 TD=0 Attr=0 Len=0x80 DW (256Bytes note that's = MaxReadRequest) 尽管如此,我还是按照您的建议清除了 PCI_EXP_DEVCTL_RELAX_EN,没有明显的变化。
        猜你喜欢
        • 2019-11-19
        • 2012-03-12
        • 1970-01-01
        • 2017-07-01
        • 1970-01-01
        • 1970-01-01
        • 2011-07-29
        • 1970-01-01
        • 2021-07-30
        相关资源
        最近更新 更多