根据我到目前为止所读到的内容,“当内核收到中断时,所有注册的处理程序都会被调用。”
据我所知,每个 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。位清除。
我看不出有什么特殊机制是特殊的。不同的硬件设备可以选择不同的机制。唯一重要的是,对于允许共享中断的设备,它必须有某种方式让驱动程序能够读设备的中断状态,以及一些方法清除中断。您必须阅读设备的数据表或编程手册,以了解您的特定设备使用的机制。
- 当您的中断处理程序告诉内核它处理了该中断时,这不会阻止内核继续调用为同一中断注册的任何其他处理程序。如果您在使用电平触发中断时共享中断线,这是不可避免的。
想象一下两个设备同时声明同一个中断线。 (或者至少,时间如此接近,以至于内核没有时间调用中断处理程序来清除该行,从而将第二个断言视为单独的。)内核必须调用该中断行的所有处理程序,以给每个有机会查询其相关硬件以查看是否需要关注。对于给定的中断,两个不同的驱动程序很可能在同一次处理程序列表中成功处理中断。
因此,您的驱动程序必须在中断处理程序返回之前告诉设备它正在设法清除其中断断言。我不清楚否则会发生什么。连续断言的中断线将导致内核连续调用共享中断处理程序,或者它将屏蔽内核查看新中断的能力,因此永远不会调用处理程序。不管怎样,都是灾难。
脚注:
答案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
以确定是否有中断需要服务。
intmask = sdhci_readl(host, SDHCI_INT_STATUS);
if (!intmask || intmask == 0xffffffff) {
result = IRQ_NONE;
goto out;
}
正如前面的示例一样,驱动程序正在检查状态寄存器,SDHCI_INT_STATUS
以确定是否需要服务中断。
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 处理程序中的下半部或任何其他逻辑。因此,这个问题默认是由优秀的程序员解决的。