PCIe Linux MRRS和MPS参数设置策略

1.概述

MPS(Max Payload Size)和MRRS(Max Read Request Size)共同影响PCIe总线的传输效率。如果MPS和MRRS设置的过小,传输相同长度的数据,需要更多的TLP报文,导致PCIe总线传输效率降低,如果MPS和MRRS设置的过大,超出系统中某些设备的允许值,导致这些设备无法处理TLP报文,然后上报Malformed TLP错误。因此合理的设置MPS和MRRS非常重要,内核提供了5种策略,可以根据不同的场景进行选择。MPS和MRRS的意义参考PCIe总线-MPS MRRS RCB参数介绍(四)

2.策略

2.1.策略定义

内核定义的5种配置MPS和MRRS的策略如下所示。

[include/linux/pci.h]
enum pcie_bus_config_types {
    PCIE_BUS_TUNE_OFF,        /* Don't touch MPS at all */
    PCIE_BUS_DEFAULT,         /* Ensure MPS matches upstream bridge */
    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 */
};

每个策略的行为如下,具体的设置流程参考后面的代码分析小结。

  1. PCIE_BUS_TUNE_OFF
    所有的PCIe设备都使用硬件默认的MPS和MRRS,内核不会修改。
  2. PCIE_BUS_DEFAULT
    内核会修改设备的MPS,使设备和设备上游桥设置的MPS保持一致(如果上游桥为Root Port,且设置的MPS大于设备支持的最大MPS,则将设备支持的最大MPS设置到Root Port中)。MRRS使用设备硬件的默认值,内核不会修改。
  3. PCIE_BUS_SAFE
    使用总线上所有设备支持的最大MPS中最小的MPS。MRRS使用设备硬件的默认值,内核不会修改。
  4. PCIE_BUS_PERFORMANCE
    比较设备上游桥设置的MPS和设备支持的最大MPS,使用较小的MPS(如果上游桥为Root Port,则将MPS设置为桥支持的最大MPS)。MRRS和MPS设置相同。
  5. PCIE_BUS_PEER2PEER
    所有设备的MPS都设置为128。MRRS使用设备硬件的默认值,内核不会修改。

2.2.策略配置

内核中定义了pcie_bus_config变量,用于保存MPS和MRRS的设置策略。如下所示,pcie_bus_config的值由配置选项决定,配置选项有两种方式决定,一个是内核menuconfig,另一种是通过command-line参数传入。

[drivers/pci/pci.c]
/* PCIe MPS/MRRS strategy; can be overridden by kernel command-line param */
#ifdef CONFIG_PCIE_BUS_TUNE_OFF
enum pcie_bus_config_types pcie_bus_config = PCIE_BUS_TUNE_OFF;
#elif defined CONFIG_PCIE_BUS_SAFE
enum pcie_bus_config_types pcie_bus_config = PCIE_BUS_SAFE;
#elif defined CONFIG_PCIE_BUS_PERFORMANCE
enum pcie_bus_config_types pcie_bus_config = PCIE_BUS_PERFORMANCE;
#elif defined CONFIG_PCIE_BUS_PEER2PEER
enum pcie_bus_config_types pcie_bus_config = PCIE_BUS_PEER2PEER;
#else
enum pcie_bus_config_types pcie_bus_config = PCIE_BUS_DEFAULT;
#endif

如下图所示,MPS和MRRS的设置策略,内核menuconfig配置选项如下图所示。

MPS和MRRS配置选项
如下图所示,内核MPS和MRRS的menuconfig选项需要先使能CONFIG_EXPERT选项,否则不会出现在menuconfig选项中。

MPS和MRRS策略依赖选项
内核MPS和MRRS策略的command-line参数如下所示:

pci=pcie_bus_tune_off
pci=pcie_bus_safe
pci=pcie_bus_perf
pci=pcie_bus_peer2peer

3.设置流程

如下图所示,内核中设置MPS/MRRS分为两步,第一步是在扫描单个设备的时候,先读取设备的配置空间,获取设备支持的最大MPS,然后调用pci_configure_mps函数设置设备的MPS,第二步是在内核扫描完总线上所有PCIe设备之后,根据策略,遍历总线,调用pcie_bus_configure_settings设置设备的MPS和MRRS。

