通用 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 屬性。