Virtio:针对 Linux 的 I/O 虚拟化框架(2)框架
- UID
- 1066743
|
Virtio:针对 Linux 的 I/O 虚拟化框架(2)框架
Virtio 架构除了前端驱动程序(在来宾操作系统中实现)和后端驱动程序(在 hypervisor 中实现)之外,virtio 还定义了两个层来支持来宾操作系统到 hypervisor 的通信。在顶级(称为 virtio)的是虚拟队列接口,它在概念上将前端驱动程序附加到后端驱动程序。驱动程序可以使用 0 个或多个队列,具体数量取决于需求。例如,virtio 网络驱动程序使用两个虚拟队列(一个用于接收,另一个用于发送),而 virtio 块驱动程序仅使用一个虚拟队列。虚拟队列实际上被实现为跨越来宾操作系统和 hypervisor 的衔接点。但这可以通过任意方式实现,前提是来宾操作系统和 hypervisor 以相同的方式实现它。
图 3. vital 框架的高级架构如 图 3 所示,分别为块设备(比如磁盘)、网络设备、PCI 模拟和 balloon 驱动程序列出了 5 个前端驱动程序。每个前端驱动程序在 hypervisor 中有一个对应的后端驱动程序。
概念层次结构从来宾操作系统的角度来看,对象层次结构 的定义如 图 4 所示。在顶级的是 virtio_driver,它在来宾操作系统中表示前端驱动程序。与该驱动程序匹配的设备由 virtio_device(设备在来宾操作系统中的表示)封装。这引用 virtio_config_ops 结构(它定义配置 virtio 设备的操作)。virtio_device 由 virtqueue 引用(它包含一个到它服务的 virtio_device 的引用)。最后,每个 virtqueue 对象引用 virtqueue_ops 对象,后者定义处理 hypervisor 的驱动程序的底层队列操作。尽管队列操作是 virtio API 的核心,我还是先简单讨论一下新的发现,然后再详细探讨 virtqueue_ops 操作。
图 4. virtio 前端的对象层次结构该流程以创建 virtio_driver 并通过 register_virtio_driver 进行注册开始。virtio_driver 结构定义上层设备驱动程序、驱动程序支持的设备 ID 的列表、一个特性表单(取决于设备类型)和一个回调函数列表。当 hypervisor 识别到与设备列表中的设备 ID 相匹配的新设备时,将调用 probe 函数(由 virtio_driver 对象提供)来传入 virtio_device 对象。将这个对象和设备的管理数据缓存起来(以独立于驱动程序的方式缓存)。可能要调用 virtio_config_ops 函数来获取或设置特定于设备的选项,例如,为 virtio_blk 设备获取磁盘的 Read/Write 状态或设置块设备的块大小,具体情况取决于启动器的类型。
注意,virtio_device 不包含到 virtqueue 的引用(但 virtqueue 确实引用了 virtio_device)。要识别与该 virtio_device 相关联的 virtqueue,您需要结合使用 virtio_config_ops 对象和 find_vq 函数。该对象返回与这个 virtio_device 实例相关联的虚拟队列。find_vq 函数还允许为 virtqueue 指定一个回调函数(查看 图 4 中的 virtqueue 结构)。
virtqueue 是一个简单的结构,它识别一个可选的回调函数(在 hypervisor 使用缓冲池时调用)、一个到 virtio_device 的引用、一个到 virtqueue 操作的引用,以及一个引用要使用的底层实现的特殊 priv 引用。虽然 callback 是可选的,但是它能够动态地启用或禁用回调。
该层次结构的核心是 virtqueue_ops,它定义在来宾操作系统和 hypervisor 之间移动命令和数据的方式。让我们首先探索添加到或从 virtqueue 移除的对象。
Virtio 缓冲池来宾操作系统(前端)驱动程序通过缓冲池与 hypervisor 交互。对于 I/O,来宾操作系统提供一个或多个表示请求的缓冲池。例如,您可以提供 3 个缓冲池,第一个表示 Read 请求,后面两个表示响应数据。该配置在内部被表示为一个散集列表(scatter-gather),列表中的每个条目表示一个地址和一个长度。
核心 API通过 virtio_device 和 virtqueue(更常见)将来宾操作系统驱动程序与 hypervisor 的驱动程序链接起来。virtqueue 支持它自己的由 5 个函数组成的 API。您可以使用第一个函数 add_buf 来向 hypervisor 提供请求。如前面所述,该请求以散集列表的形式存在。对于 add_buf,来宾操作系统提供用于将请求添加到队列的 virtqueue、散集列表(地址和长度数组)、用作输出条目(目标是底层 hypervisor)的缓冲池数量,以及用作输入条目(hypervisor 将为它们储存数据并返回到来宾操作系统)的缓冲池数量。当通过 add_buf 向 hypervisor 发出请求时,来宾操作系统能够通过 kick 函数通知 hypervisor 新的请求。为了获得最佳的性能,来宾操作系统应该在通过 kick 发出通知之前将尽可能多的缓冲池装载到 virtqueue。
通过 get_buf 函数触发来自 hypervisor 的响应。来宾操作系统仅需调用该函数或通过提供的 virtqueue callback 函数等待通知就可以实现轮询。当来宾操作系统知道缓冲区可用时,调用 get_buf 返回完成的缓冲区。
virtqueue API 的最后两个函数是 enable_cb 和 disable_cb。您可以使用这两个函数来启用或禁用回调进程(通过在 virtqueue 中由 virtqueue 初始化的 callback 函数)。注意,该回调函数和 hypervisor 位于独立的地址空间中,因此调用通过一个间接的 hypervisor 来触发(比如 kvm_hypercall)。
缓冲区的格式、顺序和内容仅对前端和后端驱动程序有意义。内部传输(当前实现中的连接点)仅移动缓冲区,并且不知道它们的内部表示。 |
|
|
|
|
|