再问PCIe中断问题

发布于 2022-09-23 15:13:19 字数 305 浏览 12 评论 0

在软件中有没有办法区分中断是MSI触发的还是INTx触发的?现在用的Xilinx V5的板子发中断是通过它的IP Core发的,导致遇到的问题不好定位。

另外规范中感觉有些含糊(也有可能是没看到位,毕竟太多了),MSI中断如果全能,最终映射到软件应该也是一个IRQ号吧,它是不是也是Configuration Register中的Interrupt Pin的值?

还有一个问题是规范中简单提了一下Level-Triggerd和Edge-Trigger,并且规定MSI只能是Edge-Trigger的,但INTx好像都可以,这两者在操作系统层面有没有什么区别?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(9

得不到的就毁灭 2022-09-30 15:13:20

>在软件中有没有办法区分中断是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给它撤销掉——这期间相当于维护了一个状态。

爱情眠于流年 2022-09-30 15:13:20

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掉,实在是很奇怪。

司马昭之心 2022-09-30 15:13:20

自己顶一下。看了一下Linux的代码,发现它的中断号是用另外的机制产生的:

static int msi_capability_init(struct pci_dev *dev)
{
    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;
}

肩上的翅膀 2022-09-30 15:13:20

有些问题自己搞清楚了:
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()中是直接读的寄存器:

static void pci_read_irq(struct pci_dev *dev)
{
    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;
}

乱世争霸 2022-09-30 15:13:20

>> 按LLD3中的说法,根本可以直接读出来做IRQ号使用于request_irq

ldd3的说法是错的。  driver应该用pdev->irq,而不是读INTERRUPT LINE寄存器。

黎夕旧梦 2022-09-30 15:13:20

感觉有点儿象是在pcibios_fixup_irqs中重新调整了dev->irq,但从内核代码看出现这种情况它应该在/var/log/messages中打印一个“PCI->APIC IRQ transform: ...”,但实际上却找不到。

先告一段落了,至少现在中断调通可以正常使用了。LLD3对于PCI设备驱动的描述实在是太少了,而且它里面的配置空间讲得都是很老的了,连Capability List入口都写成保留字节了。

·深蓝 2022-09-30 15:13:20

原帖由 albcamus 于 2009-2-13 10:44 发表
>> 按LLD3中的说法,根本可以直接读出来做IRQ号使用于request_irq

ldd3的说法是错的。  driver应该用pdev->irq,而不是读INTERRUPT LINE寄存器。

对pci来说这两个值应该是一致的吧?

drivers/pci/setup-irq.c->pdev_fixup_irq()

故人如初 2022-09-30 15:13:20

你可以在写个module,

for each pci dev, 读一下其pdev->irq和INTERRUPT LINE寄存器, 比较一下就知道是否一样了。

注意kernel不要太老的版本

云淡风轻 2022-09-30 15:13:20

那INTERRUPT LINE寄存器里面得值是什么意思呢?

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文