PCIe 的 MSI-X 中断,申请过程全流程分析,详细讲解 EP 空间中断能力 CAP 的串联

PCIe 的 MSI-X 中断

前言

  1. 什么是 MSI-X 中断
    MSI-X 中断与 MSI 中断类似,是 MSI 中断在使用中的升级优化版本。为了解决 MSI 中断的一些限制而优化。主要解决了如下限制:a. MSI 只支持 32 个中断,因为 MSI 相关的配置寄存器都在 EP 的配置空间里,这个空间有限。b. MSI 要求中断向量号必须连续。

MSI-X 初始化配置流程

  1. 找到 msix-cap void pci_msi_setup_pci_dev(struct pci_dev *dev) MSI-X 能力 ID :PCI_CAP_ID_MSIX 0x11 /* MSI-X */
    这个函数会去读 EP 的配置空间,从配置空间获取 capability。PCIE 定义了这么些 CAP
#define  PCI_CAP_ID_PM		0x01	/* Power Management */
#define  PCI_CAP_ID_AGP		0x02	/* Accelerated Graphics Port */
#define  PCI_CAP_ID_VPD		0x03	/* Vital Product Data */
#define  PCI_CAP_ID_SLOTID	0x04	/* Slot Identification */
#define  PCI_CAP_ID_MSI		0x05	/* Message Signalled Interrupts */
#define  PCI_CAP_ID_CHSWP	0x06	/* CompactPCI HotSwap */
#define  PCI_CAP_ID_PCIX	0x07	/* PCI-X */
#define  PCI_CAP_ID_HT		0x08	/* HyperTransport */
#define  PCI_CAP_ID_VNDR	0x09	/* Vendor-Specific */
#define  PCI_CAP_ID_DBG		0x0A	/* Debug port */
#define  PCI_CAP_ID_CCRC	0x0B	/* CompactPCI Central Resource Control */
#define  PCI_CAP_ID_SHPC	0x0C	/* PCI Standard Hot-Plug Controller */
#define  PCI_CAP_ID_SSVID	0x0D	/* Bridge subsystem vendor/device ID */
#define  PCI_CAP_ID_AGP3	0x0E	/* AGP Target PCI-PCI bridge */
#define  PCI_CAP_ID_SECDEV	0x0F	/* Secure Device */
#define  PCI_CAP_ID_EXP		0x10	/* PCI Express */
#define  PCI_CAP_ID_MSIX	0x11	/* MSI-X */
#define  PCI_CAP_ID_SATA	0x12	/* SATA Data/Index Conf. */
#define  PCI_CAP_ID_AF		0x13	/* PCI Advanced Features */
#define  PCI_CAP_ID_EA		0x14	/* PCI Enhanced Allocation */
+-----------------+                +----------------------+
|offset=0x34      |                |          ||          |
|   CAP-Pointer   +---------------->  CAP-Pr  ||  CPA-ID  |
|                 |                |          ||          |
+-----------------+                +----+-----------------+
                                        |
                                        |
                                   +----v-----------------+
                                   |          ||          |
                                   >  CAP-Pr  ||  CPA-ID  |
                                   |          ||          |
                                   +----+-----------------+
                                        |
                                        |
                                   +----v-----------------+
                                   |          ||          |
                                   >  CAP-Pr  ||  CPA-ID  |
                                   |          ||          |
                                   +----------------------+

规范中规定 EP 的配置空间 0x34 的地址存放了第一个 CAP 的地址偏移,后续的 CAP 都如此串成链,像一个链表,有 next 指针,有携带的值(CAP-ID)。CAP ID标识该地址存放的 CAP 类型。
题外话:解释 MSI 要求中断向量连续?PCIe 规范里并没有明确规定。
2. 分析 MSI-X cap
来自PCIe规范
主要分析 Table BIR,这个寄存器是控制将 MSI-X Table Structure 存放在 BAR 内存空间的位置的。Table BIR 有 3Bit,指示 MSI-X Table Structure 将映射到 BARn 空间的某一个,Table Offset 指示BARn基地址的偏移。PBA 的使用方法相同。然后在 CPU 的对应 RAM 空间来初始化如下结构,即完成了 MSI-X 的结构初始化。
PCIe MSI-X 结构体
3. 代码分析

  • 读取 MSI CAP 能力
