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_ioctlcompat_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
    • KVM_GET_MSR_INDEX_LIST(through kvm_dev_ioctl->kvm_arch_dev_ioctl
      • 参数:struct kvm_msr_list (in/out)
      • 该struct是一个数组,其中存放MSR index,具体实现中分成msrs_to_saveemulated_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,
        • 创建的fd设置了close-on-exec
      • 实现:
        • 通过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

  • 每个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 kvm_vcpu

struct kvm_run

struct vcpu_vmx

  • struct kvm_vcpu vcpu

  • int vpid:由enable_vpid这个module param控制是否启用
  • bool emulation_required

Features

Coalesced MMIO