KVM internals - GiantVM/KVM-Annotation GitHub Wiki
General Information
- 通过文件系统暴露接口,分为system level、vm level和vcpu level三级API
- 具备extension机制,基本功能记作basic,扩展功能通过
KVM_CAP_xyz表示
- KVM子系统的入口点在
/dev/kvm文件,它是一个miscdevice,通过misc_register()函数注册到内核,而misc设备本身又是char设备的一种
- KVM子系统编译成kernel module,不同的arch会编译成不同的ko文件,如Intel VT-x使用
arch/x86/kvm/vmx.c定义kernel module,其中module_init(vmx_init)确定了module的初始化函数,类似的AMD SVM使用arch/x86/kvm/svm.c定义kernel module
Initialization
- 首先调用
kvm_arch_init,以下为x86-VMX实现
- 检查CPU是否支持VMX以及是否被BIOS禁用
- alloc
struct kvm_shared_msrs数组
- 初始化mmu(
kvm_mmu_module_init, kvm_set_mmio_spte_mask, kvm_mmu_set_mask_ptes)
/dev/kvm文件 (system level)
- 文件上的
file_operations定义在kvm_chardev_ops变量,其中open由misc设备默认实现(misc_open),提供了unlocked_ioctl和compat_ioctl,由kvm_dev_ioctl实现
unlocked_ioctl是为了解决原始的ioctl必须加BKL(Big Kernel Lock)的问题而引入的过渡方案,过渡时期原本的ioctl可以加锁运行,并逐步迁移到不加锁的unlocked_ioctl(需要driver开发者自己加锁)
compat_ioctl是为了解决32位系统调用64位内核的问题,参数都是32位的,需要根据每个驱动的业务逻辑进行转换
- ioctl APIs
KVM_GET_API_VERSION
- 参数:无
- 从linux kernel 2.6开始KVM就已经稳定,版本号不再变更,故永远返回12
KVM_CREATE_VM
- 参数:machine type identifier (
KVM_VM_*)
- 返回值:一个VM fd,用于控制新建的VM,返回的VM没有对应的virtual cpus也没有内存
- 通过
kvm_dev_ioctl_create_vm(type)实现,步骤如下
- 用
kvm_create_vm(type)创建一个struct kvm
- Optional 若开启了Coalesced MMIO,则进行相关数据结构的初始化
- 创建一个文件并返回其fd,
struct kvm存放于该文件的file->private_data中
- 创建文件的步骤是先获取一个空闲的fd(通过
get_unused_fd_flags),再创建struct file(通过anon_inode_getfile),最后向fdtable注册(通过fd_install)
KVM_GET_MSR_INDEX_LIST(through kvm_dev_ioctl->kvm_arch_dev_ioctl)
- 参数:
struct kvm_msr_list (in/out)
- 该struct是一个数组,其中存放MSR index,具体实现中分成
msrs_to_save和emulated_msrs两个数组(在内核态)分别存储,并在返回用户态时合并起来。两者区别是前者不涉及虚拟化而后者涉及虚拟化
KVM_CHECK_EXTENSION
- 参数:
KVM_CAP_*
- 返回0表示不支持,1(或其它正数)表示支持
- vm level也支持该操作,事实上不同的VM可以开启不同的extension
KVM_GET_VCPU_MMAP_SIZE
- 参数:无
KVM_RUN与用户态通过一块mmap的共享内存沟通,此调用返回这块内存的大小
vm level
- ioctl APIs
KVM_CREATE_VCPU
- 参数:vCPU ID(在x86上是APIC ID)
- 返回值:一个vCPU fd,用于控制一个vCPU,
- 实现:
- 通过
kvm_arch_vcpu_create创建struct kvm_vcpu
- 在x86上是调用
kvm_x86_ops->vcpu_create,下面考察vmx_create_vcpu
- 如果有preempt notifier,对它进行初始化
- 通过
kvm_arch_vcpu_setup初始化struct kvm_vcpu
- 通过
anon_inode_getfd创建vcpu对应的fd
- 最后执行
kvm_arch_vcpu_postcreate
vcpu level
Important Structures
struct kvm
struct kvm_vcpu *vcpus[KVM_MAX_VCPUS]
KVM_MAX_VCPUS是最大允许的vCPU数目,可以通过KVM_CAP_MAX_VCPUS查询
KVM_SOFT_MAX_VCPUS是推荐的最大vCPU数目,可以通过KVM_CAP_NR_VCPUS查询
KVM_MAX_VCPU_ID是vCPU ID的最大允许值,可以通过KVM_CAP_MAX_VCPU_ID查询
atomic_t online_vcpus
- 直接通过原子指令操作,直到将
kvm_vcpu结构放入vcpus后才加1
int created_vcpus
- 通过
kvm->lock保护对其的操作,在KVM_CREATE_VCPU时加1
int last_boosted_vcpu
struct list_head vm_list
struct mutex lock
atomic_t users_count
- 指向该struct的指针是引用计数的,通过
kvm_get_kvm进行retain,通过kvm_put_kvm进行release
struct kvm_vcpu
struct kvm_run
struct vcpu_vmx
int vpid:由enable_vpid这个module param控制是否启用
bool emulation_required
Features
Coalesced MMIO