pci_device_add(struct pci_dev *dev, struct pci_bus *bus)
	> pci_init_capabilities(dev);
		> pci_msi_setup_pci_dev(dev);
			> dev->msi_cap = pci_find_capability(dev, PCI_CAP_ID_MSI);
			> dev->msix_cap = pci_find_capability(dev, PCI_CAP_ID_MSIX);
  • 初始化 MSI-X Table Structure
    初始化调用链
static int msix_capability_init(struct pci_dev *dev, struct msix_entry *entries,
				int nvec, struct irq_affinity *affd)
{
	void __iomem *base;
	int ret, tsize;
	u16 control;

	/*
	 * Some devices require MSI-X to be enabled before the MSI-X
	 * registers can be accessed.  Mask all the vectors to prevent
	 * interrupts coming in before they're fully set up.
	 */
	pci_msix_clear_and_set_ctrl(dev, 0, PCI_MSIX_FLAGS_MASKALL |
				    PCI_MSIX_FLAGS_ENABLE);

	pci_read_config_word(dev, dev->msix_cap + PCI_MSIX_FLAGS, &control);

	/* Request & Map MSI-X table region */
	tsize = msix_table_size(control);
	base = msix_map_region(dev, tsize);
	if (!base) {
		ret = -ENOMEM;
		goto out_disable;
	}


	ret = msix_setup_entries(dev, base, entries, nvec, affd);
	if (ret)
		goto out_disable;

	ret = pci_msi_setup_msi_irqs(dev, nvec, PCI_CAP_ID_MSIX);
	if (ret)
		goto out_avail;

	/* Check if all MSI entries honor device restrictions */
	ret = msi_verify_entries(dev);
	if (ret)
		goto out_free;

	msix_update_entries(dev, entries);

	ret = populate_msi_sysfs(dev);
	if (ret)
		goto out_free;

	/* Set MSI-X enabled bits and unmask the function */
	pci_intx_for_msi(dev, 0);
	dev->msix_enabled = 1;

	/*
	 * Ensure that all table entries are masked to prevent
	 * stale entries from firing in a crash kernel.
	 *
	 * Done late to deal with a broken Marvell NVME device
	 * which takes the MSI-X mask bits into account even
	 * when MSI-X is disabled, which prevents MSI delivery.
	 */
	msix_mask_all(base, tsize);
	pci_msix_clear_and_set_ctrl(dev, PCI_MSIX_FLAGS_MASKALL, 0);

	pcibios_free_irq(dev);
	return 0;

out_avail:
	if (ret < 0) {
		/*
		 * If we had some success, report the number of IRQs
		 * we succeeded in setting up.
		 */
		struct msi_desc *entry;
		int avail = 0;

		for_each_pci_msi_entry(entry, dev) {
			if (entry->irq != 0)
				avail++;
		}
		if (avail != 0)
			ret = avail;
	}

out_free:
	free_msi_irqs(dev);

out_disable:
	pci_msix_clear_and_set_ctrl(dev, PCI_MSIX_FLAGS_MASKALL | PCI_MSIX_FLAGS_ENABLE, 0);

	return ret;
}

分析代码:

  1. msix_map_region(struct pci_dev *dev, unsigned nr_entries) 这个函数是映射 Table Offset + Table BIR 指定的地址。
  2. msix_setup_entries(dev, base, entries, nvec, affd); 这个函数是初始化内存结构的 struct msi_desc *entry,将其初始化完成。
  3. pci_msi_setup_msi_irqs(struct pci_dev *dev, int nvec, int type) 这个函数是配置 IRQ,最后会调用到 __msi_domain_alloc_irqs(struct irq_domain *domain, struct device *dev, int nvec), 这个函数会 prepare_irqs,alloc_irqs, 最后 activate_irq。此时中断已经生效了,中断使能已经写入了 EP 设备了。
  4. msix_update_entries(dev, entries); 更新获取的 virq
  5. msix_mask_all(base, tsize); 初始化完成之后屏蔽所有的 MSI-X 中断。

其他:MSI irq_domain 是如何获取 ITS 中断的

  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值