本文基于linux 5.7.0, 平台是arm64
在平时设备的使用过程中,有可能遇到过数据通信错误(malformed tlp), 或者网卡/磁盘在进行数据读写时性能没有达到预期,这些都可能和pcie 的max payload size配置有关系。

1. 概述

1.1 什么是max payload size

我们都知道,PCIe设备是以TLP的形式发送报文的,而max payload size(简称mps)决定了pcie设备实际使用的tlp能够传输的最大字节数。mps的大小是由PCIe链路两端的设备协商决定的,PCIe设备发送TLP时,其最大payload不能超过mps的值。
mps定义在Device control register中。
Device Control register:

同样定义在该寄存器中还有一个Max read request size(简称mrrs)配置,它表示每一个读请求所能够读到的最大字节数。当PCIe设备发送memory读请求TLP时,该TLP所请求的数据的大小不能超过mrrs的值。

1.2 max payload size的作用

pice协议规定有数据的TLP包的传递规则是:按照指定DW长度单位传递数据,发送端的数据承载量不得超过“Device Control Register”中的“Max_Payload_Size”数值,接收端中,所接收到的数据量也不能超过接收端“Device Control Register”中的“Max_Payload_Size”数值。
所以mps主要的作用是:
(1) mps的大小影响pcie设备的传输效率。 对于比较大的数据量,如果mps设置比较小,那么数据只能被分割成多个TLP进行发送,势必会影响pcie链路带宽的利用率。但是mps的值也不是越大越好,一方面mps设置的越大,硬件处理数据包所需的内存和逻辑量也随之增加; 另一方面,mps的值是一个自协商的结果,当前市场上支持较大mps值的ep设备不常见,所以没有必要设置本端的mps值太大。

(2) 不合理的mps设置会导致数据通信时上报"Malformed TLP"错误。 RC设备在与EP设备对接时,对端的EP设备的MPS可能各不相同,需要RC端去适配EP端的mps, 如果EP设备接收的TLP length字段超过了它的mps配置,该设备就会认为该TLP非法。

1.3 mrrs 对mps的影响

mrrs和mps并没有直接的联系,但是有时候mrrs会对mps有影响。
举个例子:一个典型的pcie拓扑结构, 中间的红色数字mps的值。
假设发起一个memory tlp读请求操作:

根据上文的分析,这种情况下,RC和EP的通信会失败,因为他们的mps的值不相等。
但是如果设置各个节点的mrrs的值为128B,那么虽然RC设备的mps为256B,但是实际能够传输的大小却受到mrrs的影响,变成128B,这种情况下,实际上传输的TLP报文最终是128字节,可以满足各节点mps一致的条件,可以传输成功。

总结: mrrs的值不能设置的太小,一方面,如果mrrs设置太小,会影响实际TLP的mps。另一方面,如果我们设定MRRS为128B,我们读64KB数据时,就需要发送512(64KB/128B=512)次读请求,这样的话,就增加了很多额外的开销,对数据传输速率也是一种伤害,所以,为了提高特别是大Block Size data的传输效率,尽量把mrrs设的大一点,用更少的次数传递更多的数据。但是mrrs也不能设置过大,在PCIe链路中,我们不能一直发送TLP,而是需要接受端有足够的接受空间之后,才能继续发送TLP,buffer空间太大的话对于EP的设备而言是比较苛刻的,所以在实际的设计中,也会对mrrs的大小进行限制。

2. 设备MPS的查询和配置

2.1 使用lspci 查询mps


