通用 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 程序

  1. 呼叫 KVM_GET_CLOCK ioctl 以記錄主機 TSC (tsc_src)、kvmclock 納秒 (guest_src) 和主機 CLOCK_REALTIME 納秒 (host_src)。

  2. 讀取每個 vCPU 的 KVM_VCPU_TSC_OFFSET 屬性以記錄訪客 TSC 偏移量 (ofs_src[i])。

  3. 呼叫 KVM_GET_TSC_KHZ ioctl 以記錄訪客 TSC 的頻率 (freq)。

從目標 VMM 程序

  1. 呼叫 KVM_SET_CLOCK ioctl,在其各自的欄位中提供來自 kvmclock (guest_src) 和 CLOCK_REALTIME (host_src) 的源納秒。確保在提供的結構中設定了 KVM_CLOCK_REALTIME 標誌。

    KVM 將提前 VM 的 kvmclock 以考慮自記錄時鐘值以來經過的時間。請注意,除非 CLOCK_REALTIME 在源和目標之間同步,並且源暫停 VM 和目標執行步驟 4-7 之間經過了相當短的時間,否則這將在訪客中引起問題(例如,超時)。

  2. 呼叫 KVM_GET_CLOCK ioctl 以記錄主機 TSC (tsc_dest) 和 kvmclock 納秒 (guest_dest)。

  3. 調整每個 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 值。以上公式確保它在目標上與在源上相同)。

  4. 使用上一步中匯出的相應值寫入每個 vCPU 的 KVM_VCPU_TSC_OFFSET 屬性。