【问题标题】:How does KVM schedule multiple VMs in Intel VMX?KVM 如何在 Intel VMX 中调度多个 VM?
【发布时间】:2018-06-09 12:20:41
【问题描述】:

我正在通过 Linux KVM 学习 Intel VMX。
而且我无法清楚地理解 KVM (Linux) 如何安排多个 VM 在同一主机上同时运行。
例如,主机中有 1 个物理 CPU,有 2 个 KVM 虚拟机,每个虚拟机配置 1 个 vCPU。
一旦启动,KVM/QEMU 会为每个 vCPU 配置一个 VMCS,因此 KVM 中有 2 个 VMCS。由于只有 1 个 pCPU,所以 KVM/Linux 必须将每个 vCPU 1 1 调度。
我的理解是当vCPUa运行时,vCPUa的KVM VMPTRLD VMCS,并运行VM的代码。然后,要调度 vCPUb,KVM 将 vCPUa 的 VMPTRST VMCS 到某处,并从某处 vCPUb 的 VMPTRLD VMCS。
通过阅读 KVM 的代码,我没有找到用于 vCPU 调度的 VMPTRLD/VMPTRST 发生在哪里,以及“某处”是什么。

【问题讨论】:

  • 我猜 KVM 就像主机内核下的一个进程一样工作,由常规调度程序进行调度。它必须需要代码来在运行来宾 VM 和运行主机代码(包括主机上的非 VM 用户空间进程)之间切换。您是否已经查看了上下文切换函数以了解它们是否支持 KVM?
  • 这是有道理的,这也是我的猜测。我必须深入研究调度程序代码以了解详细信息,将在此处更新我的发现。

标签: linux x86 virtualization kvm


【解决方案1】:

vmptrld 在 arch/x86/kvm/vmx.c 的 vmx_vcpu_load 中
vmresume 在 vmx_vcpu_run 中

【讨论】:

  • 这有点难以理解,因为支持嵌套虚拟化的所有代码都在同一个文件中,所以大多数对 vmptrld 和 vmresume 的引用都与来宾 VMM 执行的操作有关。跨度>
  • 请注意,不需要 vmptrst,因为 VMCS 指针与加载的值保持相同。
  • 那么,如何在这个 pCPU 上调度其他 VM,或者将一个 vCPU 迁移到另一个 pCPU,这必须更改 pCPU 上的活动/当前 VMCS?
【解决方案2】:

首先,KVM 将注册两个用于 vCPU 调度进出的标注,如下所示。
kvm_preempt_ops.sched_in = kvm_sched_in;
kvm_preempt_ops.sched_out = kvm_sched_out;

因此,每次调度发生时(尚未深入研究),它们都会被调用。以kvm_sched_in()为例,它会调用,

static void kvm_sched_in(struct preempt_notifier *pn, int cpu)
{
    struct kvm_vcpu *vcpu = preempt_notifier_to_vcpu(pn);

    if (vcpu->preempted)
        vcpu->preempted = false;

    kvm_arch_sched_in(vcpu, cpu);

    kvm_arch_vcpu_load(vcpu, cpu);
}

kvm_arch_vcpu_load() 将使用 VMCS,如下所示。

void kvm_arch_vcpu_load(struct kvm_vcpu *vcpu, int cpu)
{
    /* Address WBINVD may be executed by guest */
    if (need_emulate_wbinvd(vcpu)) {
        if (kvm_x86_ops->has_wbinvd_exit())
            cpumask_set_cpu(cpu, vcpu->arch.wbinvd_dirty_mask);
        else if (vcpu->cpu != -1 && vcpu->cpu != cpu)
            smp_call_function_single(vcpu->cpu,
                    wbinvd_ipi, NULL, 1);
    }
    kvm_x86_ops->vcpu_load(vcpu, cpu); <<======

因为,.vcpu_load = vmx_vcpu_load,

/*
 * Switches to specified vcpu, until a matching vcpu_put(), but assumes
 * vcpu mutex is already taken.
 */
static void vmx_vcpu_load(struct kvm_vcpu *vcpu, int cpu)
{
    struct vcpu_vmx *vmx = to_vmx(vcpu);
    u64 phys_addr = __pa(per_cpu(vmxarea, cpu));

    if (!vmm_exclusive)
        kvm_cpu_vmxon(phys_addr);
    else if (vmx->loaded_vmcs->cpu != cpu)
        loaded_vmcs_clear(vmx->loaded_vmcs);

    if (per_cpu(current_vmcs, cpu) != vmx->loaded_vmcs->vmcs) {
        per_cpu(current_vmcs, cpu) = vmx->loaded_vmcs->vmcs;
        vmcs_load(vmx->loaded_vmcs->vmcs);
    }

就是这样。

【讨论】:

    猜你喜欢
    • 2021-12-27
    • 1970-01-01
    • 2017-09-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多