VMO 注册模式

总结

在应用和外设硬件之间传输批量数据时,最大限度地减少数据经历的副本数量非常重要。例如,假设某个应用想要从组件永久性存储空间读取文件。为此,应用会发出将文件读取到文件系统的请求,而文件系统可能需要向块设备发送请求。根据块分区拓扑,请求在最终命中可执行读取操作的驱动程序之前可能会经过多个层的驱动程序。

上述简单方法可能会导致应用与硬件之间的每一层通过 Zircon 通道发送 FIDL 消息,导致产生多个数据副本。由于这样做效率低下,因此我们不这样做。按照在整个行业中成熟的模式,我们将消息拆分为两个平面:控制平面和数据平面。通过控制平面发送的消息体量小且发送成本低,而数据平面中的消息包含批量数据,复制成本高昂。通过控制平面发送的消息通常使用基于 Zircon 通道构建而成的 FIDL 协议。数据平面中的消息通过共享内存基元 Zircon VMO 发送。

考虑到这一点,简单实现可以选择为通过控制平面传输的每个事务创建新的 VMO,直到到达发出 DMA 的驱动程序为止,从而实现预期目标,即将数据放置在 VMO 中的应用与最终驱动程序之间无副本。但是,由于以下两个原因,此测试的性能可能不够理想:

  • 如需发出 DMA 请求,您必须先固定内存,这需要调用内核,并视需要在 IOMMU 中设置页面映射。
  • 如果最终驱动程序需要将请求复制到特殊的缓冲区中(因为并非所有硬件都支持 DMA),它必须将 VMO 映射到其进程或调用内核,以便复制内存。

由于这两种方法的成本都很高,因此我们需要更好的方法:使用预注册 VMO。其工作原理是,让应用发送一次性控制消息,以便向堆栈中的最终驱动程序注册 VMO。对此消息的响应会返回一个标识符,该标识符日后可能会用于指代该 VMO。控制消息应直接引用此标识符,而不是附加 VMO 句柄。注册后,堆栈中的最终驱动程序可以执行一次成本高昂的固定或映射操作,并缓存结果。

关于 VMO 标识符的说明

为了确保我们不会受到混淆代理攻击,我们必须保持在 VMO 标识符方面与内核具有句柄的相同不变。为此,每层的客户端的 VMO 标识符都必须是唯一的,并且每一层都必须验证该标识符是否有效。更具体地说,使用 koid 作为标识符仍需要服务器检查客户端是否注册了使用该 koid 的 VMO。

为了减少往返次数,您可以允许客户端将 VMO 标识符指定为注册 API 的一部分,从而高效使用一次性 VMO。或者,协议可以声明 VMO 的 Koid 将始终用作标识符。

锆石 FIFO

为了进一步提高性能,某些协议可能还会选择为其控制平面使用 FIFO。FIFO 降低了复杂性,从而降低了开销。它们的局限性之一是可能无法转移标识名。因此,必须使用 VMO 注册模式才能使用 FIFO。(请注意,仍需使用某个渠道进行注册。)

媒体库

此模式可能会增加驱动程序的复杂性,该驱动程序用于维护 VMO 和标识符之间的映射。我们创建了一个库,用于协助实现,这些库位于 //src/lib/vmo_store 下。有关用法示例,请参阅 //src/connectivity/network/drivers/network-device/device

模式的缺点

对于吞吐量较低的情况,此模式过于复杂,很可能应避免使用。

VMO 注册会导致一次性操作变为 2 次往返。如果一次性 VMO 很常见,则 FIDL 协议应确保继续允许使用一次性 VMO,以及预注册 VMO。您还可通过允许客户端在注册期间提供 VMO 的标识符来缓解此问题。

预注册的 VMO 可能会导致内存“泄露”,即客户端不断注册 VMO,但忘记取消注册它们。此外,如果服务器在管理其客户端时不够谨慎,可能会忘记清理属于可能与服务器断开连接的客户端的已注册 VMO。

通过固定这些 VMO 的驱动程序预注册的 VMO 会导致支持该 VMO 的页面不再可分页。

特定于驱动程序的注意事项

由于某些驱动程序位于同一个驱动程序主机进程中,并且我们采用了迷你驱动程序模式,通过该模式,我们将通用逻辑提升为“核心”驱动程序,因此似乎显而易见的做法是在核心驱动程序中执行 VMO 注册,而不是在特定于设备的驱动程序中执行 VMO 注册。但这不是一个好主意,原因如下:

  • 需要通过设备专用驱动程序告知核心驱动程序是否执行固定操作或映射操作。
  • 固定需要访问由 Platform-bus 或 pci 驱动程序提供的总线事务发起者 (BTI) 句柄。将 BTI 句柄向上传递给驱动程序堆栈是一种反模式。
  • 在需要映射的情况下,这意味着原始缓冲区会通过 FIDL 传递。这是一种反模式,因为在进程内驱动程序间通信的未来迭代中,如果没有副本,可能无法再做到这一点。
  • 无论是哪种情况,如果操作是异步的(大多数情况下都是异步的),则核心驱动程序将负责确保它不会在 VMO 仍在使用中时取消固定/取消映射。在没有充分测试的关闭和挂起等情况下,此问题尤其明显。
  • 在块堆栈等情况下,核心驱动程序会以递归方式在同一驱动程序主机中多次绑定。核心驱动程序需要了解它是否直接绑定到与硬件或过滤器层进行通信的驱动程序。