kvm异常退出问题分析

问题现象:

虚拟机在启动的过程中报错,启动失败。日志中提示如下:

KVM internal error.Suberror:3

extra data[0]:800000ef

extra data[1]:31

寄存器值

------

 

分析:我觉得这个问题应该从“寄存器值RIP分析”。 这些寄存器的值是完全准确的虚拟机退出guest mode前的运行状态。 这里需要说明一下知识点,虚拟机运行时的寄存器怎么保存。

虽然我没看过完整的intel文档,但我起码可以分辨出它的设计。

1。 对于那些运行过程中使用的寄存器,比如RAX,RBX,RCS,R1,R2------等寄存器,当虚拟机陷出的时候是保存在vcpu的结构体中去的,这些是在vmx_vcpu_run函数中那一段汇编去做的。仔细阅读那一段汇编可以明白它的过程:save host registers、reload guest registers from vmsc,enter guest mode,exit guest mode and save guest registers to vmcs。

2. 还有一些寄存器是没办法使用软件自行去保存的,比如RIP,RSP,CR3等。因为当从guest mode切换到root mode的时候这些寄存器必然会变化。所以这些都是硬件去完成的,所以呢,这些寄存器是不需要软件去保存。 所以在exit guest mode后我们并不需要做什么,当这些特殊的寄存器需要被读取的时候。在vmx_cache_reg中,它重新读取VMCS,它需要从vmcs中重新读取。

static void vmx_vcpu_run
{
  一大段汇编

      vcpu->arch.regs_avail = ~((1 << VCPU_REGS_RIP) | (1 << VCPU_REGS_RSP)
        | (1 << VCPU_EXREG_RFLAGS)
        | (1 << VCPU_EXREG_PDPTR)
        | (1 << VCPU_EXREG_SEGMENTS)
        | (1 << VCPU_EXREG_CR3));
      vcpu->arch.regs_dirty = 0;

}

  static inline unsigned long kvm_register_read(struct kvm_vcpu *vcpu,
                                                enum kvm_reg reg)
  {
      if (!test_bit(reg, (unsigned long *)&vcpu->arch.regs_avail))
         kvm_x86_ops->cache_reg(vcpu, reg);

      return vcpu->arch.regs[reg];
  }

static void vmx_cache_reg(struct kvm_vcpu *vcpu, enum kvm_reg reg)
{
    __set_bit(reg, (unsigned long *)&vcpu->arch.regs_avail);
    switch (reg) {
    case VCPU_REGS_RSP:
        vcpu->arch.regs[VCPU_REGS_RSP] = vmcs_readl(GUEST_RSP);
        break;
    case VCPU_REGS_RIP:
        vcpu->arch.regs[VCPU_REGS_RIP] = vmcs_readl(GUEST_RIP);
        break;
    case VCPU_EXREG_PDPTR:
        if (enable_ept)
            ept_save_pdptrs(vcpu);
        break;
    default:
        break;
    }
}

在这个问题当中,这些信息是qemu打印的,它的路径是kvm_cpu_exec  --> kvm_handle_internal_error,因为处理不了,所以继续走到cpu_dump_state中处理。在这个函数中它先去cpu_synchronize_state获取CPU各种寄存器等运行环境,然后打印出来。

说了这么多,其实就是想说这个RIP是真实的反映虚拟机最后执行的位置(之前不知道这段日志是谁打印的,所以才有了上面这么多的分析。如果早知道是qemu打印的,估计就不用看这么多代码。 不过看了这么多代码也是有所收获了)

其实寄存器都是这样,有些会拦截,有些是透传的。这个有一个bitmap去控制哪些寄存器会被拦截。 另外还有一些寄存器MSR,他们的读取很“费时”。但是原理一样,如果虚拟机与主机不一样,那么就是需要被store and reload的。

所以我们应该从guest RIP中去找出guest中出问题的代码。

https://bugzilla.redhat.com/show_bug.cgi?id=923653

posted @ 2018-10-02 15:07  你的KPI完成了吗  阅读(1289)  评论(1)    收藏  举报