再问PCIe中断问题
在软件中有没有办法区分中断是MSI触发的还是INTx触发的?现在用的Xilinx V5的板子发中断是通过它的IP Core发的,导致遇到的问题不好定位。
另外规范中感觉有些含糊(也有可能是没看到位,毕竟太多了),MSI中断如果全能,最终映射到软件应该也是一个IRQ号吧,它是不是也是Configuration Register中的Interrupt Pin的值?
还有一个问题是规范中简单提了一下Level-Triggerd和Edge-Trigger,并且规定MSI只能是Edge-Trigger的,但INTx好像都可以,这两者在操作系统层面有没有什么区别?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(9)
>在软件中有没有办法区分中断是MSI触发的还是INTx触发的?现在用的Xilinx V5的板子发中断是通过它的IP Core发的,导致遇到的问题不好定位。
注册中断的时候区分。 实际发生中断的时候,就走到你注册的handler里去了。 再说,PCIE设备是不直接支持INTx的,它只是用MSI方式emulate了INTx中断,以便软件可以向后兼容。
>另外规范中感觉有些含糊(也有可能是没看到位,毕竟太多了),MSI中断如果全能,最终映射到软件应该也是一个IRQ号吧,它是不是也是Configuration Register中的Interrupt Pin的值?
是有个irq号,不过这个号没多少意义。 和Interupt PIN、Interrupt LINE寄存器更是没关系。
> 还有一个问题是规范中简单提了一下Level-Triggerd和Edge-Trigger,并且规定MSI只能是Edge-Trigger的,但INTx好像都可以,这两者在操作系统层面有没有什么区别?
应该说是有点区别的。 MSI之所以应该被当成一个Edge-triggered,是因为它只是一个消息写到系统总线上,并不维护什么状态。 而INTx必须是Level-Triggered,它是有PCI设备的PIN(也就是INTA/INTB/INTC/INTD这些),经过主板上chipset的路由,间接“插到” IO-APIC的输入引脚上的。 发生了中断后,电平一直有效,直到IOAPIC给它撤销掉——这期间相当于维护了一个状态。
1. 注册中断时如何区分?我只知道用reqeust_irq来注册,这个显然是和中断号绑定的吧,如果用MSI的话是不是另有专门的注册接口?不过用CONFIG_PCI_MSI搜索内核代码好像没看到中断处理上有什么特殊的区别。
2. 如果MSI和Interrupt Line没有关系,它是如何和中断处理的handler联系到一起的?
V5的板子现在测试的结果感觉是用了INTx模拟发送的,但奇怪的是它会一直产生中断,或者某种错误状态导致内核认为中断错过一个使APIC重新产生(但这种情况好像多核才会出现)。目前没办法确定是不是板子产生的,具体发送是IP Core做的,不清楚具体如何实现的。
同样的板子,通过/proc/interrupts查看在一台HP工作站上看时发现V5的板子中断是IO-APIC-level,而在AMD的一个台式机上却是IO-APIC-fastio,不知道两者是不是一样的?另外在工作站上发现两个集成的网卡(应该也是PCIe总线上)的中断是MSI,但插在PCIe x16上的独立显卡却是IO-APIC-level,这个是如何确定出来的?因为PCIe是强制支持MSI的,那么显卡本身应该是支持的,但为什么系统配置它使用传统的INTx模拟呢?
在工作站上更奇怪的是如果我不挂自己的中断处理函数,它产生中断过多之后会使一个USB的中断被Disable掉,实在是很奇怪。
自己顶一下。看了一下Linux的代码,发现它的中断号是用另外的机制产生的:
{
struct msi_desc *entry;
int pos, ret;
u16 control;
msi_set_enable(dev, 0); /* Ensure msi is disabled as I set it up */
pos = pci_find_capability(dev, PCI_CAP_ID_MSI);
pci_read_config_word(dev, msi_control_reg(pos), &control);
/* MSI Entry Initialization */
entry = alloc_msi_entry();
if (!entry)
return -ENOMEM;
entry->msi_attrib.type = PCI_CAP_ID_MSI;
entry->msi_attrib.is_64 = is_64bit_address(control);
entry->msi_attrib.entry_nr = 0;
entry->msi_attrib.maskbit = is_mask_bit_support(control);
entry->msi_attrib.masked = 1;
entry->msi_attrib.default_irq = dev->irq; /* Save IOAPIC IRQ */
entry->msi_attrib.pos = pos;
if (is_mask_bit_support(control)) {
entry->mask_base = (void __iomem *)(long)msi_mask_bits_reg(pos,
is_64bit_address(control));
}
entry->dev = dev;
if (entry->msi_attrib.maskbit) {
unsigned int maskbits, temp;
/* All MSIs are unmasked by default, Mask them all */
pci_read_config_dword(dev,
msi_mask_bits_reg(pos, is_64bit_address(control)),
&maskbits);
temp = (1 << multi_msi_capable(control));
temp = ((temp - 1) & ~temp);
maskbits |= temp;
pci_write_config_dword(dev,
msi_mask_bits_reg(pos, is_64bit_address(control)),
maskbits);
}
list_add_tail(&entry->list, &dev->msi_list);
/* Configure MSI capability structure */
ret = arch_setup_msi_irqs(dev, 1, PCI_CAP_ID_MSI);
if (ret) {
msi_free_irqs(dev);
return ret;
}
/* Set MSI enabled bits */
pci_intx(dev, 0); /* disable intx */
msi_set_enable(dev, 1);
dev->msi_enabled = 1;
dev->irq = entry->irq;
return 0;
}
有些问题自己搞清楚了:
Linux不会自动使能MSI/MSI-X的,需要驱动在调用request_irq之前调用pci_enable_msi/msix()。以前看PCI规范中写设置MSI Control Register只能是系统软件,不能在驱动中处理,所以一直以为是系统自动处理的。
今天测试了一下,发现用MSI方式是OK的,不清楚V5的IP Core怎么做的,只能发MSI或根据配置情况自动选择?INTx方式有缺陷或我们的使用方式不对?这个Core也并非很稳定,去年用新版本它上面映射上来的RAM写正常,读几次之后就挂了,整个系统崩溃,换了个旧的就OK了。
不过发现Linux中的pci.txt的描述对于PCIe并不一定完全适用,它里面讲到MSI的两个好处之一是避免DMA/IRQ race conditions,但对于PCIe,DMA和MSI如果使用不同的TC/VC的话还是会存在问题的。
现在还有一个很迷惑的问题是Interrupt Line寄存器到底是干啥用的?按LLD3中的说法,根本可以直接读出来做IRQ号使用于request_irq,但昨天我发现用INTx的PCIe设备都对不上,于是特地在一台带了PCI无线网卡的机器上查看,结果发现/proc/interrupts中的中断号和这一寄存器还是对不上。不过在Linux代码中看到在pci_setup_device()->pci_read_irq()中是直接读的寄存器:
{
unsigned char irq;
pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &irq);
dev->pin = irq;
if (irq)
pci_read_config_byte(dev, PCI_INTERRUPT_LINE, &irq);
dev->irq = irq;
}
>> 按LLD3中的说法,根本可以直接读出来做IRQ号使用于request_irq
ldd3的说法是错的。 driver应该用pdev->irq,而不是读INTERRUPT LINE寄存器。
感觉有点儿象是在pcibios_fixup_irqs中重新调整了dev->irq,但从内核代码看出现这种情况它应该在/var/log/messages中打印一个“PCI->APIC IRQ transform: ...”,但实际上却找不到。
先告一段落了,至少现在中断调通可以正常使用了。LLD3对于PCI设备驱动的描述实在是太少了,而且它里面的配置空间讲得都是很老的了,连Capability List入口都写成保留字节了。
对pci来说这两个值应该是一致的吧?
drivers/pci/setup-irq.c->pdev_fixup_irq()
你可以在写个module,
for each pci dev, 读一下其pdev->irq和INTERRUPT LINE寄存器, 比较一下就知道是否一样了。
注意kernel不要太老的版本
那INTERRUPT LINE寄存器里面得值是什么意思呢?