MPS和MRRS内核配置流程

3.1.第一步

设备支持的最大MPS通过配置空间的Device Capabilities Register获取,并保存到pci_devpcie_mpss成员中。pci_configure_mps函数的主要作用是实现PCIE_BUS_TUNE_OFF策略,将设备和设备上游桥的MPS设置相同,具体的流程如下:

  1. 当设备是RC下面接的内部EP(PCI_EXP_TYPE_RC_END)时。MPS/MRRS的配置策略为PCIE_BUS_PEER2PEER,则MPS固定设置为128,如果是其他策略,则MPS设置为设备支持的最大值。
  2. 获取当前设备和设备上游桥设置的MPS,当两者的MPS相等时,则直接返回,不设置。
  3. 当MPS/MRRS的配置策略为PCIE_BUS_TUNE_OFFPCIE_BUS_PEER2PEERPCIE_BUS_SAFEPCIE_BUS_PERFORMANCE时直接返回,等待整个PCIe总线枚举完成后再进行设置(PCIE_BUS_TUNE_OFF除外)。
  4. 当MPS/MRRS的配置策略为PCIE_BUS_DEFAULT,如果设备支持的最大MPS小于设备上游桥设置的MPS,且设备上游桥为Root Port(PCI_EXP_TYPE_ROOT_PORT),则将设备上游桥的MPS设置为设备支持的最大MPS。
  5. 将设备上游桥的MPS设置的设备当中。
[drivers/pci/probe.c]
static void pci_configure_mps(struct pci_dev *dev)
{
    ......
    /* 1. PCI_EXP_TYPE_RC_END类型的设备,PCIE_BUS_PEER2PEER策略固定设置为128,
     * 其他策略固定设置为支持的最大值
     */
    if (pci_pcie_type(dev) == PCI_EXP_TYPE_RC_END) {
        if (pcie_bus_config == PCIE_BUS_PEER2PEER)
            mps = 128;
        else
            mps = 128 << dev->pcie_mpss;
        rc = pcie_set_mps(dev, mps);
        ......
    }
    /* 2. 当设备和设备上游桥设置的MPS相等时,则直接返回 */
    mps = pcie_get_mps(dev);
    p_mps = pcie_get_mps(bridge);
    if (mps == p_mps)
        return;

    /* 3. 非PCIE_BUS_DEFAULT策略直接返回,不设置 */
    if (pcie_bus_config == PCIE_BUS_TUNE_OFF) {
        ......
        return;
    }
    if (pcie_bus_config != PCIE_BUS_DEFAULT)
        return;
    /* 4. 修正Root Port的MPS */
    mpss = 128 << dev->pcie_mpss;
    if (mpss < p_mps && pci_pcie_type(bridge) == PCI_EXP_TYPE_ROOT_PORT) {
        pcie_set_mps(bridge, mpss);
        ......
        p_mps = pcie_get_mps(bridge);
    }
    /* 5. 将设备上游桥的MPS设置的设备当中 */
    rc = pcie_set_mps(dev, p_mps);
    ......
}

3.2.第二步

当整个PCIe总线枚举完成之后,内核会遍历Host bridge子总线,调用pcie_bus_configure_settings函数设置MPS和MRRS。对于PCIE_BUS_TUNE_OFFPCIE_BUS_DEFAULT策略,则直接跳过,不设置。
pcie_bus_configure_settings的执行流程如下所示:

  1. 当MPS/MRRS的配置策略为PCIE_BUS_PEER2PEER,则MPS固定设置为128。
  2. 当MPS/MRRS的配置策略为PCIE_BUS_SAFE,如果设备是支持热插拔的桥,则MPS为128,否则设置为这条子总线上设备支持的最大MPS中最小的一个(子总线从该设备上游桥开始)。
  3. 设置当前设备和其下游设备的MPS、MRRS。
