linux pci 之ari

发布时间 2023-09-05 09:14:30作者: _备忘录

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不一样