虚拟机间共享内存设备规范

虚拟机间共享内存设备(ivshmem)用于在运行不同客户机的多个 QEMU 进程及主机之间 共享一段内存区域。为使所有客户机都能取得该共享内存区域,QEMU 将其建模为 PCI 设备,通过 PCI BAR 向客户机暴露这段内存。

设备可以直接使用主机上的共享内存对象,也可以从 ivshmem 服务器获取对象。

后一种情况下,设备还可以向其他对等设备发送中断,并接收它们发来的中断。

有关通过 QEMU 命令行配置 ivshmem 设备的信息,请参阅 虚拟机间共享内存设备。

ivshmem PCI 设备的客户机接口

设备的厂商 ID 为 1af4,设备 ID 为 1110,修订版本为 1。在 QEMU 2.6.0 之前, 修订版本为 0。

PCI BAR

ivshmem PCI 设备有两个或三个 BAR:

  • BAR0 包含设备寄存器(256 字节 MMIO)

  • BAR1 包含 MSI-X 表和 PBA(仅 ivshmem-doorbell)

  • BAR2 映射共享内存对象

此设备有两种使用方式:

  • 如果只需要共享内存功能,BAR2 即可满足需求。客户机可由此访问共享内存, 并按需使用。

  • 如果还需要对等设备相互发送中断的能力,就需要 BAR0 和 BAR1。通常需要编写 内核驱动来处理中断。当然,设备必须配置为支持中断。

在 QEMU 2.6.0 之前,如果设备配置为支持中断,BAR2 起初可能无效。只有在 ivshmem 服务器提供共享内存后,才能安全地访问它。这些设备的 PCI 修订版本为 0,而不是 1。 客户机软件应等待 IVPosition 寄存器(见下文)的值变为非负数,再访问 BAR2。

修订版本 0 的设备无法告知客户机软件其是否配置为支持中断。

PCI 设备寄存器

BAR 0 包含以下寄存器:

偏移    大小  访问        复位值    功能
    0     4   读/写            0   Interrupt Mask
                                    位  0: 对等方中断     (rev 0)
                                           保留           (rev 1)
                                    位  1..31: 保留
    4     4   读/写            0   Interrupt Status
                                    位  0: 对等方中断     (rev 0)
                                           保留           (rev 1)
                                    位  1..31: 保留
    8     4   只读        0 或 ID   IVPosition
   12     4   只写            N/A   Doorbell
                                    位  0..15: 向量
                                    位  16..31: 对等方 ID
   16   240   无              N/A   保留

软件只能按“访问”列规定的方式访问寄存器。读取时应忽略保留位,写入时应保留其值。

对于修订版本 0 的设备,在没有 MSI-X 能力时,Interrupt Status 和 Mask 寄存器共同 控制传统 INTx 中断:当 Status 与 Mask 按位与的结果非零且设备没有 MSI-X 能力时, 拉高 INTx。收到对等设备的中断请求时,Interrupt Status 寄存器的位 0 变为 1。 读取该寄存器会将它清零。

IVPosition 寄存器:若设备未配置中断,其值为零。否则为设备 ID(0 至 65535)。

在 QEMU 2.6.0 之前,复位后短时间内读取该寄存器可能得到 -1。这些设备的 PCI 修订版本为 0,而不是 1。

软件没有可靠的方法确定设备是否配置了中断。IVPosition 为正数意味着支持中断, 但为零时两种情况都有可能。

Doorbell 寄存器:写入该寄存器会请求向对等设备发送中断。写入值的高 16 位指定 目标对等设备的 ID,低 16 位选择中断向量。

如果设备未配置中断,则忽略写入。

如果中断尚未完成设置,则忽略写入。设备无法告知客户机软件设置是否完成。 迁移时,中断可能退回到此状态。

如果所请求 ID 的对等设备未连接,或其已连接的中断向量数不足,则忽略写入。 设备无法告知客户机软件哪些对等设备已连接,或已连接多少中断向量。

随后,对等设备上该向量的中断变为待处理状态。软件无法清除待处理位,因此无法 采用轮询方式操作。

