Linux内核如何处理共享的IRQ?


14

根据到目前为止的读物,“当内核收到中断时,将调用所有已注册的处理程序。”

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

irqreturn_t (*handler)(int, void *)

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

我无法理解的是,每个驱动程序如何确定是否应该处理中断。我想如果他们期望中断,他们可以在内部保持跟踪。如果是这样,我不知道他们将如何处理同一个IRQ后面的多个驱动程序都希望中断的情况。

我试图了解这些细节的原因是因为我弄乱了在kexec系统操作过程中重新执行内核的机制,同时使用了PCIe桥以及下游PCI上的复位引脚和各种寄存器设备。这样做时,重新启动后,我可能会出现内核崩溃,或者其他驱动程序抱怨即使没有进行任何操作,它们也会收到中断。

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

编辑:如果相关,则所讨论的CPU体系结构为x86


1
stackoverflow.com/questions/14371513/for
ashared-interruption-line-how-i-find-i-interrupthandler-to-usec

Answers:


14

这由Corbet等人在Linux Device Drivers第三版的第10章介绍。它可以在网上免费获得,或者您可以折腾一些谢克尔·奥赖利的方式索取死树或电子书表格。与您的问题有关的部分从第一个链接的第278页开始。

对于它的价值,这里是我对这三个页面的解释,以及我在Google上搜索过的其他内容:

  • 当您注册共享的IRQ处理程序时,内核将检查以下任一情况:

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

    b。所有先前注册的用户请求中断共享

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

  • 当PCI¹硬件设备提高IRQ线时,将调用内核的低级中断处理程序,然后依次调用所有已注册的中断处理程序,并通过分别返回dev_id您用于注册处理程序的中断处理程序request_irq()

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

    如果为一个给定的中断注册了多个驱动程序,则当任何设备引发该共享中断线时,将全部调用它们。如果不是驱动程序的设备执行此操作,则将向驱动程序的中断处理程序传递不属于它的值。发生这种情况时,驱动程序的中断处理程序必须立即返回。dev_id

    另一种情况是您的驱动程序正在管理多个设备。驱动程序的中断处理程序将获得dev_id驱动程序已知的值之一。您的代码应该轮询每个设备,以找出引发中断的设备。

    示例Corbet 等。提供的是PC并行端口。当它断言中断线时,它还会在其第一个设备寄存器中设置最高位。(也就是说inb(0x378) & 0x80 == true,假定标准的I / O端口编号。)处理程序检测到此操作后,就应该执行此工作,然后通过将从I / O端口读取的值写回到顶部的端口来清除IRQ。清除。

    我看不出任何特殊机制是特殊的原因。不同的硬件设备可以选择不同的机制。唯一重要的是,对于允许共享中断的设备,驱动程序必须具有某种方式来使驱动程序读取设备的中断状态,并具有某种方式来清除中断。您必须阅读设备的数据表或编程手册,以了解特定设备使用的机制。

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

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

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


脚注:

  1. 我在上面指定了PCI,因为以上所有条件均假设使用了原始PCI规范中所用的电平触发的中断。ISA使用了沿边沿触发的中断,这使得共享最多为棘手,甚至只有在硬件支持的情况下才有可能。PCIe使用消息信号中断;中断消息包含一个唯一的值,内核可以使用该值来避免PCI中断共享所需的循环猜测游戏。PCIe可以消除对中断共享的需求。(我不知道它是否真的有,只是它有潜力。)

  2. Linux内核驱动程序都共享相同的内存空间,但是不应该将无关的驱动程序混在另一个内存空间中。除非您传递该指针,否则您可以确定其他驱动程序不会偶然意外地提供相同的值。


1
就像您提到的那样,可能会传递一个dev_id不拥有的中断处理程序。在我看来,不拥有该dev_id结构的驱动程序仍可能基于其解释内容的方式将其误认为其自身的可能性为零。如果不是这种情况,那么有什么机制可以阻止这种情况?
bsirang 2012年

通过dev_id在驱动程序的内存空间中指向某个内容来防止出现这种情况。另一个驱动程序可以构成一个dev_id值,该值恰好与您的驱动程序拥有的内存指针混淆了,但这不会发生,因为每个人都在遵守规则。请记住,这是内核空间:理所当然地认为,自律是与用户空间代码不同的,用户空间代码可能会顺理成章地假定允许任何不被禁止的行为。
沃伦·杨

根据LDD3的第十章:“每当两个或多个驱动程序共享一条中断线,并且硬件中断该线的处理器时,内核都会调用为该中断注册的每个处理程序,并传递各自的dev_id”,这似乎像以前的理解关于中断处理程序是否可以通过dev_id它不拥有的传递是不正确的。
bsirang 2012年

我觉得那是误读。当我写这篇文章时,我正在混淆两个概念。我已经编辑了答案。要求您的中断处理程序快速返回的条件是,由于其未管理的设备的中断声明而调用了它。的值dev_id不会帮助您确定是否已发生。您必须问硬件,“您响了吗?”
沃伦·杨

是的,现在我需要弄清楚我所要修改的内容实际上是如何导致其他驱动程序通过重新启动内核后认为其设备“响”的kexec
bsirang 2012年

4

当驱动程序请求共享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;

再举一个例子。


0

请访问检查此链接

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


您的链接内容不可用
user3405291
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.