1、MSI-X中断实现
目前PCIE设备主要通过MSI-X机制向CPU发送中断,实现步骤如下:
a) CPU初始化的时候会给PCIE设备分配中断号,并且根据分配好的中断号生成MSI的address和data信息;
b)然后把address和data信息写入PCIE设备 ;
c) 后期如果PCIE设备想发送中断,则把data的值写入address就可以了
2、查找virtio虚拟网卡的中断信息
a、在PCIE的配置空间找到MSI-X CAP的配置
b、根据配置找出MSI-X table的地址(BAR3 + OFFSET)
c、访问MSI-X table中的内容可以知道中断信息( data and address )
示例:
[root@localhost ~]# lspci -s 0000:0d.0 -vvv
00:0d.0 Ethernet controller: Virtio: Virtio network device
Region 0: I/O ports at 6120 [size=32]
Region 1: Memory at fea5a000 (32-bit, non-prefetchable) [size=4K] # 2、MSI-X 使用该地址空间
Region 4: Memory at fca10000 (64-bit, prefetchable) [size=16K]
Expansion ROM at fea20000 [disabled] [size=128K]
Capabilities: [98] MSI-X: Enable+ Count=3 Masked- #1、 Enable+ 表示MSIX为Enable状态 Count=3 表示支持3个中断
Vector table: BAR=1 offset=00000000 # 3、 MSI-X配置的寄存器在BAR1
PBA: BAR=1 offset=00000800
```
d、MSIX Table 中每一条Entry 代表一个中断向量,Msg Data 中包括了中断向量号,Msg Addr 中通常包含了多核CPU用于处理 中断的 Local APIC 编号;
address: 决定中断发送给哪个CPU
data: 决定中断发送给CPU的哪个vector
详细信息可以参考 irq_msi_compose_msg() 函数实现
下图是一个操作实例:

3、代码实现
3.1、 irq domian代码实现
- 子domain调用alloc申请中断的时候会递归调用父domain的alloc
msi_domain_alloc_irqs -> __irq_domain_alloc_irqs -> irq_domain_alloc_irqs_recursive // 递归调用alloc函数
msi_domain_alloc
|-> irq_find_mapping 检查是否已经分配了
|-> irq_domain_alloc_irqs_parent // 递归调用父domain的alloc函数
|-> x86_vector_alloc_irqs
|-> per_cpu(vector_irq, new_cpu)[vector] = irq_to_desc(irq) 把irq_desc 挂到cpu的中断向量上
- 子domain调用active使能中断的时候会递归调用父domain的active
msi_domain_alloc_irqs --> irq_domain_activate_irq // 递归调用父domain的active函数
msi_domain_activate
|-> irq_chip_compose_msi_msg // 组装Msi消息(包含address和data)
|-> irq_msi_compose_msg 中断目的CPU写入到msg->address中 中断向量写入msg->data中
|-> irq_chip_write_msi_msg // msi消息写入msix的table中
|-> pci_msi_domain_write_msg
|-> __pci_write_msi_msg 写入msix-table中
3.2、中断初始化:msi_domain_alloc_irqs
msi_domain_alloc_irqs
|-> ops->msi_check= pci_msi_domain_check_cap // 检查是否支持msix等功能
|-> ops->msi_prepare = pci_msi_prepare // 初始化 msi_alloc_info_t
|-> ops->set_desc = pci_msi_set_desc // 计算 msi_hwirq 存入 msi_alloc_info_t中
|-> __irq_domain_alloc_irqs
|-> irq_domain_alloc_descs // 1、 申请irq_desc 并且插入到irq_desc_tree中
|-> irq_domain_alloc_irq_data // 2 、申请irq_data、并且申请父domain的irq_data
|-> irq_domain_alloc_irqs_recursive // 3、递归调用domain的alloc函数,挂irq_desc到cpu中断向量
|-> irq_domain_insert_irq // 4、 建立virq和hwirq的映射关系
|-> irq_set_msi_desc_off // msi desc 信息写入到irq_desc
|-> irq_domain_activate_irq // 5、 递归调用domain的active函数、激活中断
|-> irq_chip_compose_msi_msg // 组装Msi消息(包含address和data)
|-> irq_chip_write_msi_msg // msi消息写入msix的table中