RFC-0270:Zircon 虚拟中断信号 | |
---|---|
状态 | 已接受 |
区域 |
|
说明 | 为虚拟中断对象提议了新信号。 |
问题 | |
Gerrit 更改 | |
作者 | |
审核人 | |
提交日期(年-月-日) | 2025-04-02 |
审核日期(年-月-日) | 2025-04-16 |
问题陈述
我们需要更好的方法来解复用共享中断,并管理虚拟中断的已确认/未确认状态。如需了解详情,请参阅下面的“扩展问题陈述”部分。
摘要
此 RFC 建议向虚拟中断对象添加一个新的 Zircon 信号,该信号在虚拟中断对象处于“未触发”状态时保持有效。
背景
实体中断与虚拟中断
Zircon 中断对象有两种类型:物理中断和虚拟中断。
物理中断对象在逻辑上表示通过系统的中断控制器传递的中断信号,有时通过 PCIe 总线传递,有时直接通过平台或 SoC 专用路由传递到控制器。与之相反,虚拟中断对象不会直接表示来自非 CPU 硬件的信号。而是由程序创建,以便看起来像物理中断,但只有当软件对对象调用 zx_interrupt_trigger
时,它们才能发出信号。
自定义等待界面
中断对象目前不使用标准 Zircon 信号系统来通知客户端已收到中断。虽然它们可以绑定到端口并在收到 IRQ 时传送端口数据包,并且可以用于在收到 IRQ 之前阻塞线程,但它们不会使用标准 zx_object_wait_one
/zx_object_wait_many
或 zx_object_wait_async
系统调用来实现此行为。相反,如果用户希望接收端口数据包,则必须使用 zx_interrupt_bind
将中断对象绑定到端口对象;如果用户希望阻塞线程,则必须使用 zx_interrupt_wait
。中断对象上一次只能有 1 个逻辑等待操作待处理,不支持任意数量的等待操作。线程取消阻塞或端口数据包传送后,需要先“确认”中断,然后才能再次发出中断信号。
当用户通过 zx_interrupt_wait
等待中断时,下次用户的线程通过再次调用 zx_interrupt_wait
在中断上阻塞时,系统会确认中断。当用户通过 zx_interrupt_bind
将中断绑定到端口时,可以通过对中断对象调用 zx_interrupt_ack
来确认中断,从而允许传送新的端口数据包。
虽然这种非标准信号接口并不是最理想的,并且我们希望设计出与标准 Zircon 信号机制兼容的新接口(除了解决其他痛点之外),但完全重新设计 Zircon 中断 API 并过渡到新系统是一项复杂的工作,可能需要花费大量的时间和精力,并且超出了此提案的有限范围。
虚拟中断的用途
虚拟中断在 Fuchsia 中具有两个主要用途,这两个用途源自相同的动机。这些是测试和解调。
由于驱动程序需要使用中断对象使用的特殊等待接口,因此很难轻松地将其他内核对象(可能是事件)替换为中断对象,并让驱动程序代码“正常运行”。在编写驱动程序测试时,如果测试环境想要模拟硬件,就会出现问题。无法向驱动程序提供任何物理中断,即使有,也可能很难针对任何给定测试控制此类中断,并且非常依赖于目标平台。
而是可以使用虚拟中断对象。从驱动程序使用方的角度来看,它们使用相同的 API,并且基本上无法区分,这样一来,您无需任何能够感知测试环境与真实环境之间差异的特殊情况代码,即可测试代码。
当需要对单个中断进行解复用时,也可能会出现类似的情况。假设有一个需要发出中断的外部芯片的驱动程序。我们希望为外部芯片使用单个驱动程序,该驱动程序独立于使用外部芯片的特定产品中使用的特定 SoC。该芯片可能具有连接到 SoC 的 IRQ 信号。在某些 SoC 设计中,此 IRQ 信号可能会直接路由到 SoC 的中断控制器,这意味着可以在初始化期间实例化专用物理中断并将其传递给驱动程序。只要管理中断只需要与系统的中断控制器进行交互,内核就可以通过物理中断对象直接管理 IRQ。
另一方面,其他设计可能会将此中断连接到 GPIO,作为中断块(通常为 16 或 32 个中断)的一部分。当配置为中断的块中的任何 GPIO 收到信号时,系统会触发 GPIO 硬件的物理中断。然后,GPIO 驱动程序可以唤醒并确定哪个 GPIO 中断已触发,并屏蔽中断,直到可以进行服务为止。不过,它仍需要向发出此中断的驱动程序发出信号。我们不希望 irq-consumer-driver 需要知道这些情况之间的区别,也不希望仅仅因为开发板设计将多个中断路由到同一 GPIO 块,就需要在同一进程中存在多个用于不同外部芯片的驱动程序。因此,在这种情况下,GPIO 驱动程序可以为共享硬件中配置为中断的每个 GPIO 创建虚拟中断对象,从而有效地对共享同一物理中断的多个中断进行解调,而无需驱动程序具有用于以不同的方式处理这两种情况的代码。
扩展问题陈述
不过,目前这种方法存在一个实际问题。考虑 GPIO 解调场景。多个 GPIO 中断共享同一块硬件,并且驱动程序为每个中断创建并分发了一个虚拟中断。当其中一个中断触发时,系统会通过该块的共享物理中断向 GPIO 发送信号。然后,GPIO 驱动程序可以使用 SoC 专用 GPIO 硬件屏蔽特定位,并zx_interrupt_trigger
关联的虚拟中断。现在,所有待处理的 GPIO 中断都已发出信号,它可以确认自己的物理中断,然后返回等待新的中断被断言。
在驱动程序/使用方,系统会唤醒中断处理程序,处理中断,最后进行确认。此时,GPIO 驱动程序需要采取行动。具体而言,它需要先唤醒,然后再取消屏蔽/重新启用 GPIO 块中的特定位,然后才能发出任何新中断信号。
不过,目前还没有很好的方法来实现这一点。系统不会通过中断对象从客户端驱动程序发送任何信号到 GPIO 驱动程序,但如果没有这样做,GPIO 驱动程序就无法很好地知道何时可以安全地取消屏蔽和重新启用中断。
可以引入其他对象或建立协议来提供此类信号,但这需要中断使用方知道这是虚拟中断而非物理中断,这会破坏使这两种场景在实际上无法区分的目标。
利益相关方
协调员:cpu@google.com
审核员: + 'maniscalco@google.com' + 'bradenkell@google.com' + 'rudymathu@google.com' + 'cpu@google.com'
已咨询:
Zircon 驱动程序框架团队。
社交:
此提案已与 Zircon 团队分享(采用 Google 文档格式)。
要求
使用中断对象的“下游”驱动程序必须不考虑它是否为虚拟对象。
设计
中断对象和标准 Zircon 信号
如前所述,Zircon 中断对象目前不使用标准 Zircon 信号系统来发送来自硬件的中断请求信号。这并不意味着它们完全不参与标准信号系统。用户可以自由地发布异步等待来中断对象,也可以使用 zx_object_wait_one
/zx_object_wait_many
阻塞对象上的线程。尽管如此,要想满足任何这些等待操作,唯一的方法是等待几乎每个 Zircon 对象上存在的 8 个“用户信号”之一,并且软件断言该信号。目前,还没有为中断对象定义任何“系统信号”,以便内核发出信号。
ZX_VIRTUAL_INTERRUPT_UNTRIGGERED
因此,我们引入一个新信号(称为 ZX_VIRTUAL_INTERRUPT_UNTRIGGERED
),并仅将其用于虚拟中断。以下是信号的基本属性:
- 虚拟中断对象在创建后最初不会触发,因此系统会对任何新创建的虚拟中断对象断言此信号。
- 当用户对虚拟中断对象调用
zx_interrupt_trigger
时,该对象会进入触发状态,并且系统会取消断言信号。 - 当用户确认已触发的虚拟中断对象时,该对象会进入未触发状态,并且系统会断言信号。
实际使用示例
假设有一个 GPIO 驱动程序使用物理中断 I
,并在同一 GPIO 块中公开三个虚拟中断 A
、B
和 C
。
- 在启动时,驱动程序会创建并分发
A
、B
和C
,然后将I
绑定到端口P
。 - 它还会创建专用 IRQ 线程 (
T
),配置适当的配置文件,然后在P
中阻塞T
以等待数据包。 I
触发将数据包传送到P
的操作,并在此过程中取消屏蔽T
。T
会检查 GPIO 状态,并发现与A
和C
关联的 GPIO 已被断言。T
对A
和C
调用zx_interrupt_trigger
。A
和C
进入触发状态,并在此过程中清除ZX_VIRTUAL_INTERRUPT_UNTRIGGERED
。T
会对A
和C
调用zx_object_async_wait
,将它们与其端口相关联,并等待ZX_VIRTUAL_INTERRUPT_UNTRIGGERED
在虚拟中断对象上被断言。T
会掩盖与A
和C
关联的 GPIO。T
会对I
调用zx_interrupt_ack
,然后再次在端口上阻塞,等待新数据包。- 在某个时间点,使用
C
的驱动程序会唤醒,处理中断,然后对C
的句柄调用zx_interrupt_ack
,并作为副作用断言ZX_VIRTUAL_INTERRUPT_UNTRIGGERED
。 - 等待
C
未触发信号的异步等待操作已满足条件,因此系统会生成端口数据包并将其传送到P
,从而取消阻塞T
。 T
唤醒,并注意到C
已变为“未触发”,因此它使用其硬件专用寄存器来取消屏蔽并重新启用与C
关联的 GPIO 中断。T
会简单地返回到其端口进行等待。当I
再次有效或A
的ZX_VIRTUAL_INTERRUPT_UNTRIGGERED
信号有效时,我们将收到端口数据包。您目前无需采取进一步行动。
确认待处理的 interrupt
对于某些中断对象,系统可能会触发中断,并且在确认时还有第二个 IRQ 正在等待。虽然这在 MSI 中断中最为典型,但也可以通过虚拟中断对象生成这种情况,只需在客户端设法处理中断之前连续调用两次 trigger 即可。
当用户确认有其他 IRQ 待处理的某个中断时,中断对象会立即有效触发。通过 zx_interrupt_wait
确认的线程将立即取消阻塞。通过 zx_interrupt_ack
确认线程会导致立即生成新的端口数据包。
UNTRIGGERED
信号会发生什么情况?由于中断在逻辑上未触发,然后立即重新触发,因此信号的行为应为闪烁行为。也就是说,当前正在等待 UNTRIGGERED
信号的所有线程都应取消阻塞,并且当前发布的所有异步等待操作都应满足并生成端口数据包。不过,在确认操作后立即的信号状态将被取消断言。
实现
实现此方法预计会很简单且风险较低。内核代码目前有两个中断对象调度程序,即 InterruptDispatcher
和 VirtualInterruptDispatcher
,其中第二个是第一个的子类。虽然它们共享大量代码,但接收信号的路径是分开的。InterruptDispatcher
会在硬 IRQ 时间通过 InterruptHandler
方法发出信号,而虚拟中断会在 zx_interrupt_trigger
ed 时间通过 Trigger
方法发出信号。对 InterruptDispatcher
调用 Trigger
是非法的,否则会导致 BAD_STATE
错误。因此,触发操作仅对虚拟中断对象执行,并且始终在系统调用上下文中执行,在该上下文中,可以合法地获取操控对象信号状态所需的锁。
在下次调用 zx_interrupt_wait
(对于非端口绑定的中断)或调用 zx_interrupt_ack
(对于端口绑定的中断)时,系统会确认中断。同样,与 zx_interrupt_trigger
一样,此调用是在系统调用上下文中进行的,以允许信号操作。
最后,当最后一个中断句柄关闭或线程调用 zx_interrupt_destroy
时,中断会被销毁。再次强调,所有这些都发生在可以获取内核互斥量并断言用户信号的上下文中,这样便可在显式销毁对象期间轻松断言未触发的信号(取消阻塞所有等待器)。
因此,实现此功能只需在以下四个操作期间重载 VirtualInterruptDispatcher
的行为即可:触发、确认、等待和销毁。过载的版本可能会在执行操作本身之前获取调度程序锁,并在操作结束时根据需要更新信号状态。
就是这样。UpdateState
和现有的标准 Zircon 信号机制已处理了解除线程阻塞和传送端口数据包的所有复杂繁重工作。实体中断对象不会公开信号,也不会使用任何虚拟调度程序代码,从而避免了需要将任何信号状态与在硬中断期间执行的操作同步。
工效学设计
这项功能的人体工学设计应简单且可接受。无需对现有中断使用方进行任何更改,ACK 信号行为完全自动化。虚拟中断生产者只需使用现有的完善信号处理模式来使用信号。
安全注意事项
添加此功能有效地引入了从中断使用方到中断生产方的全新通信渠道,让生产方能够知道使用方何时确认了虚拟中断。这是多路复用中断系统正常运行的要求之一,不被视为单独的或不必要的边信道,因此也不构成安全风险。
隐私注意事项
此功能不会处理任何用户数据或其他可能敏感的信息,因此无需考虑任何隐私保护事项。
测试
测试应是对现有单元测试的简单扩展。我们基本上需要添加测试来确保:
- 创建虚拟中断对象时,系统会先断言
ZX_VIRTUAL_INTERRUPT_UNTRIGGERED
信号。 - 系统会在响应对
zx_interrupt_trigger
的调用时清除信号。 - 系统会在响应对
zx_interrupt_ack
的调用时设置信号。 - 当存在其他待处理的 IRQ 时,信号在收到确认时会正确实现“触发”行为。
文档
虚拟中断对象的文档将更新为介绍新信号,说明它仅与虚拟中断对象一起使用,并总结上述行为规则(最初断言,触发时清除,ACK 时设置,待处理 ACK 时闪烁)。
缺点、替代方案和未知情况
这种方法没有已知的重大缺点。满足用户要求。实现和测试应简单且风险低。
Zircon 驱动程序团队已经讨论了多种替代方案,但所有这些方案都存在相同的缺点。虚拟中断的驱动程序使用方必须明确意识到自己正在使用虚拟中断,然后明确参与某种用户级定义的协议,以向中断生产方发送确认信号。
虽然所有这些方法都是可行的,但似乎都不太理想。它们会给驱动程序作者带来额外的负担,因为他们需要在使用虚拟中断时参与已定义的任何协议,并在使用物理中断时采用不同的行为方式。此外,已有一批现有驱动程序需要更新才能使用新协议,如果内核以透明的方式处理 ack 信号,则可以完全跳过此步骤。
无论实现哪种协议,都将完全在用户模式下实现,因此需要仔细考虑可能由潜在恶意行为者引发的信任问题。让信号机制由 Zircon 内核中介有助于简化此处的分析。
最后,Zircon 中断的用户已经必须确认其中断(隐式或显式)。很难想象,任何额外的带外确认协议最终能否达到与在现有确认发生时仅操作内核对象的信号位一样高的效率。