0 Comments

vCPU的运行(1)

发布于:2012-12-03  |   作者:广州网站建设  |   已聚集:人围观

在创建完VM和vCPU并且完成了初始化工作之后,就可以通过调度程序调度执行。因为在Linux中,KVM虚拟机作为一个系统线程运行,因此,KVM虚拟机的调度程序实际上也就是Linux的调度程序,具体的调度将在后文qemu部分进行讨论,在当前,KVM的调用是从ioctl的KVM_RUN指令字开始的。

KVM_RUN指令字针对fd_vcpu描述符操作,当vCPU准备完成之后,即可通过该指令让虚拟机运行起来。虚拟机运行的主要任务则是进行上下文切换。上下文切换的内容较多,通常包括通用寄存器、浮点寄存器、段寄存器、控制寄存器、MSR等,在KVM中,还包括APIC状态、TLB等。广州网站建设

通常,进行上下文切换的过程可以归纳为如下步骤。

1)KVM保存自己的上下文。

2)KVM通过使用将kvm_vcpu结构体中的相关上下文加载到物理CPU中。

3)KVM执行kvm_x86_ops中的run_vcpu函数,调用具体的平台相关指令,进入虚拟机运行环境中。

由此可见,上下文切换次数过于频繁会带来不小的性能开销,因此,很有必要对这方面进行优化。和操作系统进行进程切换的思路一样,KVM使用Lazy Save/Restore的方法进行优化。其基本思想是尽量不要对寄存器进行恢复/保存操作,直到必须要这么做的时候,才进行类似的操作。

执行vCPU的请求首先发送到kvm_vcpu_ioctl函数中,然后加载vCPU参数,调用kvm_arch_vcpu_ioctl_run函数进入具体的vCPU运行环节。

1)通过调用sigprocmask函数,保证在vCPU的初始化过程中,不会因为来自其他线程的信号干扰而中断。

2)将vCPU的状态切换为KVM_MP_STATE_UNINITIALIZED。广州网站建设

3)配置APIC和mmio的中断信息。

4)对要进入的虚拟机进行一些关键指令的测试,在测试中主要针对内存读/ 写情况进行测试。

5)将vCPU中保存的上下文信息(寄存器状态等)写入指定的位置。

6)接下来才开始实质性的工作,调用__vcpu_run函数进行后续处理。

__vcpu_run函数的代码如下。

代码5-11  __vcpu_run函数

 


  1. (5232)  static int __vcpu_run(struct kvm_vcpu *vcpu)  
  2. (5233)  {  
  3. (5234)      int r;  
  4. (5235)      struct kvm *kvm = vcpu->kvm;  
  5. (5236)    
  6. (5237)      if (unlikely(vcpu->arch.mp_state == KVM_MP_STATE_SIPI_RECEIVED)) {  
  7. (5238)          pr_debug("vcpu %d received sipi with vector # %x\n",  
  8. (5239)               vcpu->vcpu_id, vcpu->arch.sipi_vector);  
  9. (5240)          kvm_lapic_reset(vcpu);  
  10. (5241)          r = kvm_arch_vcpu_reset(vcpu);  
  11. (5242)          if (r)  
  12. (5243)              return r;  
  13. (5244)          vcpu->arch.mp_state = KVM_MP_STATE_RUNNABLE;  
  14. (5245)      }  
  15. (5246)    
  16. (5247)      vcpu->srcu_idx = srcu_read_lock(&kvm->srcu);  
  17. (5248)      vapic_enter(vcpu);  
  18. (5249)    
  19. (5250)      r = 1;  
  20. (5251)      while (r > 0) {  
  21. (5252)          if (vcpu->arch.mp_state == KVM_MP_STATE_RUNNABLE)  
  22. (5253)              r = vcpu_enter_guest(vcpu);  
  23. (5254)          else {  
  24. (5255)              srcu_read_unlock(&kvm->srcu, vcpu->srcu_idx);  
  25. (5256)              kvm_vcpu_block(vcpu);  
  26. (5257)              vcpu->srcu_idx = srcu_read_lock(&kvm->srcu);  
  27. (5258)              if (kvm_check_request(KVM_REQ_UNHALT, vcpu))  
  28. (5259)              {  
  29. (5260)                  switch(vcpu->arch.mp_state) {  
  30. (5261)                  case KVM_MP_STATE_HALTED:  
  31. (5262)                      vcpu->arch.mp_state =  
  32. (5263)                          KVM_MP_STATE_RUNNABLE;  
  33. (5264)                  case KVM_MP_STATE_RUNNABLE:  
  34. (5265)                      break;  
  35. (5266)                  case KVM_MP_STATE_SIPI_RECEIVED:  
  36. (5267)                  default:  
  37. (5268)                      r = -EINTR;  
  38. (5269)                      break;  
  39. (5270)                  }  
  40. (5271)              }  
  41. (5272)          }  
  42. (5273)    
  43. (5274)          if (r <= 0)  
  44. (5275)              break;  
  45. (5276)    
  46. (5277)          clear_bit(KVM_REQ_PENDING_TIMER, &vcpu->requests);  
  47. (5278)          if (kvm_cpu_has_pending_timer(vcpu))  
  48. (5279)              kvm_inject_pending_timer_irqs(vcpu);  
  49. (5280)    
  50. (5281)          if (dm_request_for_irq_injection(vcpu)) {  
  51. (5282)              r = -EINTR;  
  52. (5283)              vcpu->run->exit_reason = KVM_EXIT_INTR;  
  53. (5284)              ++vcpu->stat.request_irq_exits;  
  54. (5285)          }  
  55. (5286)          if (signal_pending(current)) {  
  56. (5287)              r = -EINTR;  
  57. (5288)              vcpu->run->exit_reason = KVM_EXIT_INTR;  
  58. (5289)              ++vcpu->stat.signal_exits;  
  59. (5290)          }  
  60. (5291)          if (need_resched()) {  
  61. (5292)              srcu_read_unlock(&kvm->srcu, vcpu->srcu_idx);  
  62. (5293)              kvm_resched(vcpu);  
  63. (5294)              vcpu->srcu_idx = srcu_read_lock(&kvm->srcu);  
  64. (5295)          }  
  65. (5296)      }  
  66. (5297)    
  67. (5298)      srcu_read_unlock(&kvm->srcu, vcpu->srcu_idx);  
  68. (5299)    
  69. (5300)      vapic_exit(vcpu);  
  70. (5301)    
  71. (5302)      return r;  
  72. (5303)  }  
飞机