[drivers/pci/probe.c]
void pcie_bus_configure_settings(struct pci_bus *bus)
{
    u8 smpss = 0;
    ......
    /* 1. MPS固定为128 */
    if (pcie_bus_config == PCIE_BUS_PEER2PEER)
        smpss = 0;
    /* 2. 如果设备是支持热插拔的桥,则MPS为128,否则设置为这条子总线上
     * 设备支持的最大MPS中最小的一个
     */
    if (pcie_bus_config == PCIE_BUS_SAFE) {
        smpss = bus->self->pcie_mpss;
        pcie_find_smpss(bus->self, &smpss);
        pci_walk_bus(bus, pcie_find_smpss, &smpss);
    }
    /* 3. 设置当前设备和其下游设备的MPS、MRRS */
    pcie_bus_configure_set(bus->self, &smpss);
    pci_walk_bus(bus, pcie_bus_configure_set, &smpss);
}

pcie_bus_configure_set函数如下所示,对于MPS/MRRS的配置策略为PCIE_BUS_TUNE_OFFPCIE_BUS_DEFAULT,直接返回,不设置MPS和MRRS,而对于其他策略,调用pcie_write_mps设置MPS,调用pcie_write_mrrs设置MRRS。

[drivers/pci/probe.c]
static int pcie_bus_configure_set(struct pci_dev *dev, void *data)
{
    ......
    if (pcie_bus_config == PCIE_BUS_TUNE_OFF ||
        pcie_bus_config == PCIE_BUS_DEFAULT)
            return 0;

    mps = 128 << *(u8 *)data;
    orig_mps = pcie_get_mps(dev);

    pcie_write_mps(dev, mps); // 设置MPS
    pcie_write_mrrs(dev);     // 设置MRRS
    ......
}

pcie_write_mps函数如下所示,对于MPS/MRRS的配置策略为PCIE_BUS_PERFORMANCE,如果设备是Root Port(PCI_EXP_TYPE_ROOT_PORT)则MPS为其支持的最大MPS,如果是其他设备,则取设备和设备上游桥支持的最大MPS中最小的一个。最后调用pcie_set_mps函数将MPS设置到配置空间的Device Control Register中。

[drivers/pci/probe.c]
static void pcie_write_mps(struct pci_dev *dev, int mps)
{
    ......
    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)
                mps = min(mps, pcie_get_mps(dev->bus->self));
    }

    rc = pcie_set_mps(dev, mps);
    ......
}

pcie_write_mrrs函数如下所示,只有MPS/MRRS的设置策略为PCIE_BUS_PERFORMANCE才会设置MRRS,其他策略使用设备默认的MRRS。在性能模式下,MRRS应该设置为设备支持的最大值,但不能超过设备设置的MPS,因此先读取设备的MPS,然后将读取的值作为MRRS值设置到配置空间的Device Control Register中,如果写入的MRRS值超过了设备支持的最大值,则减半继续设置。

[drivers/pci/probe.c]
static void pcie_write_mrrs(struct pci_dev *dev)
{
    // 只有PCIE_BUS_PERFORMANCE策略才会配置MRRS
    if (pcie_bus_config != PCIE_BUS_PERFORMANCE)
        return;
    mrrs = pcie_get_mps(dev);  // 获取设备配置的MPS
    // 设置MRRS,和设备的MPS相等
    while (mrrs != pcie_get_readrq(dev) && mrrs >= 128) {
        rc = pcie_set_readrq(dev, mrrs);
        if (!rc)
            break;
        mrrs /= 2;
    }
}

4.设置接口

除了内核会根据选择的策略设置MPS和MRRS之外,驱动也可以调用内核提供的接口主动设置MPS和MRRS。内核定义的接口如下所示:

[include/linux/pci.h]
int pcie_get_readrq(struct pci_dev *dev);         // 获取设置的MRRS
int pcie_set_readrq(struct pci_dev *dev, int rq); // 设置MRRS
int pcie_get_mps(struct pci_dev *dev);            // 获取设置的MPS
int pcie_set_mps(struct pci_dev *dev, int mps);   // 设置MPS

参考资料

  1. PCI Express® Base Specification Revision 5.0 Version 1.0
  2. Linux Kernel 5.10
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值