linux pci 之ari
Alternative Routing-ID Interpretation(ARI)
1. ARI 背景介绍
Alternative Routing-ID Interpretation,是pci规范中的一个属性,意思为可替换的Routing ID,意味着这是一种要把Routing ID的部分或全部替换掉的机制。
通常来讲,Requester ID和Comleter ID等Routing ID由Bus Number, Device Number, Function Number (BDF) 3个字段组成,其中Bus Number 8-bit,Device Number 5-bit,Function Number 3-bit,一共16-bit。
即最多支持256个Bus、32个Device及8个Function。
由于PCIe采用端到端的传输方式,每一个链路仅挂载一个设备,Device数量为1,Device Number恒为0,采用5-bit宽的Device Number没有意义;此外3-bit Function Number最高支持8个Function,对有多个VM的SR-IOV系统而言,8个Function稍显不足。
基于以上原因,从PCIe Gen3 Spec开始提供一种ARI机制,取消了Device Number字段,合并到Function Number字段中,这样Routing ID便变成了8-bit Bus Number + 8-bit Function Number的格式,最多支持的Bus数量不变,支持的Function数量增大到256个。
2. ARI 扩展能力结构
具备ARI能力的PCIe设备需实现ARI扩展能力结构。ARI扩展能力结构如下图图1所示,有ARI扩展能力头标、ARI控制寄存器、ARI能力寄存器三部分。
以下ARI扩展能力结构仅适用于Device,不适用于RP、Switch下行端口、RCiEP、RC事件收集器等。
图1 ARI Extended Capability
ARI Extended Capability Header: ARI扩展能力头标(图2),用以指示该设备具备ARI能力、ARI能力版本及下一能力的偏移。
ARI Capability Register:ARI能力寄存器(图3),MFVC Function Groups Capability用以指示具备MFVC能力的ARI设备是否支持Function Group粒度的仲裁,ACS Function Groups Capability用以指示具备ACS P2P出口控制的ARI设备是否支持Function Group粒度的访问,这两个字段仅用于Function0,其余Function该字段需置零。Next Function Number字段用以指示该Device中的下一Function Number,若没有则置零。
ARI Control Register:ARI控制寄存器(图4),用以开启MFVC Function Group Capability及ACS Function Group Capability,Function Group字段用以指示当前Function所属的Function Group Number。
linux pci驱动对ari的影响
配置某个设备的ari能力
void pci_configure_ari(struct pci_dev *dev)//caq:配置某个pci设备的ari的能力
{
u32 cap;
struct pci_dev *bridge;
if (pcie_ari_disabled || !pci_is_pcie(dev) || dev->devfn)//caq:全局是否关闭ari能力
return;
bridge = dev->bus->self;//caq:如果当前是bridge
if (!bridge)
return;
pcie_capability_read_dword(bridge, PCI_EXP_DEVCAP2, &cap);//caq:读取exp的cap
if (!(cap & PCI_EXP_DEVCAP2_ARI))//caq:查看是否具备ari能力
return;
if (pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ARI)) {
pcie_capability_set_word(bridge, PCI_EXP_DEVCTL2,
PCI_EXP_DEVCTL2_ARI);//caq:设置对应的寄存器
bridge->ari_enabled = 1;//caq:使能ari的记录
} else {
pcie_capability_clear_word(bridge, PCI_EXP_DEVCTL2,
PCI_EXP_DEVCTL2_ARI);
bridge->ari_enabled = 0;//caq:关闭ari
}
}
对于全局ari的配置开关:
static int __init pci_setup(char *str)//caq:pci 启动的参数解析
{
while (str) {
char *k = strchr(str, ',');
if (k)
*k++ = 0;
if (*str && (str = pcibios_setup(str)) && *str) {
if (!strcmp(str, "nomsi")) {
pci_no_msi();//caq:pci驱动要求nomsi
} else if (!strncmp(str, "noats", 5)) {
pr_info("PCIe: ATS is disabled\n");
pcie_ats_disabled = true;
} else if (!strcmp(str, "noaer")) {//caq:是否不需要支持aer
pci_no_aer();
} else if (!strcmp(str, "earlydump")) {//caq:是否开启earlydump
pci_early_dump = true;
} else if (!strncmp(str, "realloc=", 8)) {//caq:带 = 模式
pci_realloc_get_opt(str + 8);
} else if (!strncmp(str, "realloc", 7)) {//caq:空格模式
pci_realloc_get_opt("on");//caq:获取是on 还是off
} else if (!strcmp(str, "nodomains")) {
pci_no_domains();
**} else if (!strncmp(str, "noari", 5)) {//caq:是否关闭ari**
pcie_ari_disabled = true;
查看某个设备的ari是否开启:
static ssize_t ari_enabled_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct pci_dev *pci_dev = to_pci_dev(dev);
return sprintf(buf, "%u\n", pci_ari_enabled(pci_dev->bus));
}
static DEVICE_ATTR_RO(ari_enabled);//caq:只是read only的,查看状态
对sriov的影响:
static int sriov_init(struct pci_dev *dev, int pos)//caq:初始化一个pci_dev的sriov信息
{
int i, bar64;
int rc;
int nres;
u32 pgsz;
u16 ctrl, total;
struct pci_sriov *iov;
struct resource *res;
struct pci_dev *pdev;
pci_read_config_word(dev, pos + PCI_SRIOV_CTRL, &ctrl);//caq:sriov的寄存器
if (ctrl & PCI_SRIOV_CTRL_VFE) {//caq:vf是否enable
pci_write_config_word(dev, pos + PCI_SRIOV_CTRL, 0);//caq:先写0
ssleep(1);
}
ctrl = 0;
list_for_each_entry(pdev, &dev->bus->devices, bus_list)
if (pdev->is_physfn)//caq:查找bus内是pf的设备
goto found;
pdev = NULL;
if (pci_ari_enabled(dev->bus))//caq:如果dp归属的bus树都支持ari,并且全局开关打开
ctrl |= PCI_SRIOV_CTRL_ARI;//caq:其实就是bit4
found:
pci_write_config_word(dev, pos + PCI_SRIOV_CTRL, ctrl);//caq:写入ctrl,如果支持ari,则会写入在bit4
//caq:顺序上,一定是先写配置,特别是ari,不然offset不一样