Joe*_*oel 7 linux x86 linux-device-driver linux-kernel pci-e
在 PCI Express Base 规范的第 2.2.5 节“第一个/最后一个 DW 字节启用规则”中,它说零长度读取可以用作刷新请求。但是,在 linux 内核文档中,大多数示例仅使用 1B 或 4B 读取请求:
我想知道 x86-64 架构是否有可能生成导致 PCI 读取长度为零的指令,如果可以,是否有一些 linux 内核函数可以创建该指令。
您提到的两个示例涉及 MMIO 访问或从 CPU 到 I/O 设备的传统 I/O 端口访问,但 PCIe 规范第 2.2.5 节中的零长度读取实现说明是关于来自 I/O 的访问设备。PCIe规范和Intel/AMD64 x86手册显然彼此不同,并且它们使用不同的术语,所以我不明白你是如何混淆两者的。不,x86 中不存在零长度读取这样的东西。
第一个链接的代码如下:
WRT_REG_WORD(®->ictrl, 0);
/*
* The following read will ensure that the above write
* has been received by the device before we return from this
* function.
*/
RD_REG_WORD(®->ictrl);
Run Code Online (Sandbox Code Playgroud)
对同一地址进行 16 位 MMIO 写入,然后进行 16 位 MMIO 读取。目标位置的内存类型很可能是 UC,这确保所有 UC 访问都按程序顺序出现在系统总线上。这意味着它按顺序到达 PCIe 根联合体(集成在现代处理器上)。MMIO 写入由处理器的 I/O 单元转换为发布写入 PCIe 事务,而读取则转换为非发布读取 PCIe 事务。这两个事务都将具有流量类别并且禁用宽松排序。根据事务顺序规则,此类非发布读取不能与任何较早发布写入一起重新排序。总体效果是,当 UC 读取返回结果时,前面的 UC 写入必须已经在目标 I/O 设备上完成。
您提供的第二个链接还包括一个 MMIO 排序示例,其工作方式完全相同。在发布写入后发出读取是确定写入何时完成的常用技术。UC 读取不是 x86 中的完全序列化操作。如果您不希望在读取完成之前执行任何后续指令(不是 UC 访问),则需要在读取后添加完全序列化指令。Linux 内核本身定义了许多用于不同情况的 MMIO 屏障。
第二个链接还提到,传统 I/O 写入不需要后续读取,因为“I/O 端口空间保证写入事务在 CPU 继续之前到达 PCI 设备”。I/O 指令比 UC 访问提供更多的排序保证,但它们仍然没有完全序列化。这些保证包括在执行 I/O 指令之前等待先前的指令提交,以及在 I/O 指令完成之前不允许执行后面的指令。这些保证与 I/O 指令由 I/O 控制器转换为 PCIe I/O 事务(其中 I/O 写入事务是非发布事务)这一事实相结合,确保了当下一条指令执行时,它得到保证目标 I/O 设备上的 I/O 写入已完成。
I/O 设备可以使用零长度读取来确定较早的写入已在目标处完成。例如,I/O 设备可以通过这种方式确保写入操作已到达支持异步 DRAM 刷新 (ADR) 的平台上的持久域,或者确保写入操作已可由设备驱动程序观察到。