Linux通用中断处理
版权
- © 2005-2010:Thomas Gleixner
- © 2005-2006:Ingo Molnar
简介
通用中断处理层旨在为设备驱动程序提供完整的中断处理抽象。它能够处理所有不同类型的中断控制器硬件。设备驱动程序使用通用API函数来请求、启用、禁用和释放中断。驱动程序无需了解任何关于中断硬件细节的信息,因此它们可以在不同的平台上使用而无需进行代码更改。
本文档是为那些希望基于通用IRQ处理层实现中断子系统的开发人员提供的。
缘由
Linux中断处理的原始实现使用了__do_IRQ()
超级处理程序,它能够处理各种类型的中断逻辑。
最初,Russell King在Linux 2.5/2.6中鉴别出了不同类型的处理程序,以构建一个相当通用的ARM中断处理程序实现。他区分了以下类型:
- Level类型
- Edge类型
- Simple类型
在实现过程中,我们还鉴别出了另一种类型:
- Fast EOI类型
在SMP世界中的__do_IRQ()
超级处理程序中,我们还鉴别出了另一种类型:
- Per CPU类型
这种高级别IRQ处理程序的分割实现使我们能够针对每种特定的中断类型优化中断处理流程。这减少了特定代码路径中的复杂性,并允许对给定类型进行优化处理。
原始的通用IRQ实现使用了hw_interrupt_type
结构及其->ack
、->end
等回调函数来区分超级处理程序中的流控制。这导致了流逻辑和低级硬件逻辑的混合,并且导致了不必要的代码重复:例如在i386中,存在一个ioapic_level_irq
和一个ioapic_edge_irq
中断类型,它们共享许多低级细节但具有不同的流处理。
更自然的抽象是将“中断流”和“芯片细节”清晰地分开。
分析了一些架构的IRQ子系统实现后发现,它们大多数可以使用一组通用的“中断流”方法,并且只需要添加芯片级别的特定代码。这种分离对于需要在中断流本身具有特定怪癖但在芯片细节上没有怪癖的(子)架构也是有价值的,因此提供了更透明的IRQ子系统设计。
每个中断描述符都被分配了自己的高级别流处理程序,通常是通用实现之一。(这种高级别流处理程序的实现也使得在各种架构的嵌入式平台上提供了可以找到的解复用处理程序变得简单。)
这种分离使得通用中断处理层更加灵活和可扩展。例如,(子)架构可以使用通用的IRQ流实现来处理“level类型”中断,并添加一个(子)架构特定的“edge类型”实现。
为了使过渡到新模型更加容易,并防止现有实现的破坏,__do_IRQ()
超级处理程序仍然可用。这在一段时间内导致了一种二元性。随着时间的推移,新模型应该在越来越多的架构中使用,因为它能够实现更小、更清晰的IRQ子系统。它已经被弃用了三年,即将被移除。
已知的错误和假设
无(碰碰木头)。
抽象层
中断代码中有三个主要的抽象层:
- 高级别驱动程序API
- 高级别IRQ流处理程序
- 芯片级硬件封装
中断控制流
每个中断都由一个中断描述符结构irq_desc
描述。中断由一个“unsigned int”数值引用,该数值选择描述符结构数组中对应的中断描述结构。描述符结构包含状态信息和指向分配给该中断的中断流方法和中断芯片结构的指针。
每当中断触发时,低级架构代码通过调用desc->handle_irq()
调用通用中断代码。这个高级别的IRQ处理函数仅使用由分配给该中断的芯片描述符结构引用的desc->irq_data.chip
原语。
高级别驱动程序API
高级别驱动程序API包括以下函数:
request_irq()
request_threaded_irq()
free_irq()
disable_irq()
enable_irq()
disable_irq_nosync()
(仅适用于SMP)synchronize_irq()
(仅适用于SMP)irq_set_irq_type()
irq_set_irq_wake()
irq_set_handler_data()
irq_set_chip()
irq_set_chip_data()
有关详细信息,请参阅自动生成的函数文档。
高级别IRQ流处理程序
通用层提供了一组预定义的中断流方法:
handle_level_irq()
handle_edge_irq()
handle_fasteoi_irq()
handle_simple_irq()
handle_percpu_irq()
handle_edge_eoi_irq()
handle_bad_irq()
中断流处理程序(无论是预定义的还是特定于架构的)都是由架构在引导过程中或设备初始化期间分配给特定中断的。
默认流实现
辅助函数
辅助函数调用芯片原语,并被默认流实现所使用。以下是实现的辅助函数(简化摘录):
default_enable(struct irq_data *data) { desc->irq_data.chip->irq_unmask(data); } default_disable(struct irq_data *data) { if (!delay_disable(data)) desc->irq_data.chip->irq_mask(data); } default_ack(struct irq_data *data) { chip->irq_ack(data); } default_mask_ack(struct irq_data *data) { if (chip->irq_mask_ack) { chip->irq_mask_ack(data); } else { chip->irq_mask(data); chip->irq_ack(data); } } noop(struct irq_data *data)) { }
默认流处理程序实现
默认Level IRQ流处理程序
handle_level_irq
提供了一个用于级联触发中断的通用实现。
实现了以下控制流程(简化摘录):
desc->irq_data.chip->irq_mask_ack(); handle_irq_event(desc->action); desc->irq_data.chip->irq_unmask();
默认Fast EOI IRQ流处理程序
handle_fasteoi_irq
为只需要在处理程序结束时进行EOI的中断提供了一个通用实现。
实现了以下控制流程(简化摘录):
handle_irq_event(desc->action); desc->irq_data.chip->irq_eoi();
默认Edge IRQ流处理程序
handle_edge_irq
为边沿触发中断提供了一个通用实现。
实现了以下控制流程(简化摘录):
if (desc->status & running) { desc->irq_data.chip->irq_mask_ack(); desc->status |= pending | masked; return; } desc->irq_data.chip->irq_ack(); desc->status |= running; do { if (desc->status & masked) desc->irq_data.chip->irq_unmask(); desc->status &= ~pending; handle_irq_event(desc->action); } while (desc->status & pending); desc->status &= ~running;
默认Simple IRQ流处理程序
handle_simple_irq
为简单中断提供了一个通用实现。
注意: 简单流处理程序不调用任何处理程序/芯片原语。
实现了以下控制流程(简化摘录):
handle_irq_event(desc->action);
默认Per CPU流处理程序
handle_percpu_irq
为每CPU中断提供了一个通用实现。
每CPU中断仅在SMP上可用,该处理程序提供了一个简化版本而无需锁定。
实现了以下控制流程(简化摘录):
if (desc->irq_data.chip->irq_ack) desc->irq_data.chip->irq_ack(); handle_irq_event(desc->action); if (desc->irq_data.chip->irq_eoi) desc->irq_data.chip->irq_eoi();
EOI Edge IRQ流处理程序
handle_edge_eoi_irq
提供了边沿处理程序的一种变体,仅用于驯服powerpc/cell上一个严重受损的中断控制器。
Bad IRQ流处理程序
handle_bad_irq
用于没有真正处理程序的虚假中断。
怪癖和优化
通用函数适用于没有特定平台特定中断处理怪癖的“干净”架构和芯片。如果一个架构需要在“流”级别上实现怪癖,那么它可以通过覆盖高级别IRQ流处理程序来实现。
延迟中断禁用
这是一种可选择的、针对每个中断的功能,由Russell King在ARM中断实现中引入。当调用disable_irq()
时,它不会在硬件级别屏蔽中断。中断保持启用,并在中断事件发生时在流处理程序中屏蔽。这可以防止在硬件级别禁用中断时丢失边沿中断事件。当中断到达时,如果设置了IRQ_DISABLED标志,则中断将在硬件级别被屏蔽,并设置IRQ_PENDING位。当通过enable_irq()
重新启用中断时,将检查挂起位,如果设置了,则通过硬件或软件重发机制重新发送中断。(当您想要使用延迟中断禁用功能并且您的硬件无法重新触发中断时,需要启用CONFIG_HARDIRQS_SW_RESEND
。延迟中断禁用是不可配置的。
芯片级硬件封装
芯片级硬件描述符结构irq_chip
包含了所有直接与芯片相关的函数,这些函数可以被IRQ流实现所利用。
irq_ack
irq_mask_ack
- 可选,建议用于性能irq_mask
irq_unmask
irq_eoi
- 可选,用于EOI流处理程序irq_retrigger
- 可选irq_set_type
- 可选irq_set_wake
- 可选
这些原语严格地意味着它们所说的:ack表示ACK,masking表示屏蔽IRQ线路等。由流处理程序使用这些低级功能的基本单元。
__do_IRQ
入口点
原始实现的__do_IRQ()
是所有类型中断的另一入口点。它已经不存在。
这个处理程序被证明不适用于所有中断硬件,因此为边沿/级别/简单/每CPU中断重新实现了分割功能。这不仅仅是一种功能优化,它还缩短了中断的代码路径。
SMP上的锁定
芯片寄存器的锁定取决于定义芯片原语的架构。通过通用层,每个中断结构都受到desc->lock
的保护。
通用中断芯片
为了避免相同功能的IRQ芯片的重复实现,核心提供了一个可配置的通用中断芯片实现。开发人员在实现稍微不同的功能之前应该仔细检查通用芯片是否符合他们的需求。
https://www.kernel.org/doc/html/v6.6/core-api/genericirq.html#c.irq_gc_noop