Friday, February 7, 2020

kvm_all.c - kvm_init, kvm_init_vcpu

继续接着前面的东西来,了解QEMU如何在KVM虚拟化环境中创建和初始化虚拟机和vCPU。

1. kvm_init
kvm_init函数用于打开KVM设备文件,如下所示,它还填充KVMStatefdvmfd

static int kvm_init(MachineState *ms)
{
……
    KVMState *s;
……
    s = KVM_STATE(ms->accelerator);
……
    s->vmfd = -1;
    s->fd = qemu_open("/dev/kvm", O_RDWR);
    if (s->fd == -1) {
        fprintf(stderr, "Could not access KVM kernel module: %mn");
        ret = -errno;
        goto err;
    }
……
    do {
        ret = kvm_ioctl(s, KVM_CREATE_VM, type);
    } while (ret == -EINTR);

    if (ret < 0) {
        fprintf(stderr, "ioctl(KVM_CREATE_VM) failed: %d %sn", -ret,
                strerror(-ret));
        goto err;
    }

    s->vmfd = ret;
……
    ret = kvm_arch_init(ms, s);
    if (ret < 0) {
        goto err;
    }
……
    return ret;
}

如上所示,带有KVM_CREATE_VM参数的ioctl将返回vmfd。一旦QEMU有了fdvmfd,就必须再填充一个文件描述符,即kvm_fd或vcpu fd。
2. kvm_init_vcpu

int kvm_init_vcpu(CPUState *cpu)
{

……
    KVMState *s = kvm_state;
    int ret;
……
    ret = kvm_get_vcpu(s, kvm_arch_vcpu_id(cpu));
    if (ret < 0) {
        goto err;
    }

    cpu->kvm_fd = ret;
    cpu->kvm_state = s;
 ……
    ret = kvm_arch_init_vcpu(cpu);
err:
    return ret;
}

一些内存页在qemu-kvm进程和KVM内核模块之间共享。你可以在kvm_init_vcpu()函数中看到这样的映射。也就是说,每个vCPU的两个主机内存页为QEMU用户空间进程和KVM内核模块之间的通信提供了通道:kvm_runpio_data
还要理解,在执行这些返回前面fd的ioctl期间,Linux内核会分配一个文件结构和相关的匿名节点。

vCPU是QEMU-KVM创建的线程,要运行来自guest代码,这些vCPU线程需要使用KVM_RUN作为参数执行ioctl,即第一篇文章(http://www.qemu.world/?x=entry:entry200206-201418)的:

run_ret = kvm_vcpu_ioctl(cpu, KVM_RUN, 0);