[root@localhost linux]# lspci  -s 00:18.0 -vv
00:18.0 PCI bridge: VMware PCI Express Root Port (rev 01) (prog-if 00 [Normal decode])Control: I/O+ Mem+ BusMaster+ SpecCycle- MemWINV- VGASnoop- ParErr- Stepping- SERR- FastB2B- DisINTx-Status: Cap+ 66MHz- UDF- FastB2B- ParErr- DEVSEL=fast >TAbort- <TAbort- <MAbort- >SERR- <PERR- INTx-Latency: 0, Cache Line Size: 32 bytesInterrupt: pin ? routed to IRQ 48Bus: primary=00, secondary=1b, subordinate=1b, sec-latency=0I/O behind bridge: 00007000-00007fffMemory behind bridge: fd100000-fd1fffffPrefetchable memory behind bridge: 00000000e7700000-00000000e77fffffSecondary status: 66MHz- FastB2B- ParErr- DEVSEL=fast >TAbort- <TAbort- <MAbort- <SERR- <PERR-BridgeCtl: Parity- SERR- NoISA+ VGA- MAbort- >Reset- FastB2B-PriDiscTmr- SecDiscTmr- DiscTmrStat- DiscTmrSERREn-Capabilities: [40] Subsystem: VMware PCI Express Root PortCapabilities: [48] Power Management version 3Flags: PMEClk- DSI- D1- D2- AuxCurrent=0mA PME(D0+,D1-,D2-,D3hot+,D3cold+)Status: D0 NoSoftRst- PME-Enable- DSel=0 DScale=0 PME-Capabilities: [50] Express (v2) Root Port (Slot+), MSI 00DevCap:    MaxPayload 128 bytes, PhantFunc 0ExtTag- RBE-DevCtl:    Report errors: Correctable- Non-Fatal- Fatal- Unsupported-RlxdOrd- ExtTag- PhantFunc- AuxPwr- NoSnoop-MaxPayload 128 bytes, MaxReadReq 128 bytes

查出来mps的大小为128 byte, mrrs的大小为 128byte。

2.2 使用setpci进行设置

在之前的博客 【PCIe学习笔记之pcie结构和配置空间】介绍过怎么用lspci和setpci得到配置空间的值。

