通用 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(或等效的 /sys/bus/even_source)下所需 PMU 实例的“type”文件中读取。此属性在系统上至少有两个 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 结构。
1.5 属性:KVM_ARM_VCPU_PMU_V3_SET_NR_COUNTERS¶
- 参数:
在 kvm_device_attr.addr 中,地址指向表示 PMCR_EL0.N 采用的最大值的 unsigned int
- 返回值:
-EBUSY
PMUv3 已初始化、VCPU 已运行或已设置事件过滤器
-EFAULT
访问 addr 指向的值时出错
-ENODEV
不支持 PMUv3 或未初始化 GIC
-EINVAL
未显式选择 PMUv3,或 N 值超出范围
设置虚拟 PMU 中实现的事件计数器的数量。这要求通过 KVM_ARM_VCPU_PMU_V3_SET_PMU 显式选择 PMU,并且在未显式选择 PMU 或计数器数量超出所选 PMU 的范围时将失败。选择新的 PMU 会取消设置此属性的效果。
2. 组:KVM_ARM_VCPU_TIMER_CTRL¶
- 架构:
ARM64
2.1. 属性:KVM_ARM_VCPU_TIMER_IRQ_{VTIMER,PTIMER,HVTIMER,HPTIMER}¶
- 参数:
在 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) |
KVM_ARM_VCPU_TIMER_IRQ_HVTIMER |
EL2 虚拟定时器 intid(默认:28) |
KVM_ARM_VCPU_TIMER_IRQ_HPTIMER |
EL2 物理定时器 intid(默认:26) |
为不同的定时器设置相同的 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 属性。