-
Notifications
You must be signed in to change notification settings - Fork 42
KVM internals
Zhuocheng Ding edited this page Mar 24, 2017
·
1 revision
- 通过文件系统暴露接口,分为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
- 首先调用
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
)
- 文件上的
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也没有内存
- 创建的fd设置了close-on-exec
- 通过
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
)
- 创建文件的步骤是先获取一个空闲的fd(通过
- 用
- 参数:machine type identifier (
-
KVM_GET_MSR_INDEX_LIST
(throughkvm_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的共享内存沟通,此调用返回这块内存的大小
-
- ioctl APIs
-
KVM_CREATE_VCPU
- 参数:vCPU ID(在x86上是APIC ID)
- 返回值:一个vCPU fd,用于控制一个vCPU,
- 创建的fd设置了close-on-exec
- 实现:
- 通过
kvm_arch_vcpu_create
创建struct kvm_vcpu
- 在x86上是调用
kvm_x86_ops->vcpu_create
,下面考察vmx_create_vcpu
- 在x86上是调用
- 如果有preempt notifier,对它进行初始化
- 通过
kvm_arch_vcpu_setup
初始化struct kvm_vcpu
- 通过
anon_inode_getfd
创建vcpu对应的fd - 最后执行
kvm_arch_vcpu_postcreate
- 通过
-
- 每个VM实例对应一个
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的指针是引用计数的,通过
struct kvm_vcpu vcpu
-
int vpid
:由enable_vpid
这个module param控制是否启用 bool emulation_required