Device Control寄存器位于PCI express capability结构里,对应的capability id 为0x10(#define PCI_CAP_ID_EXP 0x10 /* PCI Express */)。

[root@localhost linux]# lspci -s 00:18.0 -xxx
00:18.0 PCI bridge: VMware PCI Express Root Port (rev 01)
00: ad 15 a0 07 07 00 10 00 01 00 04 06 08 00 81 00
10: 00 00 00 00 00 00 00 00 00 1b 1b 00 70 70 00 00
20: 10 fd 10 fd 71 e7 71 e7 00 00 00 00 00 00 00 00

查看lspci结果, 可以看出偏移0x20的位置对应的capability id为0x10, 所以device control register的偏移是0x20 + 0x8。
查看device control register的值:

[root@localhost linux]# setpci -s 00:18.0 0x28.W
1b00

或者使用CAP_EXP(对应的是pcie express capability的偏移)

[root@localhost linux]# setpci -s 00:18.0 CAP_EXP+08.W
0000

可以看出device control register的值为0, 该寄存器【14:12】是mrrs, [7:5]是mps, 所以mps和mrrs都是0, 对应的正好是128B。

bit[2:0] max payload size or max read request size
000 128B
001 256B
010 512B
011 1024B
100 2048B
101 4096B

设置mps和mrrs的大小为256B,即【14:12】=0x1, [7:5]=0x1

setpci -s 00:18.0 CAP_EXP+08.W=0x2020
[root@localhost linux]# lspci -s 00:18.0 -vvv
00:18.0 PCI bridge: VMware PCI Express Root Port (rev 01) (prog-if 00 [Normal decode])Control: I/O+ Mem+ BusMaster+ SpecCycle- MemWINV- VGASnoop- ParErr- Stepping- SERR- FastB2B- Capabilities: [50] Express (v2) Root Port (Slot+), MSI 00DevCap: MaxPayload 256 bytes, PhantFunc 0ExtTag- RBE-DevCtl:    Report errors: Correctable- Non-Fatal- Fatal- Unsupported-RlxdOrd- ExtTag- PhantFunc- AuxPwr- NoSnoop-MaxPayload 256 bytes, MaxReadReq 256 bytes

mps和mrrs的值成功设置为256byte. (有时候设置会不生效,这个也是和硬件支持的能力相关联)

3. linux内核实现

3.1 mps的配置类型

在linux内核中,当前有四种配置mps的类型,定义在include/linux/pci.h中

enum pcie_bus_config_types {PCIE_BUS_TUNE_OFF,      /* Don't touch MPS at all */PCIE_BUS_SAFE,          /* Use largest MPS boot-time devices support */PCIE_BUS_PERFORMANCE,   /* Use MPS and MRRS for best performance */PCIE_BUS_PEER2PEER,     /* Set MPS = 128 for all devices */
};

PCIE_BUS_TUNE_OFF: 不对PCIe MPS(Max Payload Size)进行调整,而是使用BIOS配置好的默认值。
PCIE_BUS_SAFE: 将每个设备的MPS都设为root complex下所有设备支持的MPS中的最大值。 指的是设置最小的那个设备的mps为所有设备的mps。
PCIE_BUS_PERFORMANCE: 将设备的MPS设为其上级总线允许的最大MPS,同时将MRRS(Max Read Request Size)设为能支持的最大值(但不能大于设备或总线所支持的MPS值)
PCIE_BUS_PEER2PEER: 所有的设备的MPS都设置为128B,以确保支持所有设备之间的点对点DMA,同时也能保证热插入(hot-added)设备能够正常工作,但代价是可能会造成性能损失。

3.2 os更改mps的配置类型

内核在启动的cmdline中可以配置pci_bus_config,如:

pci=pcie_bus_perf

3.3 代码实现

结合代码分析linux中mps的配置, 以及看mps是怎么生效的:

void pcie_bus_configure_settings(struct pci_bus *bus)
{u8 smpss = 0;if (!bus->self)return;if (!pci_is_pcie(bus->self))return;/** FIXME - Peer to peer DMA is possible, though the endpoint would need* to be aware of the MPS of the destination.  To work around this,* simply force the MPS of the entire system to the smallest possible.*/if (pcie_bus_config == PCIE_BUS_PEER2PEER)           -------  (1)smpss = 0;if (pcie_bus_config == PCIE_BUS_SAFE) {smpss = bus->self->pcie_mpss;pcie_find_smpss(bus->self, &smpss);            ------- (2)pci_walk_bus(bus, pcie_find_smpss, &smpss);         }pcie_bus_configure_set(bus->self, &smpss);              -------- (3)pci_walk_bus(bus, pcie_bus_configure_set, &smpss);
}

(1) 当pcie_bus_config是peer2peer时,设置smpss的值为0, 最终通过pcie_write_mps(dev, 128 << smpss)写到配置空间里,所以peer2peer的配置下,mps被固定128B。

(2) 当pcie_bus_config是safe时, pcie_find_smpss()会被调用,该函数的作用是寻找一个最小的mps。

static int pcie_find_smpss(struct pci_dev *dev, void *data)
{u8 *smpss = data;if (*smpss > dev->pcie_mpss)*smpss = dev->pcie_mpss;return 0;
}

(3) pcie_bus_configure_set会将mps设置写进配置空间, 会对pci_bus_perf做特殊处理,
在pci_bus_perf配置下: 会对每一个非根节点取本身和父节点的mps的较小值作为自己的mps

static void pcie_write_mps(struct pci_dev *dev, int mps)
{int rc;if (pcie_bus_config == PCIE_BUS_PERFORMANCE) {mps = 128 << dev->pcie_mpss;if (pci_pcie_type(dev) != PCI_EXP_TYPE_ROOT_PORT &&dev->bus->self)/** For "Performance", the assumption is made that* downstream communication will never be larger than* the MRRS.  So, the MPS only needs to be configured* for the upstream communication.  This being the case,* walk from the top down and set the MPS of the child* to that of the parent bus.** Configure the device MPS with the smaller of the* device MPSS or the bridge MPS (which is assumed to be* properly configured at this point to the largest* allowable MPS based on its parent bus).*/mps = min(mps, pcie_get_mps(dev->bus->self)); }rc = pcie_set_mps(dev, mps);if (rc)pci_err(dev, "Failed attempting to set the MPS\n");
}

同时pci_bus_perf配置下也会对pcie_write_mrrs()进行调用, 将MRRS设为各个节点能支持的最大值(但不能大于设备或总线所支持的MPS值).

总结

结合Document和代码分析,最终我们可以得出mps的配置规则如下图所示:

绿色对应的是pci_bus_no_tune, 什么都没改。
橙色的对应的是pci_bus_safe, 所有的设备的mps改为最小的那个设备的mps值,即所有的设备的mps设置为256
黄色的对应的pci_bus_perf, 除了根节点以外,比较自己设备的mps和父节点的mps, 选择较小的那一个设为自己的mps, 所以除了rc的mps, 其他节点的mps都设置为256, 此时,还需要更新各个节点mrrs的大小,保证mrrs的值不大于自身的mps的值,否则会出现1.3章节介绍的问题。
蓝色的对应的是pci_bus_peer2peer, 保证DMA的通信,所有设备的mps设为128.

参考资料

PCIe 体系结构导读

PCIe学习笔记之Max payload size相关推荐

  1. PCIE学习笔记(四)Xilinx FPGA PCI Express 硬核配置

    目录 2.3.2 配置核IP生成 2.3.2.1 Base模式 2.3.2.2 Advanced模式 本文主要介绍Xilinx 7系列FPGA的 IP 核配置与应用,使用的工具版本为VIVADO 20 ...

  2. PCIe学习笔记之MSI/MSI-x中断及代码分析

    本文基于linux 5.7.0, 平台是arm64 1. MSI/MSI-X概述 PCIe有三种中断,分别为INTx中断,MSI中断,MSI-X中断,其中INTx是可选的,MSI/MSI-X是必须实现 ...

  3. 【UEFI】PCIE学习笔记

    本文是本人学习记录,不保证准确,如有错误请指出.如果侵犯请联系删除. PCIE一共支持256条bus(8个bit),32个device(5个bit),8个function(3个bit), 假设负载全满 ...

  4. PCIe学习笔记之pcie初始化枚举和资源分配流程代码分析

    本文主要是对PCIe的初始化枚举.资源分配流程进行分析.代码对应的是linux 4.19, 平台是arm64. 文章首发于这里 1. PCIe architecture 1.1 pcie的拓扑结构 在 ...

  5. PCIe学习笔记之pcie结构和配置空间

    PCIe概述 PCI Express,是计算机总线PCI的一种,它沿用现有的PCI编程概念及通信标准,但建基于更快的串行通信系统. PCIE总线使用的是高速差分总线,并采用端到端的连接方式, 现在的高 ...

  6. PCIe学习笔记(15)--- TLP的ROUTING方式

    PCIE是POINT TO POINT的,不像PCI,是SHARED-BUS,总线上的数据,是被所有EP DEV看到的. 这一点与USB2.0比较类似,是广播方式的(BROADCASTING) USB ...

  7. DWC PCIE学习笔记(一)-----PCIE PHY接口

    (以下都是PCIE2 PHY的各种问题) 一.PIPE接口 1.PIPE接口用于连接PCIE controller和PCIE PHY, controller用PIPE接口发送并行数给PHY用于并串转换 ...

  8. PCIe学习笔记(一)-------1.5 一个TLP包的传输过程

    1,MRd包的传输 以MRd TLP包的传输为例,说明一个Non-Posted事务的TLP是如何在PCIe系统层次中处理的. 1)Requester的软件层发送需要传送的信息给事务层. 2)事务层将数 ...

  9. PCIe学习笔记(一)-------1.3 PCIe数据包(TLP,DLLP,PLP)

    目录 1,PCIe的层次结构 2,PCIe数据包 2.1,TLP包 2.2,DLLP包 2.3,PLP包 1,PCIe的层次结构 PICe设备的体系结构可以分为三个层次:事务层(Transaction ...

最新文章

  1. 调试JDK源码-一步一步看HashMap怎么Hash和扩容
  2. Spring Boot 实现通用 Auth 认证的 4 种方式
  3. jenkins插件findbugs+pmd+checkstyle结合sonar与maven(java环境代码质量和代码规范管理)...
  4. Oracle 12C 新特性之级联truncate
  5. cacti 网络流量监测图形分析工具
  6. ABP vNext微服务架构详细教程——身份管理服务
  7. 【算法系列之十三】二叉树两叶节点的最大距离
  8. 【Elasticsearch】 Elasticsearch中数据是如何存储的
  9. 扩展JS格式化(Format)功能及评论树
  10. 服务器显示AL024是什么意思,焦作台达ASD-A2-0241-M伺服驱动器出现报警代码AL024怎么维修...
  11. python源代码文件加密
  12. shark恒破解笔记2-绕过自校验
  13. Hello Guice
  14. 计算机网络——集线器和交换机
  15. VB中MID如何使用
  16. 视频gif如何制作?试试这个视频制作gif神器
  17. 贪吃蛇大作战中的“马太效应”
  18. 【C++ 】STL求全排列和组合
  19. [625]Git +TortoiseGit安装配置详细步骤
  20. python抓取小红书_小红书很难爬?最新爬取方法教给你啦~

热门文章

  1. iptables 创建新链
  2. U-Boot 顶层 Makefile 分析二
  3. localtime、localtime_s
  4. 沈坤荣《宏观经济学教程》第3版课后答案
  5. 如何实现触摸屏与多台PLC之间无线Profinet通信?
  6. C语言中生成随机数函数
  7. CAJ文献格式转PDF格式(通过CAJViewer)
  8. 亚马逊激光产品通告,亚马逊激光安全认证
  9. 给大家分享一篇Python开发:MySQL(一)
  10. 2、PHP环境搭建(推荐宝塔面板)