1、MSI-X Capability
1.1、下图是MSI-X Capability的结构体,各字段含义如下:
Capability ID |
RO |
表示MSI-X Cap功能的ID号,其值为0x11 |
Next Pointer |
RO |
指向下一个Capability的地址 |
Message Control |
RO |
当前PCIe设备使用MSI-X机制进行中断请求的状态与控制信息,详情见下文。 |
Table BIR |
RO |
表示MSI-X Table所在的BAR空间。该字段由三位组成,其中0b000~0b101与BAR0~5空间一一对应 |
Table Offset |
RO |
表示MSI-X Table在相应BAR空间中的偏移。 |
PBA BIR |
RO |
表示Pending Table在PCIe设备的哪个BAR空间中。在通常情况下,Pending Table和MSI-X Table存放在PCIe设备的同一个BAR空间中。 |
PBA Offset |
RO |
表示Pending Table在相应BAR空间中的偏移 |
下图是Message Control的结构体和字段解释
Table Size |
RO |
用来存放MSI-X Table的大小 |
Function Mask |
RW |
中断请求的全局Mask位,默认值为0。如果为1,该设备所有的中断请求都将被屏蔽;如果为0,则由Per Vector Mask位,决定是否屏蔽相应的中断请求 |
MSI-X Enable |
RW |
MSI-X中断机制的使能位,复位值为0,表示不使能MSI-X中断机制。该位为1且MSI Enable位为0时,当前PCIe设备使用MSI-X中断机制,此时INTx和MSI中断机制被禁止。当PCIe设备的MSI Enble和MSI-X Enable位为0时,将使用INTx中断消息报文发出/结束中断请求。 |
1.2、从QEMU出发
qemu支持pci msi-x的模拟,接口如下:
/*
* Make PCI device @dev MSI-X capable
* @nentries is the max number of MSI-X vectors that the device support.
* @table_bar is the MemoryRegion that MSI-X table structure resides.
* @table_bar_nr is number of base address register corresponding to @table_bar.
* @table_offset indicates the offset that the MSI-X table structure starts with
* in @table_bar.
* @pba_bar is the MemoryRegion that the Pending Bit Array structure resides.
* @pba_bar_nr is number of base address register corresponding to @pba_bar.
* @pba_offset indicates the offset that the Pending Bit Array structure
* starts with in @pba_bar.
* Non-zero @cap_pos puts capability MSI-X at that offset in PCI config space.
* @errp is for returning errors.
*
* Return 0 on success; set @errp and return -errno on error:
* -ENOTSUP means lacking msi support for a msi-capable platform.
* -EINVAL means capability overlap, happens when @cap_pos is non-zero,
* also means a programming error, except device assignment, which can check
* if a real HW is broken.
*/
int msix_init(struct PCIDevice *dev, unsigned short nentries,
MemoryRegion *table_bar, uint8_t table_bar_nr,
unsigned table_offset, MemoryRegion *pba_bar,
uint8_t pba_bar_nr, unsigned pba_offset, uint8_t cap_pos,
Error **errp);
2、pci_alloc_irq_vectors()
接口如下:
int pci_alloc_irq_vectors(struct pci_dev *dev, unsigned int min_vecs, unsigned int max_vecs, unsigned int flags)
参数和return:
dev |
pci 设备 |
min_vecs |
设备对中断向量数目的最小要求,如果小于该值,会返回错误 |
max_vecs |
期望分配的中断向量最大个数 |
flags |
用于区分设备和驱动能够使用的中断类型,一般有4种 #define PCI_IRQ_LEGACY (1 << 0) /* Allow legacy interrupts */ #define PCI_IRQ_MSI (1 << 1) /* Allow MSI interrupts */ #define PCI_IRQ_MSIX (1 << 2) /* Allow MSI-X interrupts */ #define PCI_IRQ_ALL_TYPES (PCI_IRQ_LEGACY | PCI_IRQ_MSI | PCI_IRQ_MSIX) |
return |
返回分配到的向量数量 |
主要分析当flag 为__pci_enable_msix_range时的流程
pci_alloc_irq_vectors()
->pci_alloc_irq_vectors_affinity()
pci_alloc_irq_vectors_affinity()
->__pci_enable_msix_range()
->min_vecs > max_vecs ? max小于min 则直接返回
->dev->msix_enabled ? 如果已经enable,则直接返回(lspci 能看到MSI-X的使能状态)加载KO后就是使能状态,卸载KO后应该是非使能状态
->__pci_enable_msix()
__pci_enable_msix()
-> pci_msi_supported() 判断是否支持msi,即是否支持中断
-> pci_msix_vec_count()获取设备支持的MSI-X的entry的个数,从msix的message_control字段中获取支持个数(lspci看到的数量+ 1)
->msix_capability_init()
msix_capability_init()
->pci_msix_clear_and_set_ctrl()写message_control enable字段为1
->pci_read_config_word() 获取mis-x的数量
->msix_map_region()根据支持的entry个数,bar number,在bar中的偏移,进行ioremap,大小是entry 个数 * 16(sizeof(msix entry))
->msix_setup_entries()init msi table entry,设置entry的向量号,base地址等信息。链表形式放在dev管理dev->msi_list
->pci_msi_setup_msi_irqs() 使能mis 中断
->msi_domain_alloc_irqs() 以domain进行申请中断
->遍历dev->msi_list,__irq_domain_alloc_irqs()申请irq的描述符,一些资源
->遍历dev->msi_list,irq_domain_activate_irq()激活irq,这块是递归调用,使用domain->ops->activate;激活irp
->irq_chip_write_msi_msg() 调用data->chip->irq_write_msi_msg()
->pci_msi_domain_write_msg()完成msix tab的内容。
->msi_verify_entries()校验entry填充的是否符合规则(是否使用64bit和高32位地址是否使用)
->msix_program_entries() 上一步屏蔽中断,防止新中断进来,遍历entry,查看mask等信息
->populate_msi_sysfs() 写sysfs的msi_irqs信息
->pci_intx_for_msi()关闭INTx中断
->解除mis-x屏蔽