Linux handle_level_irq电平触发与mask_ack_irq序列

发布时间:2026/6/15 6:37:06

Linux handle_level_irq电平触发与mask_ack_irq序列 Linux handle_level_irq电平触发与mask_ack_irq序列handle_level_irq是Linux内核为电平触发中断提供的标准处理函数。电平触发中断的特点是当中断信号线处于有效电平高或低时中断请求持续有效。与边沿触发不同电平触发不会锁存脉冲信号因此在处理函数返回前必须确保信号线上的有效电平已被设备清除否则中断会立即再次触发。handle_level_irq围绕mask_ack_irq序列管理这一行为。函数实现如下cvoid handle_level_irq(struct irq_desc *desc){raw_spin_lock(desc-lock);mask_ack_irq(desc);desc-istate ~(IRQS_REPLAY | IRQS_WAITING);if (unlikely(irqd_irq_disabled(desc-irq_data))) {desc-istate | IRQS_PENDING;goto out_unlock;}if (unlikely(!desc-action)) {goto out_unlock;}kstat_incr_irqs_this_cpu(desc);irq_compat_set_prog_affinity(desc);raw_spin_unlock(desc-lock);handle_irq_event(desc);raw_spin_lock(desc-lock);if (!(desc-istate IRQS_DISABLED) desc-action) {if (irqd_irq_masked(desc-irq_data))unmask_irq(desc);}out_unlock:raw_spin_unlock(desc-lock);}核心流程在第一行就执行mask_ack_irq。这是电平触发与边沿触发的关键区别在运行任何处理函数之前先屏蔽中断源并确认ACK。为什么必须mask因为电平触发的信号线在设备服务完毕前持续有效如果不maskCPU在开中断后或handler返回瞬间再次被同一中断打断形成无限递归的中断风暴。mask操作切断了信号线到CPU的路径。mask_ack_irq函数的实现cvoid mask_ack_irq(struct irq_desc *desc){struct irq_chip *chip desc-irq_data.chip;if (chip-irq_mask_ack) {chip-irq_mask_ack(desc-irq_data);return;}if (chip-irq_mask)chip-irq_mask(desc-irq_data);if (chip-irq_ack)chip-irq_ack(desc-irq_data);}优先使用芯片提供的复合操作chip-irq_mask_ack因为部分硬件可以在一条寄存器指令中同时完成mask和ack减少了寄存器读写次数。若芯片未提供复合操作则分别调用irq_mask和irq_ack。unmask操作的处理是电平触发的另一关键点。handle_irq_event返回后desc-lock重新被获取检查条件后调用unmask_irqcif (!(desc-istate IRQS_DISABLED) desc-action) {if (irqd_irq_masked(desc-irq_data))unmask_irq(desc);}unmask必须在设备已将中断信号线恢复到非有效电平之后执行。如果设备驱动在handler内正确清除了中断源写设备寄存器清除中断标志信号线回到无效状态此时unmask是安全的。如果设备未能清除中断源unmask后中断线仍然有效将立即重新触发mask_ack_irq序列CPU陷入循环处理。这通常被视为设备驱动bug。IRQS_PENDING标志在电平触发中的含义与边沿触发不同。当中断在disable状态下到达时handle_level_irq设置IRQS_PENDINGcif (unlikely(irqd_irq_disabled(desc-irq_data))) {desc-istate | IRQS_PENDING;goto out_unlock;}但这个pending在电平触发中不会被二次消费——它仅标记该中断在mask状态下曾经到达。当中断通过enable恢复后__enable_irq检查IRQS_PENDING并调用desc-handle_irq重新处理。对于电平触发中断重新处理意味着重新执行mask_ack_irq和handle_irq_event。电平触发中断的IRQ_WAITING检测机制与边沿触发类似在中断注册阶段设置IRQS_WAITING标志handle_level_irq在顶部清除cdesc-istate ~(IRQS_REPLAY | IRQS_WAITING);区别在于电平触发的IRQ_WAITING不仅仅是首次中断确认。当电平触发中断的action链表为空时中断处理会直接将pending中断丢弃但不屏蔽——这与边沿触发不同边沿触发在action为空时会调用mask_irq永久屏蔽。电平触发之所以不永久屏蔽是因为电平信号线可以被动监控设备驱动注册后自然能收到下一次中断。handle_level_irq与handle_fasteoi_irq的对比值得注意。handle_fasteoi_irq是为支持EOIEnd of Interrupt机制的中断控制器如GIC设计的它不需要显式mask而是通过EOI通知控制器中断处理结束。但在语义上fasteoi等效于电平触发——控制器在EOI前不会再次提交相同中断。mask_ack_irq在fasteoi中退化为仅仅执行mask操作ack由后续的EOI替代。对于共享中断IRQF_SHARED电平触发要求所有共享设备的中断触发类型一致。如果共享中断线上任一设备是边沿触发则不能与电平触发设备共享。这是硬件电气特性决定的——电平信号线是线与逻辑只要有一个设备保持有效电平中断线持续有效无法区分是哪个设备触发了中断因此必须逐个调用action链表处理函数进行polling。

相关新闻