通用 vcpu 接口¶
虚拟 CPU “设备” 也接受 ioctl KVM_SET_DEVICE_ATTR、KVM_GET_DEVICE_ATTR 和 KVM_HAS_DEVICE_ATTR。该接口使用与其他设备相同的 struct kvm_device_attr,但目标是 VCPU 范围内的设置和控制。
每个虚拟 CPU 的组和属性(如果有)是特定于架构的。
1. 组:KVM_ARM_VCPU_PMU_V3_CTRL¶
- 架构:
ARM64
1.1. 属性:KVM_ARM_VCPU_PMU_V3_IRQ¶
- 参数:
在 kvm_device_attr.addr 中,PMU 溢出中断的地址是指向 int 的指针
返回
-EBUSY
PMU 溢出中断已设置
-EFAULT
读取中断号时出错
-ENXIO
不支持 PMUv3,或者在尝试获取时未设置溢出中断
-ENODEV
VCPU 缺少 KVM_ARM_VCPU_PMU_V3 功能
-EINVAL
提供的 PMU 溢出中断号无效,或者尝试在不使用内核 irqchip 的情况下设置 IRQ 号。
描述此 vcpu 的 PMUv3(性能监视单元 v3)溢出中断号的值。此中断可以是 PPI 或 SPI,但每个 vcpu 的中断类型必须相同。作为 PPI,中断号对于所有 vcpu 都是相同的,而作为 SPI,它必须是每个 vcpu 的单独编号。
1.2 属性:KVM_ARM_VCPU_PMU_V3_INIT¶
- 参数:
kvm_device_attr.addr 中没有其他参数
返回
-EEXIST
中断号已使用
-ENODEV
不支持 PMUv3 或 GIC 未初始化
-ENXIO
不支持 PMUv3,缺少 VCPU 功能或未设置中断号
-EBUSY
PMUv3 已初始化
请求初始化 PMUv3。如果将 PMUv3 与内核虚拟 GIC 实现一起使用,则必须在初始化内核 irqchip 后执行此操作。
1.3 属性:KVM_ARM_VCPU_PMU_V3_FILTER¶
- 参数:
在 kvm_device_attr.addr 中,PMU 事件过滤器的地址是指向 struct kvm_pmu_event_filter 的指针
- 返回:
-ENODEV
不支持 PMUv3 或 GIC 未初始化
-ENXIO
PMUv3 配置不正确,或者内核 irqchip 未按照要求在调用此属性之前进行配置
-EBUSY
PMUv3 已初始化或 VCPU 已运行
-EINVAL
无效的过滤器范围
请求安装如下所述的 PMU 事件过滤器
struct kvm_pmu_event_filter {
__u16 base_event;
__u16 nevents;
#define KVM_PMU_EVENT_ALLOW 0
#define KVM_PMU_EVENT_DENY 1
__u8 action;
__u8 pad[3];
};
过滤器范围定义为范围 [@base_event, @base_event + @nevents),以及 @action (KVM_PMU_EVENT_ALLOW 或 KVM_PMU_EVENT_DENY)。第一个注册的范围定义全局策略(如果第一个 @action 是 DENY,则为全局 ALLOW,如果第一个 @action 是 ALLOW,则为全局 DENY)。可以编程多个范围,并且必须适合 PMU 架构定义的事件空间(在 ARMv8.0 上为 10 位,从 ARMv8.1 开始为 16 位)。
注意:“取消”过滤器通过为同一范围注册相反的操作不会更改默认操作。例如,为事件范围 [0:10) 安装 ALLOW 过滤器作为第一个过滤器,然后为同一范围应用 DENY 操作将使整个范围保持禁用状态。
限制:事件 0 (SW_INCR) 永远不会被过滤,因为它不计算硬件事件。过滤事件 0x1E (CHAIN) 也没有效果,因为它严格来说不是一个事件。可以使用事件 0x11 (CPU_CYCLES) 过滤循环计数器。
1.4 属性:KVM_ARM_VCPU_PMU_V3_SET_PMU¶
- 参数:
在 kvm_device_attr.addr 中,指向表示 PMU 标识符的 int 的地址。
- 返回:
-EBUSY
PMUv3 已初始化,VCPU 已运行或已设置事件过滤器
-EFAULT
访问 PMU 标识符时出错
-ENXIO
未找到 PMU
-ENODEV
不支持 PMUv3 或 GIC 未初始化
-ENOMEM
无法分配内存
请求 VCPU 在创建用于 PMU 仿真的访客事件时使用指定的硬件 PMU。PMU 标识符可以从 /sys/devices 下所需 PMU 实例的 “type” 文件(或等效的 /sys/bus/even_source)读取。此属性在系统上至少有两个 CPU PMU 的异构系统上特别有用。为一个 VCPU 设置的 PMU 将被所有其他 VCPU 使用。如果已存在 PMU 事件过滤器,则无法设置 PMU。
请注意,KVM 不会尝试在由此属性指定的 PMU 关联的物理 CPU 上运行 VCPU。这完全留给用户空间。但是,尝试在 PMU 不支持的物理 CPU 上运行 VCPU 将失败,并且 KVM_RUN 将返回 exit_reason = KVM_EXIT_FAIL_ENTRY,并通过将 hardare_entry_failure_reason 字段设置为 KVM_EXIT_FAIL_ENTRY_CPU_UNSUPPORTED 和 cpu 字段设置为处理器 ID 来填充 fail_entry 结构。
2. 组:KVM_ARM_VCPU_TIMER_CTRL¶
- 架构:
ARM64
2.1. 属性:KVM_ARM_VCPU_TIMER_IRQ_VTIMER、KVM_ARM_VCPU_TIMER_IRQ_PTIMER¶
- 参数:
在 kvm_device_attr.addr 中,定时器中断的地址是指向 int 的指针
返回
-EINVAL
无效的定时器中断号
-EBUSY
一个或多个 VCPU 已运行
描述连接到内核虚拟 GIC 时的架构定时器中断号的值。这些必须是 PPI(16 <= intid < 32)。设置属性会覆盖默认值(见下文)。
KVM_ARM_VCPU_TIMER_IRQ_VTIMER |
EL1 虚拟定时器 intid(默认值:27) |
KVM_ARM_VCPU_TIMER_IRQ_PTIMER |
EL1 物理定时器 intid(默认值:30) |
为不同的定时器设置相同的 PPI 将阻止 VCPU 运行。在 VCPU 上设置中断号会将当时创建的所有 VCPU 配置为使用为给定定时器提供的编号,从而覆盖其他 VCPU 上先前配置的任何值。用户空间应在创建所有 VCPU 之后并且在运行任何 VCPU 之前,在至少一个 VCPU 上配置中断号。
3. 组:KVM_ARM_VCPU_PVTIME_CTRL¶
- 架构:
ARM64
3.1 属性:KVM_ARM_VCPU_PVTIME_IPA¶
- 参数:
64 位基地址
返回
-ENXIO
未实现窃取时间
-EEXIST
已为此 VCPU 设置基地址
-EINVAL
基地址不是 64 字节对齐的
指定此 VCPU 的窃取时间结构的基地址。基地址必须是 64 字节对齐的,并且存在于有效的访客内存区域中。有关更多信息,包括窃取时间结构的布局,请参阅arm64 的准虚拟化时间支持。
4. 组:KVM_VCPU_TSC_CTRL¶
- 架构:
x86
4.1 属性:KVM_VCPU_TSC_OFFSET
- 参数:
64 位无符号 TSC 偏移量
返回
-EFAULT
读取/写入提供的参数地址时出错。
-ENXIO
不支持该属性
指定访客的 TSC 相对于主机的 TSC 的偏移量。访客的 TSC 然后通过以下公式得出
guest_tsc = host_tsc + KVM_VCPU_TSC_OFFSET
此属性对于在实时迁移时调整访客的 TSC 非常有用,以便 TSC 计算 VM 暂停期间的时间。以下描述了为此目的可能使用的算法。
从源 VMM 进程
调用 KVM_GET_CLOCK ioctl 来记录主机 TSC (tsc_src)、kvmclock 纳秒 (guest_src) 和主机 CLOCK_REALTIME 纳秒 (host_src)。
读取每个 vCPU 的 KVM_VCPU_TSC_OFFSET 属性以记录访客 TSC 偏移量 (ofs_src[i])。
调用 KVM_GET_TSC_KHZ ioctl 来记录访客 TSC 的频率 (freq)。
从目标 VMM 进程
调用 KVM_SET_CLOCK ioctl,在其各自的字段中提供来自 kvmclock (guest_src) 和 CLOCK_REALTIME (host_src) 的源纳秒。确保在提供的结构中设置 KVM_CLOCK_REALTIME 标志。
KVM 将提前 VM 的 kvmclock,以计算自记录时钟值以来经过的时间。请注意,除非 CLOCK_REALTIME 在源和目标之间同步,并且从源暂停 VM 到目标执行步骤 4-7 之间经过的时间相当短,否则这将在访客中引起问题(例如,超时)。
调用 KVM_GET_CLOCK ioctl 来记录主机 TSC (tsc_dest) 和 kvmclock 纳秒 (guest_dest)。
调整每个 vCPU 的访客 TSC 偏移量以考虑 (1) 自记录状态以来经过的时间以及 (2) 源机器和目标机器之间 TSC 的差异
- ofs_dst[i] = ofs_src[i] -
(guest_src - guest_dest) * freq + (tsc_src - tsc_dest)
(“ofs[i] + tsc - guest * freq” 是 kvmclock 中时间为 0 时对应的客户机 TSC 值。上述公式确保它在目标机上的值与在源机器上的值相同。)
使用上一步中导出的相应值,为每个 vCPU 写入 KVM_VCPU_TSC_OFFSET 属性。