kvm模塊的加載
arch/x86/kvm/vmx/vmx.c
載入kvm模塊時,調用順序為module_init()->vmx_init()->kvm_init(),所以kvm_init()為核心函數
int kvm_init()
{
...
/*
/* 1. 將vmx_x86_ops重命名為kvm_x86_ops
/* 2. 檢查cpu是否支持kvm
/* 3. 檢查是否包含FPU Feature,並為FPU分配存儲空間
/* 4. 為所有CPU分配共享MSR
/* 5. 初始化及設置MMU
/* 6. 設置一個回調函數,用於獲取guest信息
/* 7. 初始化LAPIC
*/
r = kvm_arch_init(opaque); // 這里的opaque為一個結構體(vmx_x86_ops),包含一系列kvm主要操作
...
/*
/* 1. 對kvm_x86_ops結構中與硬件相關的內容進行初始化賦值
/* 2. 為每個cpu申請vmcs空間
/* 3. 設置TSC
*/
r = kvm_arch_hardware_setup();
}
kvm模塊加載完成后,在系統的/dev目錄下出現kvm設備,用於外界與kvm交互
kvm模塊向外界提供的ioctl分類
virt/kvm/kvm_main.c
分為3大類,大多都在kvm_main.c中實現,剩余一小部分在arch/x86/kvm/vmx/vmx.c中.3類ioctl分別為設備、虛擬機、虛擬cpu級別.3個層次逐級概念變小,虛擬機由設備層面的ioctl創建,而虛擬cpu由虛擬機級別的ioctl創建.
設備級別ioctl
以kvm_dev_ioctl為前綴,負責以下通用設備級別的ioctl功能的處理
- [ ] KVM_GET_API_VERSION
- [ ] KVM_CREATE_VM
- [ ] KVM_CHECK_EXTENSION
- [ ] KVM_GET_VCPU_MMAP_SIZE
- [ ] KMV_TRACE_ENABLE/PAUSE/DISABLE
其余設備級別的ioctl功能與架構有關,因此在kvm_arch_dev_ioctl()中實現.
虛擬機級別ioctl
以kvm_vm_ioctl為前綴,負責以下通用虛擬機級別的ioctl功能的處理
- [ ] KVM_CREATE_VCPU
- [ ] KVM_ENABLE_CAP
- [ ] KVM_SET_USER_MEMORY_REGION
- [ ] KVM_GET/CLEAR_DIRTY_LOG
- [ ] KMV_REGISTER/UNREGISTER_CONESCED_MMIO
- [ ] KVM_IRQFD
- [ ] KVM_IOEVENTFD
- [ ] KVM_SIGNAL_MSI
- [ ] KVM_IRQ_LINE_STATUS
- [ ] KVM_IRQ_LINE
- [ ] KVM_SET_GSI_DORTINE
- [ ] KVM_CREATE_DEVICE
- [ ] KVM_CHECK_EXTENSION
其余虛擬機級別的ioctl功能與架構有關,因此在kvm_arch_vm_ioctl()中實現.
虛擬CPU級別ioctl
以kvm_vcpu_ioctl為前綴,負責以下通用虛擬CPU級別的ioctl功能的處理
- [ ] KVM_RUN
- [ ] KVM_SET/GET_REGS
- [ ] KVM_SET/GET_SPGGS
- [ ] KVM_SET/GET_MPSTATE
- [ ] KVM_TRANSLATE
- [ ] KVM_SET_GUEST_DEBUG
- [ ] KVM_SET_SIGNAL_MASK
- [ ] KVM_SET/GET_FPU
其余虛擬CPU級別的ioctl功能與架構有關,因此在kvm_arch_vcpu_ioctl()中實現.
kvm中虛擬機的創建流程
主要創建流程就是dev->vm->vcpu,這期間最重要的數據結構為kvm_x86_ops(在vmx.c中定義),但是在ioctl函數中展現的形式與實際調用的函數形式有些許不同. 如在創建vm時,函數調用流程為:
kvm_dev_ioctl_create_vm() -> kvm_create_vm() -> kvm_arch_alloc_vm()
在kvm_arch_alloc_vm()中,具體實現只有一句return kvm_x86_ops->vm_alloc(),此時使用vim插件無法找到vm_alloc()的定義,但在vmx.c中可以看到類似.vm_alloc = vmx_vm_alloc, 因此其實vm_alloc()調用的是vmx_vm_alloc,其它kvm_x86_ops的子方法也可以使用類似的方法找到.
創建vm
kvm_dev_ioctl_create_vm() -> kvm_create_vm() -> kvm_arch_alloc_vm() -> vmx_vm_alloc()
| -> kvm_arch_init_vm() | -> vmx_vm_init()
因此可以通過調用kvm_dev_ioctl_create_vm()完成對一個vm的內存分配和初始化,最終返回的是一個kvm_vmx結構的結構.
創建vCPU
kvm_vm_ioctl_create_vcpu() -> kvm_arch_vcpu_create() -> vmx_create_vcpu()
| -> kvm_arch_vcpu_setup() -> vmx_vcpu_setup()
| -> create_vcpu_fd
kvm_arch_vcpu_create()為vcpu_vmx結構申請空間,初始化vcpu,為guest_msr,vmcs結構申請空間,並利用vmx_vcpu_setup()將vmcs設置為實模式狀態,利用vmx_vcpu_put()准備將vcpu切換至host狀態.
運行vCPU
kvm_vcpu_ioctl(...,KVM_RUN,...) -> kvm_arch_vcpu_ioctl_run() -> vcpu_run() -> vcpu_enter_guest()|
-> need_resched() <-|
vcpu_enter_guest()使虛擬vcpu進入non-root操作,退出該函數等效於執行了VM exit,如果退出該函數時返回1,即代表guest無法處理本次exit reasion,需要進入到用戶空間(qemu),使用qemu對本次exit reason進行處理,處理完成之后再進入vcpu_enter_guest().
QEMU與KVM的交互
QEMU的ioctl
由於這部分與qemu有交互的部分,因此我提前對qemu進行了學習.
qemu與kvm對應有4大類的ioctl,用於對kvm提供的3個級別的fd進行控制.
- kvm_device_ioctl()
用於對device_fd(/dev/kvm)進行操作 ---> hypervisor
- kvm_ioctl()
用於對KVMState結構體中的fd進行操作 ---> 對應kvm的device級別
- kvm_vm_ioctl()
用於對KVMState結構體中vmfd進行操作 ---> 對應kvm的vm級別
- kvm_vcpu_ioctl()
用於對CPUState結構體中的kvm_fd進行操作 ---> 對應kvm的vcpu級別
kvm的kvm_vcpu結構體中含有kvm_run結構,用於QEMU的用戶空間和kvm模塊的交互. 例如在VM exit時,為了對虛擬硬件的訪問進行模擬,kvm必須返回到QEMU用戶空間中,因此kvm將相關信息存儲到kvm_run結構中,留給QEMU獲取信息.
【update】 2019.11.22
QEMU配置加速器(kvm)
qemu使用了大量面向對象的編程方式,並用c語言實現了類和類的構建、析構函數等。
qemu將kvm定義為一種加速類型(AccelClass),注冊到了type_table中,因此通過初始化對象就可以直接調用對象所屬類的方法。
AccelClass的init_machine()方法可以獲得vm_fd.
QEMU設置vCPU
與加速器類似,vCPU也被設計為一種CPU類型(x86_vcpu_type), 將qemu的main函數中的current_machine->type指向x86_vcpu_type,即可調用該類的方法使用vCPU.
x86_vcpu_type的x86_vcpu_common_class_init()方法可以獲得vCPU的inode(qemu中稱為kvm_fd).
值得注意的是qemu為每個vCPU申請了一個線程(thread),所占空間在qemu本身在host上所占的空間中.
QEMU的main函數中與kvm有關的部分
QEMU的main函數中與kvm有關的部分對應的為配置加速器和設置vCPU兩部分,以及最后的循環運行vCPU,主要函數為:
configure_accelerator();
current_machine->cpu_type的賦值;
main_loop();
我對qemu-kvm框架整理后,畫了以下結構圖.