如果对等设备为没有 MSI-X 能力的修订版本 0 设备,其 Interrupt Status 寄存器 置为 1。除非 Interrupt Mask 寄存器将其屏蔽,否则会拉高 INTx。在这种情况下, 设备无法向客户机软件传达中断向量。

存在多个 MSI-X 向量时,可使用不同向量表示发生了不同事件。中断向量的语义由应用决定。

中断基础设施

配置中断后,对等设备除共享内存外,还共享 eventfd 对象。共享资源由 ivshmem 服务器管理。

ivshmem 服务器

服务器监听 UNIX 域套接字。

对于每个新连接的客户端,服务器会:

  • 选取一个 ID,

  • 为中断向量创建 eventfd 文件描述符,

  • 向新客户端发送 ID 和共享内存的文件描述符,

  • 向其他客户端发送新客户端的连接通知(包含发送中断所需的文件描述符),

  • 向新客户端发送其他客户端的连接通知,以及

  • 向新客户端发送中断设置消息(包含接收中断所需的文件描述符)。

第一个连接服务器的客户端获得 ID 零。

客户端断开连接时,服务器向其他客户端发送断开通知。

下一节详细说明协议。

如果服务器在未发送其已连接客户端的断开通知的情况下终止,客户端可以选择继续运行。 它们仍可正常相互通信,但断开时不会收到断开通知,也无法连接新客户端。客户端无法 连接到重启后的服务器。设备无法告知客户机软件服务器是否仍在运行。

示例服务器代码位于 contrib/ivshmem-server/,不可用于生产环境。它假定所有客户端 使用相同数量的中断向量。

独立客户端位于 contrib/ivshmem-client/,可用于调试。

ivshmem 客户端—服务器协议

配置为支持中断的 ivshmem 设备会连接到 ivshmem 服务器。本节详细介绍两者之间的协议。

连接是单向的:服务器向客户端发送消息。每条消息包含一个 8 字节的小端有符号数, 并且可以通过 SCM_RIGHTS 附带一个文件描述符。客户端和服务器遇到错误时均关闭连接。

注意:QEMU 目前不会在出错时立即关闭连接,而是等到字符设备销毁时才关闭。

连接建立后,服务器按顺序发送以下消息:

  1. 协议版本号,目前为零。客户端收到无法处理的版本号时,应关闭连接。

  2. 客户端 ID,在此服务器的所有客户端中唯一。ID 必须位于 0 至 65535 之间, 因为 Doorbell 寄存器仅为它提供 16 位。

  3. 数字 -1,并附带共享内存的文件描述符。

  4. 其他现有客户端的连接通知(如有)。消息为一个对等设备 ID(0 至 65535 之间且 不同于自身 ID 的数),重复 N 次。每次附带一个文件描述符,依次用于通过向量 0,..,N-1 向该 ID 的对等设备发送中断。如果客户端配置的向量较少,就关闭多余的 文件描述符;如果配置的向量较多,多余的向量保持未连接状态。

  5. 中断设置。消息为客户端自身的 ID,重复 N 次。每次附带一个文件描述符,依次用于 通过向量 0,..,N-1 接收对等设备的中断。如果客户端配置的向量较少,就关闭多余的 文件描述符;如果配置的向量较多,多余的向量保持未连接状态。

此后,服务器发送以下类型的消息:

  1. 连接/断开通知,内容为一个对等设备 ID。

  • 如果该数字附带文件描述符,则为连接通知,与第 4 步完全相同。

  • 否则,为该 ID 对等设备的断开通知。

已知问题:

  • 协议在 QEMU 2.5 中发生了不兼容的变化。此前消息采用本机字节序的 long, 且没有版本号。

  • 协议设计较差。

ivshmem 客户端间协议

如上一节所述,配置为支持中断的 ivshmem 设备从服务器接收 eventfd 文件描述符, 用来向对等设备发送中断,以及接收对等设备的中断。

要向对等设备发送中断,设备以本机字节序向相应文件描述符写入 8 字节整数 1。

要接收中断,设备尽可能多地读取并丢弃 8 字节整数。