130
社区成员




调整通用中断管理流程 (genirq)
中断流水线涉及控制中断流的基本变化:来自 IRQ 域 的API handle_domain_irq() 通过调用 generic_pipeline_irq() 而不是 generic_handle_irq() 将所有父 IRQ 重定向到流水线入口。
通用中断流处理程序通过根据中断类型调用对应的 irqchip 例程(例如 irq_ack()、irq_eoi())确认通过硬件传入 IRQ 事件。但是,中断流处理程序不会立即调用in-band中断处理程序。相反,他们通过调用 handle_oob_irq() 将事件移交给中断管道核心。
如果接收到的中断存在out-of-band处理程序,则 handle_oob_irq() 将执行上下文切换到 oob 阶段(如果不是当前)后立即调用它。否则,该事件在当前 CPU 的in-band阶段日志中标记为未决中断。
下图说明了 Dovetail 流水线中断模型中整个内核代码的执行流程。注意两步过程:首先我们尝试将传入的 IRQ 传递给对应的out-of-band处理程序(如果存在),然后我们可以处理当前每个 CPU 中断日志中的任何 IRQ,其中可能存在非 OOB 事件。
如上所示,在 Dovetail 的流水线中断模型中,中断流处理程序可能会针对单个 IRQ 运行两次:
注意:
任何传入的 IRQ 事件要么被分派给一个或多个out-of-band处理程序,要么被分派给一个或多个in-band处理程序,但绝不会分派给它们的混合体。此外,由于每个未被out-of-band处理程序处理的中断都将无条件地进入in-band阶段的中断事件日志,因此所有不能被out-of-band处理程序处理的外部中断都必须在in-band代码中有一个中断处理程序。
一旦 generic_pipeline_irq() 返回,如果被抢占的执行上下文在未停止的in-band阶段上运行,中断流水线核心立即同步中断状态,这意味着在in-band阶段的日志中发现的所有挂起的 IRQ 立即传递到它们各自的in-band处理程序。在所有其他情况下,立即离开IRQ 堆栈而不运行这些处理程序。 IRQ 可能会一直挂起,直到in-band代码从被抢占中恢复,然后清除虚拟中断禁用标志,这将导致中断状态同步,最终运行in-band处理程序。
In-band IRQ 传递代码
为了将挂起的中断传递到in-band阶段,您必须提供同步 IRQ 阶段的通用 Dovetail 内核调用名为 arch_do_IRQ_pipelined() 的例程,该例程作为流水线特定架构支持代码的一部分。此函数同时传递设备 IRQ 和 IPI,它应根据以下逻辑相应地调度事件:
对于 ARM 和 ARM64,相应的代码如下所示:
阅读这段代码时的一些注意事项:
延迟电平触发的 IRQ
在事件没有任何out-of-band处理程序的情况下,设备可能会继续发送中断信号,直到该中断在其自己的寄存器中被解除。同时,如果in-band阶段当前停止,我们可能不允许立即在当前中断上下文上运行in-band处理程序,我们将不得不等待直到in-band代码再次接受中断。但是,CPU 中的中断禁用位肯定会同时被清除。出于这个原因,根据中断类型,流水线代码修改的中断流处理程序可能必须屏蔽中断,直到in-band处理程序从in-band阶段运行,解除该中断源的中断状态。这通常发生在电平触发的中断中,防止设备通过连续的中断请求冲击 CPU。
有问题的场景示例:
由于共享一条中断的所有 IRQ 处理程序都以互斥的方式在in-band或out-of-band进行,因此这种屏蔽不能延迟out-of-band事件。
注意:
屏蔽中断线背后的逻辑,直到事件在稍后的某个时间点被处理 - 在原始中断上下文之外 - 适用于线程中断模型(即 IRQF_THREAD)。在这种情况下,中断线可能会被屏蔽,直到 IRQ 线程被调度,在中断处理程序最终清除事件原因之后。
使中断流处理程序适配中断管道流水线
调整流处理程序处理中断流水线的逻辑由以下步骤组成:
示例:调整处理电平触发的 IRQ 的处理程序
这个变化是这样写的:
修复 IRQ 芯片驱动程序
我们必须确保 由irqchip 驱动程序暴露的以下处理程序可以安全地在out-of-band上下文中运行:
对于所谓的设备中断,不需要更改,因为 genirq 层通过在对那些 irqchip 处理程序的调用中持有每个描述符 irq_desc::lock 自旋锁来确保单个 CPU 处理给定的 IRQ 事件,并且这种锁定是自动的当流水线中断时变成混合自旋锁。换句话说,这些处理程序已正确序列化,在 CPU 中禁用中断的情况下运行,正如它们的非流水线实现所期望的那样。
与设备中断不同,每个 CPU 的中断处理不需要以这种方式序列化,因为根据定义,不能有多个 CPU 争相访问此类事件。
但是,可能还有其他原因需要修复其中一些处理程序:
最初由公共中断禁用序列化的其他代码部分可能需要完全原子化才能在流水线中断模式下一致地运行。这可以通过引入硬掩码、将 local_irq_save() 调用转换为 hard_local_irq_save()、将 local_irq_restore() 转换为 hard_local_irq_restore() 来实现。
最后,必须将 IRQCHIP_PIPELINE_SAFE 添加到支持中断管道irqchip 驱动程序的 struct irqchip::flags 成员中,以便通知内核此类控制器可以在管道中断模式下运行。即使您没有引入任何其他更改来支持流水线,这也是必需的:它告诉内核您确实为此目的审查了代码。
调整 ARM GIC 驱动程序以中断流水线
调整 BCM2835 引脚控制驱动程序以中断流水线
在某些(罕见的)情况下,我们可能需要做更多的工作来调整中断芯片驱动程序。例如,我们可能必须先将休眠自旋锁转换为原始自旋锁,以便最终将后者转换为硬自旋锁。与休眠自旋锁不同,像原始自旋锁这样的硬自旋锁应该通过 raw_spin_lock() API 进行操作。
注意:
每当传递的 irqchip 描述符不带有 IRQCHIP_PIPELINE_SAFE 标志并且启用 CONFIG_IRQ_PIPELINE 时,irq_set_chip() 将发出内核警告。将此警告视为您的 IRQ 管道到目标系统的端口不完整的信号。
内核抢占控制(CONFIG_PREEMPT)
启用流水线时,Dovetail 确保 preempt_schedule_irq() 协调虚拟中断状态——在内核进入汇编级代码尚未触及时——与调度程序核心做出的基本假设,例如进入时中断被虚拟地禁止了(即 in-band阶段应该停止)。