首页 > 解决方案 > ioctl KVM_RUN 在什么情况下返回?

问题描述

https://github.com/qemu/qemu/blob/stable-4.2/cpus.c#L1290中有一个非常重要的 Qemu。我猜这是 KVM 上 CPU 的事件循环。

这是代码:

static void *qemu_kvm_cpu_thread_fn(void *arg)
{
    CPUState *cpu = arg;
    int r;

    rcu_register_thread();

    qemu_mutex_lock_iothread();
    qemu_thread_get_self(cpu->thread);
    cpu->thread_id = qemu_get_thread_id();
    cpu->can_do_io = 1;
    current_cpu = cpu;

    r = kvm_init_vcpu(cpu);
    if (r < 0) {
        error_report("kvm_init_vcpu failed: %s", strerror(-r));
        exit(1);
    }

    kvm_init_cpu_signals(cpu);

    /* signal CPU creation */
    cpu->created = true;
    qemu_cond_signal(&qemu_cpu_cond);
    qemu_guest_random_seed_thread_part2(cpu->random_seed);

    do {
        if (cpu_can_run(cpu)) {
            r = kvm_cpu_exec(cpu);
            if (r == EXCP_DEBUG) {
                cpu_handle_guest_debug(cpu);
            }
        }
        qemu_wait_io_event(cpu);
    } while (!cpu->unplug || cpu_can_run(cpu));

    qemu_kvm_destroy_vcpu(cpu);
    cpu->created = false;
    qemu_cond_signal(&qemu_cpu_cond);
    qemu_mutex_unlock_iothread();
    rcu_unregister_thread();
    return NULL;
}

我对do循环感兴趣。它在一个循环中调用kvm_cpu_exec,在此处定义:https ://github.com/qemu/qemu/blob/stable-4.2/accel/kvm/kvm-all.c#L2285

在某一时刻kvm_cpu_exec,它调用run_ret = kvm_vcpu_ioctl(cpu, KVM_RUN, 0);,它调用KVM_RUN此处记录的 ioctl:https ://www.kernel.org/doc/Documentation/virtual/kvm/api.txt

4.10 KVM_RUN

能力:基本
架构:全部
类型:vcpu ioctl
参数:无
返回:0 成功,-1 错误
错误:
EINTR:未屏蔽的信号待处理

此 ioctl 用于运行来宾虚拟 CPU。虽然没有
显式参数,但有一个隐式参数块,可以
通过在偏移量 0 处对 vcpu fd 进行 mmap() 处理,其大小由
KVM_GET_VCPU_MMAP_SIZE 给出。参数块被格式化为“struct
kvm_run”(见下文)。

我很难理解这个ioctl是否会阻止执行?在哪些情况下会返回?

我想了解一下正在发生的事情。鉴于这一行qemu_wait_io_event(cpu),至少看起来,每次事件要从 CPU 读取/写入/从 CPU 读取时, ioctl都会返回。我不知道,我很困惑。

标签: clinuxvirtual-machineqemukvm

解决方案


KVM API 设计要求 VM 中的每个虚拟 CPU 在控制该 VM 的程序(如 QEMU)中具有关联的用户空间线程(该程序通常称为“虚拟机监视器”或 VMM,它不必是QEMU;其他示例是 kvmtool 和 firecracker)。

该线程的行为类似于 QEMU 中的普通用户空间线程,直到它生成 KVM_RUN ioctl。此时,内核使用该线程在与该线程关联的 vCPU 上执行客户代码。这种情况一直持续到遇到某些情况,这意味着来宾执行无法继续进行。(一个常见的情况是“客户对 QEMU 模拟的设备进行了内存访问”。)此时,内核停止在该线程上运行客户代码,而是使其从 KVM_RUN ioctl 返回。QEMU 中的代码然后查看返回代码等,以找出它获得控制权的原因,处理任何情况,然后循环返回以再次调用 KVM_RUN 以要求内核继续运行客户代码。

通常在运行 VM 时,您会看到几乎所有时间线程都在 KVM_RUN ioctl 中,运行真正的客户代码。偶尔执行会返回,QEMU 会花尽可能少的时间做它需要做的任何事情,然后它会循环并再次运行客户代码。提高 VM 效率的一种方法是尝试确保这些“VM 退出”的数量尽可能少(例如,通过仔细选择来宾所使用的网络或块设备类型)。


推荐阅读