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框架整理后,畫了以下結構圖.