MSI是什么?
MSI(Message Signaled Interrupts)是一种中断方式, 依靠设备将一小段中断描述数据写入特定地址 【注一】来通知CPU中断的产生。
MSI从PCI 2.2开始支持, 在PCI 3.0中得到扩展. 支持更多中断以及拥有独立配置各个中断能力的MSI-X则从PCI 3.0开始被支持.
注一:这里写入数据,并不是device可以直接向cpu发送数据作为中断的一部分。 而是写到MMIO(memory mapped IO)地址的数据是给chipset的,chipset读取这段数据来决定说发送什么样的中断给某个CPU。Device是没有办法直接利用这段数据给interrupt handler传递更多的信息的。
MSI是设备端通过向Host发MSG的方式产生IRQ request,在送达Host/PCI桥之前它和普通的Memory Write 并没有区别,Host收到以后再将该Memory Write转化成Interrupt送到各个CPU.
MSI的优点:
1. 相对于基于引脚的中断响应方式的性能优势:
传统中断的中断引脚 常常被多个设备共享,当中断触发时, 内核必须依次触发每个设备相应的中断处理(interrupt service route)
来确定是哪个设备产生的,这必将损失系统的整体性能. 每个MSI中断属于设备所独有,因此不会产生共享中断带来的性能损失。
2. MSI增加了中断的数目:
传统基于引脚的中断每个设备最多4个中断引脚,对于多功能PCI设备而言,每一个功能最多只有一个中断引脚。设备驱动程序必须查询设备产生
的具体事件,势必降低中断处理速度。而MSI中断中,一个设备可以支持最多32个MSI中断,每个中断有其特定功能,那么驱动程序在处理如数 据收发中断时使用单独的中 断就更有效率。
3. 数据写入内存与中断触发是串行的:
在传统中断中,中断通常是设备发送完数据后,给cpu一个中断请求,通知cpu进行处理。但是这里有个问题,PCI设备本来是要在将
数据写到内存后,然后发送一个中断表明DMA写操作完成了,但是为了不打扰到其他内存的使用,PCI 网桥或者内存控制器可能会延迟写数据操作,这时候中断却已经到达cpu了,那么就可能导致cpu从内存读取了错误的数据【注二】。为了阻止这样超期的情况发生,interrupt handlers必须从设备读取寄存器(轮询方式)来保证DMA写操作已经完成,数据已经到达内存,但是这会使性能降低。MSI中断与main data path共享同一通道,并且是先发生特定数据到MMIO, 然后才决定中断发生,有严格的顺序,并且不用去轮询设备寄存器,提升了性能。
注二: 传统的基于引脚的中断使用专用的通道来发生中断控制信息,与main data path是分开的,所以也会出现上述延迟情况,从而出现了超期现象.
如何在Linux下使用MSI?
PCI设备默认以基于引脚的中断响应方式初始化, 然后由驱动来检测是否支持MSI并决定是否启用, 如果启用MSI失败则会回退到基于引脚的中断响应方式.
下面是关于MSI的几个重要的内核函数:
- 启用MSI并给设备分配一个中断: int pci_enable_msi(struct pci_dev *dev)
- 允许驱动申请minvec至maxvec个中断: int pci_enable_msi_range(struct pci_dev *dev, int minvec, int maxvec)
- 获取设备申请的中断向量个数: int pci_msi_vec_count(struct pci_dev *dev)
- 禁用MSI, 回退到基于引脚的中断响应方式: void pci_disable_msi(struct pci_dev *dev)