映射MMIO区域的写回操作不起作用。

3
我希望所有对PCIe设备的读写请求都能被CPU缓存,但实际情况并非如此。以下是我对写回MMIO区域的假设:
  1. 对PCIe设备的写操作只会在缓存写回时发生。
  2. TLP负载的大小为缓存块大小(64B)。
然而,捕获到的TLP并不符合我的假设:
  1. 对PCIe设备的写操作发生在每次对MMIO区域的写入中。
  2. TLP负载的大小为1B。
我使用以下用户空间程序和设备驱动程序向MMIO区域写入8字节的0xff。
struct pcie_ioctl ioctl_control;
ioctl_control.bar_select = BAR_ID;
ioctl_control.num_bytes_to_write = atoi(argv[1]);
if (ioctl(fd, IOCTL_WRITE_0xFF, &ioctl_control) < 0) {
    printf("ioctl failed\n");
}

设备驱动程序的一部分

case IOCTL_WRITE_0xFF:
{
    int i;
    char *buff;
    struct pci_cdev_struct *pci_cdev = pci_get_drvdata(fpga_pcie_dev.pci_device);
    copy_from_user(&ioctl_control, (void __user *)arg, sizeof(ioctl_control));
    buff = kmalloc(sizeof(char) * ioctl_control.num_bytes_to_write, GFP_KERNEL);
    for (i = 0; i < ioctl_control.num_bytes_to_write; i++) {
        buff[i] = 0xff;
    }
    memcpy(pci_cdev->bar[ioctl_control.bar_select], buff, ioctl_control.num_bytes_to_write);
    kfree(buff);
    break;
}

我修改了MTRRs,使相应的MMIO区域变为写回模式。MMIO区域从0x0c7300000开始,长度为0x100000(1MB)。以下是不同策略下的cat /proc/mtrr结果。请注意,我将每个区域设置为独占。 不可缓存
reg00: base=0x080000000 ( 2048MB), size= 1024MB, count=1: uncachable
reg01: base=0x380000000000 (58720256MB), size=524288MB, count=1: uncachable
reg02: base=0x0c0000000 ( 3072MB), size=   64MB, count=1: uncachable
reg03: base=0x0c4000000 ( 3136MB), size=   32MB, count=1: uncachable
reg04: base=0x0c6000000 ( 3168MB), size=   16MB, count=1: uncachable
reg05: base=0x0c7000000 ( 3184MB), size=    1MB, count=1: uncachable
reg06: base=0x0c7100000 ( 3185MB), size=    1MB, count=1: uncachable
reg07: base=0x0c7200000 ( 3186MB), size=    1MB, count=1: uncachable
reg08: base=0x0c7300000 ( 3187MB), size=    1MB, count=1: uncachable
reg09: base=0x0c7400000 ( 3188MB), size=    1MB, count=1: uncachable

写合并

reg00: base=0x080000000 ( 2048MB), size= 1024MB, count=1: uncachable
reg01: base=0x380000000000 (58720256MB), size=524288MB, count=1: uncachable
reg02: base=0x0c0000000 ( 3072MB), size=   64MB, count=1: uncachable
reg03: base=0x0c4000000 ( 3136MB), size=   32MB, count=1: uncachable
reg04: base=0x0c6000000 ( 3168MB), size=   16MB, count=1: uncachable
reg05: base=0x0c7000000 ( 3184MB), size=    1MB, count=1: uncachable
reg06: base=0x0c7100000 ( 3185MB), size=    1MB, count=1: uncachable
reg07: base=0x0c7200000 ( 3186MB), size=    1MB, count=1: uncachable
reg08: base=0x0c7300000 ( 3187MB), size=    1MB, count=1: write-combining
reg09: base=0x0c7400000 ( 3188MB), size=    1MB, count=1: uncachable

写回

