Linux 内核如何处理共享 IRQ?

Linux 内核如何处理共享 IRQ?

根据我到目前为止所读到的内容,“当内核收到中断时,所有注册的处理程序都会被调用。”

据我所知,每个 IRQ 的注册处理程序都可以通过查看/proc/interrupts,并且我还了解注册的处理程序来自已调用request_irq传递大致以下形式的回调的驱动程序:

irqreturn_t (*handler)(int, void *)

据我所知,每个与特定 IRQ 相关的中断处理程序回调都应该被调用,并且由处理程序来确定中断是否确实应该由它处理。如果处理程序不应处理特定中断,则它必须返回内核宏IRQ_NONE

我无法理解的是,每个驱动程序如何确定它是否应该处理中断。我想如果他们应该等待中断,他们可以在内部进行跟踪。如果是这样,我不知道他们如何能够处理同一 IRQ 后面的多个驱动程序等待中断的情况。

我试图理解这些细节的原因是因为我kexec在处理 PCIe 桥接器以及下游 PCI 上的复位引脚和各种寄存器时,正在搞乱在系统操作过程中重新执行内核的机制。设备。这样做时,重新启动后,我要么遇到内核恐慌,要么其他驱动程序抱怨他们收到中断,即使没有进行任何操作。

处理程序如何决定应该由它处理中断是个谜。

编辑:如果相关的话,有问题的CPU架构是x86

答案1

这涵盖在第10章Linux 设备驱动程序,第三版,作者:Corbet 等人。免费提供在线的,或者你可以以奥莱利的方式扔一些谢克尔对于死树或电子书形式。与您的问题相关的部分从第一个链接的第 278 页开始。

就其价值而言,这是我对这三页的尝试,以及我在谷歌上搜索到的其他内容:

  • 当您注册共享 IRQ 处理程序时,内核会检查:

    A。该中断不存在其他处理程序,或者

    b.所有之前注册的请求中断共享

如果任一情况适用,它都会检查您的dev_id参数是否唯一,以便内核可以区分多个处理程序,例如在处理程序删除期间。

  • 当 PCI1 硬件设备发出 IRQ 线时,内核的低级中断处理程序将被调用,并依次调用全部注册的中断处理程序,将每个返回dev_id用于通过注册处理程序request_irq()

dev_id值必须是机器唯一的。常见的方法是将指针传递给struct驱动程序用来管理该设备的每个设备。由于该指针必须位于驱动程序的内存空间内才能对驱动程序有用,因此它是本身该驱动程序独有。²

如果有多个驱动程序为给定中断注册,它们将全部被呼叫时任何设备引发共享中断线。如果不是您的驱动程序的设备执行此操作,则驱动程序的中断处理程序需要检测到这一点并IRQ_NONE立即返回。

另一种情况是您的驱动程序正在管理多个设备。驱动程序的中断处理程序应使用dev_id来决定轮询哪个设备。

科贝特示例等人。给出的是PC并行端口的端口。当它置位中断线时,它还会设置其第一个设备寄存器中的最高位。 (即inb(0x378) & 0x80 == true,假设标准 I/O 端口编号。)当您的处理程序检测到这一点时,它应该执行其工作,然后通过将从 I/O 端口读取的值写回到顶部的端口来清除 IRQ。位清除。

我看不出有什么特殊机制是特殊的。不同的硬件设备可以选择不同的机制。唯一重要的是,对于允许共享中断的设备,它必须有某种方式让驱动程序能够设备的中断状态,以及一些方法清除中断。您必须阅读设备的数据表或编程手册,以了解您的特定设备使用的机制。

  • 当您的中断处理程序告诉内核它处理了该中断时,这不会阻止内核继续调用为同一中断注册的任何其他处理程序。如果您在使用电平触发中断时共享中断线,这是不可避免的。

想象一下两个设备同时声明同一个中断线。 (或者至少,时间如此接近,以至于内核没有时间调用中断处理程序来清除该行,从而将第二个断言视为单独的。)内核必须调用该中断行的所有处理程序,以给每个有机会查询其相关硬件以查看是否需要关注。对于给定的中断,两个不同的驱动程序很可能在同一次处理程序列表中成功处理中断。

因此,您的驱动程序必须在中断处理程序返回之前告诉设备它正在设法清除其中断断言。我不清楚否则会发生什么。连续断言的中断线将导致内核连续调用共享中断处理程序,或者它将屏蔽内核查看新中断的能力,因此永远不会调用处理程序。不管怎样,都是灾难。


脚注:

  1. 我在上面指定了 PCI,因为以上所有内容都假设电平触发中断,如原始 PCI 规范中所使用的那样。使用ISA边沿触发中断,这使得共享充其量也变得棘手,而且即使如此也只有在硬件支持的情况下才可能实现。 PCIe用途消息信号中断;中断消息包含一个唯一值,内核可以使用它来避免 PCI 中断共享所需的循环猜测游戏。 PCIe 可能会消除中断共享的需要。 (我不知道它是否真的如此,只是它有潜力。)

  2. Linux 内核驱动程序都共享相同的内存空间,但不相关的驱动程序不应该在另一个驱动程序的内存空间中乱搞。除非您传递该指针,否则您可以非常确定另一个驱动程序不会意外地自行得出相同的值。

答案2

当驱动程序请求共享 IRQ 时,它会将指向内核的指针传递给对驱动程序内存空间内的设备特定结构的引用。

根据LDD3:

每当两个或多个驱动程序共享一条中断线并且硬件中断该线上的处理器时,内核就会调用为该中断注册的每个处理程序,并传递每个处理程序自己的 dev_id。

在检查几个驱动程序的 IRQ 处理程序后,它们似乎探测硬件本身,以确定它是否应该处理中断或返回IRQ_NONE

例子

UHCI-HCD驱动程序
  status = inw(uhci->io_addr + USBSTS);
  if (!(status & ~USBSTS_HCH))  /* shared interrupt, not mine */
    return IRQ_NONE;

在上面的代码中,驱动程序正在读取寄存器USBSTS以确定是否有中断需要服务。

SDHCI驱动程序
  intmask = sdhci_readl(host, SDHCI_INT_STATUS);

  if (!intmask || intmask == 0xffffffff) {
    result = IRQ_NONE;
    goto out;
  }

正如前面的示例一样,驱动程序正在检查状态寄存器,SDHCI_INT_STATUS以确定是否需要服务中断。

Ath5k 驱动程序
  struct ath5k_softc *sc = dev_id;
  struct ath5k_hw *ah = sc->ah;
  enum ath5k_int status;
  unsigned int counter = 1000;

  if (unlikely(test_bit(ATH_STAT_INVALID, sc->status) ||
        !ath5k_hw_is_intr_pending(ah)))
    return IRQ_NONE;

再举一个例子。

答案3

请访问检查此关联:

通常的做法是,仅在从内存映射寄存器检查 IRQ 状态后才触发 IRQ 处理程序中的下半部或任何其他逻辑。因此,这个问题默认是由优秀的程序员解决的。

相关内容