reg00: base=0x080000000 ( 2048MB), size= 1024MB, count=1: uncachable
reg01: base=0x380000000000 (58720256MB), size=524288MB, count=1: uncachable
reg02: base=0x0c0000000 ( 3072MB), size=   64MB, count=1: uncachable
reg03: base=0x0c4000000 ( 3136MB), size=   32MB, count=1: uncachable
reg04: base=0x0c6000000 ( 3168MB), size=   16MB, count=1: uncachable
reg05: base=0x0c7000000 ( 3184MB), size=    1MB, count=1: uncachable
reg06: base=0x0c7100000 ( 3185MB), size=    1MB, count=1: uncachable
reg07: base=0x0c7200000 ( 3186MB), size=    1MB, count=1: uncachable
reg08: base=0x0c7300000 ( 3187MB), size=    1MB, count=1: write-back
reg09: base=0x0c7400000 ( 3188MB), size=    1MB, count=1: uncachable

以下是使用不同策略进行8B写入的波形捕获。我使用集成逻辑分析仪(ILA)来捕获这些波形。请观察pcie_endpoint_litepcietlpdepacketizer_tlp_req_valid被设置时的pcie_endpoint_litepcietlpdepacketizer_tlp_req_payload_dat。您可以通过在这些波形示例中计算pcie_endpoint_litepcietlpdepacketizer_tlp_req_valid的数量来计算数据包的数量。
1. 不可缓存:link -> 正确,1B x 8个数据包 2. 写组合:link -> 正确,8B x 1个数据包 3. 回写:link -> 不符合预期,1B x 8个数据包
系统配置如下。
  • CPU: 英特尔 Xeon E5-2630 v4 @ 2.20GHz
  • 操作系统: Linux内核版本4.15.0-38
  • PCIe设备: Xilinx FPGA KC705,使用litepcie进行编程

相关链接

  1. 从x86 CPU生成一个64字节的PCIe TLP读取
  2. 如何在Intel®架构上实现64B PCIe* Burst Transfer
  3. 写结合缓冲区乱序写入和PCIe
  4. Ryzen是否支持通过PCIe接口进行内存映射IO的写回缓存?
  5. MTRR(内存类型范围寄存器)控制
  6. PATting Linux
  7. 深入了解TLP:PCI Express设备通信方式(第一部分)

1
有可能其他东西(如PAT)覆盖了MTRR设置,使其实际上成为UC而不是WB吗?顺便说一句,单字节事务可能来自于mempcy在内核中被实现为rep movsb(因为您的CPU足够新,支持ERMSB,这使得rep movsb相当不错)。 - Peter Cordes
1
@PeterCordes 谢谢,Peter。我在PAT中找到了一个冲突。该区域被设置为“未缓存减去”。我会尝试解决它。 - Taekyung Heo
1个回答

2
简而言之,似乎将MMIO区域写回映射并不起作用。
如果有人发现可能可以,请上传答案。
我找到了John McCalpin的文章和答案。首先,将MMIO区域写回映射是不可能的。其次,在某些处理器上可以使用解决方法。
  1. 无法将MMIO区域写回映射

    此链接引用的语句

    注意:WB类型不能与内存映射IO一起使用。您可以编程位以设置映射为WB,但是系统会在收到不知道如何处理的事务时立即崩溃。理论上可以使用WP或WT从MMIO获得缓存读取,但必须在软件中处理一致性。

    此链接引用的语句

    只有在我将PAT和MTRR都设置为WB时,内核才会崩溃

  2. 某些处理器可以实现解决方法

    《关于对内存映射IO区域进行缓存访问的笔记》,John McCalpin

    至少在某些x86-64处理器上可以使一组映射正常工作,它基于将MMIO空间映射两次。使用允许写组合存储(但仅限未缓存读取)的属性集映射MMIO范围。使用允许缓存行读取(但仅限未缓存、非写组合存储)的属性集再次映射MMIO范围。


1
我想知道Skylake-SP是否有任何改变,使得使用单个AVX512指令可以将64字节存储到UC MMIO区域。但是,与在UC内存区域上使用movntps [rdi], zmm0movaps相比,缓存逐出可能仍然不同。 - Peter Cordes

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接