设为首页 收藏本站
查看: 1295|回复: 0

[经验分享] KVM源代码分析3:CPU虚拟化

[复制链接]

尚未签到

发表于 2015-12-24 15:07:44 | 显示全部楼层 |阅读模式
  在虚拟机的创建与运行章节里面笼统的介绍了KVM在qemu中的创建和运行,基本的qemu代码流程已经梳理清楚,后续主要写一些硬件虚拟化的原理和代码流程,主要写原理和qemu控制KVM运行的的ioctl接口,后续对内核代码的梳理也从这些接口下手。
1.VT-x 技术
  Intel处理器支持的虚拟化技术即是VT-x,之所以CPU支持硬件虚拟化是因为软件虚拟化的效率太低。
  处理器虚拟化的本质是分时共享,主要体现在状态恢复和资源隔离,实际上每个VM对于VMM看就是一个task么,之前Intel处理器在虚拟化上没有提供默认的硬件支持,传统 x86 处理器有4个特权级,Linux使用了0,3级别,0即内核,3即用户态,(更多参考CPU的运行环、特权级与保护)而在虚拟化架构上,虚拟机监控器的运行级别需要内核态特权级,而CPU特权级被传统OS占用,所以Intel设计了VT-x,提出了VMX模式,即VMXroot operation 和 VMX non-root operation,虚拟机监控器运行在VMX root operation,虚拟机运行在VMX non-root operation。每个模式下都有相对应的0~3特权级。
  为什么引入这两种特殊模式,在传统x86的系统中,CPU有不同的特权级,是为了划分不同的权限指令,某些指令只能由系统软件操作,称为特权指令,这些指令只能在最高特权级上才能正确执行,反之则会触发异常,处理器会陷入到最高特权级,由系统软件处理。还有一种需要操作特权资源(如访问中断寄存器)的指令,称为敏感指令。OS运行在特权级上,屏蔽掉用户态直接执行的特权指令,达到控制所有的硬件资源目的;而在虚拟化环境中,VMM控制所有所有硬件资源,VM中的OS只能占用一部分资源,OS执行的很多特权指令是不能真正对硬件生效的,所以原特权级下有了root模式,OS指令不需要修改就可以正常执行在特权级上,但这个特权级的所有敏感指令都会传递到root模式处理,这样达到了VMM的目的。
  在KVM源代码分析1:基本工作原理章节中也说了kvm分3个模式,对应到VT-x 中即是客户模式对应vmx非root模式,内核模式对应VMX root模式下的0特权级,用户模式对应vmx root模式下的3特权级。
  如下图
DSC0000.jpg
  
  在非根模式下敏感指令引发的陷入称为VM-Exit,VM-Exit发生后,CPU从非根模式切换到根模式;对应的,VM-Entry则是从根模式到非根模式,通常意味着调用VM进入运行态。VMLAUCH/VMRESUME命令则是用来发起VM-Entry。
2.VMCS
  VMCS保存虚拟机的相关CPU状态,每个VCPU都有一个VMCS(内存的),每个物理CPU都有VMCS对应的寄存器(物理的),当CPU发生VM-Entry时,CPU则从VCPU指定的内存中读取VMCS加载到物理CPU上执行,当发生VM-Exit时,CPU则将当前的CPU状态保存到VCPU指定的内存中,即VMCS,以备下次VMRESUME。
  VMLAUCH指VM的第一次VM-Entry,VMRESUME则是VMLAUCH之后后续的VM-Entry。VMCS下有一些控制域:
VM-execution controls Determines what operations cause VM exits CR0, CR3, CR4, Exceptions, IO Ports, Interrupts, Pin Events, etc
Guest-state area Saved on VM exits,Reloaded on VM entry EIP, ESP, EFLAGS, IDTR, Segment Regs, Exit info, etc
Host-state area Loaded on VM exits CR3, EIP set to monitor entry point, EFLAGS hardcoded, etc
VM-exit controls Determines which state to save, load, how to transition Example: MSR save-load list
VM-entry controls Determines which state to load, how to transition Including injecting events (interrupts, exceptions) on entry
  关于具体控制域的细节,还是翻Intel手册吧。
3.VM-Entry/VM-Exit
  VM-Entry是从根模式切换到非根模式,即VMM切换到guest上,这个状态由VMM发起,发起之前先保存VMM中的关键寄存器内容到VMCS中,然后进入到VM-Entry,VM-Entry附带参数主要有3个:1.guest是否处于64bit模式,2.MSR VM-Entry控制,3.注入事件。1应该只在VMLAUCH有意义,3更多是在VMRESUME,而VMM发起VM-Entry更多是因为3,2主要用来每次更新MSR。
  VM-Exit是CPU从非根模式切换到根模式,从guest切换到VMM的操作,VM-Exit触发的原因就很多了,执行敏感指令,发生中断,模拟特权资源等。
  运行在非根模式下的敏感指令一般分为3个方面:
  1.行为没有变化的,也就是说该指令能够正确执行。
  2.行为有变化的,直接产生VM-Exit。
  3.行为有变化的,但是是否产生VM-Exit受到VM-Execution控制域控制。
  主要说一下”受到VM-Execution控制域控制”的敏感指令,这个就是针对性的硬件优化了,一般是1.产生VM-Exit;2.不产生VM-Exit,同时调用优化函数完成功能。典型的有“RDTSC指令”。除了大部分是优化性能的,还有一小部分是直接VM-Exit执行指令结果是异常的,或者说在虚拟化场景下是不适用的,典型的就是TSC offset了。
  VM-Exit发生时退出的相关信息,如退出原因、触发中断等,这些内容保存在VM-Exit信息域中。
4.KVM_CREATE_VM
  创建VM就写这里吧,kvm_dev_ioctl_create_vm函数是主干,在kvm_create_vm中,主要有两个函数,kvm_arch_init_vm和hardware_enable_all,需要注意,但是更先一步的是KVM结构体,下面的struct是精简后的版本。



帮助

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15


struct kvm {
    struct mm_struct *mm; /* userspace tied to this vm */
    struct kvm_memslots *memslots;  /*qemu模拟的内存条模型*/
    struct kvm_vcpu *vcpus[KVM_MAX_VCPUS]; /* 模拟的CPU */
    atomic_t online_vcpus;
    int last_boosted_vcpu;
    struct list_head vm_list;  //HOST上VM管理链表,
    struct kvm_io_bus *buses[KVM_NR_BUSES];
    struct kvm_vm_stat stat;
    struct kvm_arch arch; //这个是host的arch的一些参数
    atomic_t users_count;

    long tlbs_dirty;
    struct list_head devices;
};
  kvm_arch_init_vm基本没有特别动作,初始化了KVM->arch,以及更新了kvmclock函数,这个另外再说。
而hardware_enable_all,针对于每个CPU执行“on_each_cpu(hardware_enable_nolock, NULL, 1)”,在hardware_enable_nolock中先把cpus_hardware_enabled置位,进入到kvm_arch_hardware_enable中,有hardware_enable和TSC初始化规则,主要看hardware_enable,crash_enable_local_vmclear清理位图,判断MSR_IA32_FEATURE_CONTROL寄存器是否满足虚拟环境,不满足则将条件写入到寄存器内,CR4将X86_CR4_VMXE置位,另外还有kvm_cpu_vmxon打开VMX操作模式,外层包了vmm_exclusive的判断,它是kvm_intel.ko的外置参数,默认唯一,可以让用户强制不使用VMM硬件支持。
5.KVM_CREATE_VCPU
  kvm_vm_ioctl_create_vcpu主要有三部分,kvm_arch_vcpu_create,kvm_arch_vcpu_setup和kvm_arch_vcpu_postcreate,重点自然是kvm_arch_vcpu_create。老样子,在这之前先看一下VCPU的结构体。



帮助

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56


struct kvm_vcpu {
    struct kvm *kvm;  //归属的KVM
#ifdef CONFIG_PREEMPT_NOTIFIERS
    struct preempt_notifier preempt_notifier;
#endif
    int cpu;
    int vcpu_id;
    int srcu_idx;
    int mode;
    unsigned long requests;
    unsigned long guest_debug;

    struct mutex mutex;
    struct kvm_run *run;  //运行时的状态

    int fpu_active;
    int guest_fpu_loaded, guest_xcr0_loaded;
    wait_queue_head_t wq; //队列
    struct pid *pid;
    int sigset_active;
    sigset_t sigset;
    struct kvm_vcpu_stat stat; //一些数据

#ifdef CONFIG_HAS_IOMEM
    int mmio_needed;
    int mmio_read_completed;
    int mmio_is_write;
    int mmio_cur_fragment;
    int mmio_nr_fragments;
    struct kvm_mmio_fragment mmio_fragments[KVM_MAX_MMIO_FRAGMENTS];
#endif

#ifdef CONFIG_KVM_ASYNC_PF
    struct {
        u32 queued;
        struct list_head queue;
        struct list_head done;
        spinlock_t lock;
    } async_pf;
#endif

#ifdef CONFIG_HAVE_KVM_CPU_RELAX_INTERCEPT
    /*
     * Cpu relax intercept or pause loop exit optimization
     * in_spin_loop: set when a vcpu does a pause loop exit
     *  or cpu relax intercepted.
     * dy_eligible: indicates whether vcpu is eligible for directed yield.
     */
    struct {
        bool in_spin_loop;
        bool dy_eligible;
    } spin_loop;
#endif
    bool preempted;
    struct kvm_vcpu_arch arch;  //当前VCPU虚拟的架构,默认介绍X86
};
  借着看kvm_arch_vcpu_create,它借助kvm_x86_ops->vcpu_create即vmx_create_vcpu完成任务,vmx是X86硬件虚拟化层,从代码看,qemu用户态是一层,kernel 中KVM通用代码是一层,类似kvm_x86_ops是一层,针对各个不同硬件架构,而vcpu_vmx则是具体架构的虚拟化方案一层。首先是kvm_vcpu_init初始化,主要是填充结构体,可以注意的是vcpu->run分派了一页内存,下面有kvm_arch_vcpu_init负责填充x86 CPU结构体,下面就是kvm_vcpu_arch:



帮助

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196


struct kvm_vcpu_arch {
    /*
     * rip and regs accesses must go through
     * kvm_{register,rip}_{read,write} functions.
     */
    unsigned long regs[NR_VCPU_REGS];
    u32 regs_avail;
    u32 regs_dirty;
//类似这些寄存器就是就是用来缓存真正的CPU值的
    unsigned long cr0;
    unsigned long cr0_guest_owned_bits;
    unsigned long cr2;
    unsigned long cr3;
    unsigned long cr4;
    unsigned long cr4_guest_owned_bits;
    unsigned long cr8;
    u32 hflags;
    u64 efer;
    u64 apic_base;
    struct kvm_lapic *apic;    /* kernel irqchip context */
    unsigned long apic_attention;
    int32_t apic_arb_prio;
    int mp_state;
    u64 ia32_misc_enable_msr;
    bool tpr_access_reporting;
    u64 ia32_xss;

    /*
     * Paging state of the vcpu
     *
     * If the vcpu runs in guest mode with two level paging this still saves
     * the paging mode of the l1 guest. This context is always used to
     * handle faults.
     */
    struct kvm_mmu mmu; //内存管理,更多的是附带了直接操作函数

    /*
     * Paging state of an L2 guest (used for nested npt)
     *
     * This context will save all necessary information to walk page tables
     * of the an L2 guest. This context is only initialized for page table
     * walking and not for faulting since we never handle l2 page faults on
     * the host.
     */
    struct kvm_mmu nested_mmu;

    /*
     * Pointer to the mmu context currently used for
     * gva_to_gpa translations.
     */
    struct kvm_mmu *walk_mmu;

    struct kvm_mmu_memory_cache mmu_pte_list_desc_cache;
    struct kvm_mmu_memory_cache mmu_page_cache;
    struct kvm_mmu_memory_cache mmu_page_header_cache;

    struct fpu guest_fpu;
    u64 xcr0;
    u64 guest_supported_xcr0;
    u32 guest_xstate_size;

    struct kvm_pio_request pio;
    void *pio_data;

    u8 event_exit_inst_len;

    struct kvm_queued_exception {
        bool pending;
        bool has_error_code;
        bool reinject;
        u8 nr;
        u32 error_code;
    } exception;

    struct kvm_queued_interrupt {
        bool pending;
        bool soft;
        u8 nr;
    } interrupt;

    int halt_request; /* real mode on Intel only */

    int cpuid_nent;
    struct kvm_cpuid_entry2 cpuid_entries[KVM_MAX_CPUID_ENTRIES];

    int maxphyaddr;

    /* emulate context */
//下面是KVM的软件模拟模式,也就是没有vmx的情况,估计也没人用这一套
    struct x86_emulate_ctxt emulate_ctxt;
    bool emulate_regs_need_sync_to_vcpu;
    bool emulate_regs_need_sync_from_vcpu;
    int (*complete_userspace_io)(struct kvm_vcpu *vcpu);

    gpa_t time;
    struct pvclock_vcpu_time_info hv_clock;
    unsigned int hw_tsc_khz;
    struct gfn_to_hva_cache pv_time;
    bool pv_time_enabled;
    /* set guest stopped flag in pvclock flags field */
    bool pvclock_set_guest_stopped_request;

    struct {
        u64 msr_val;
        u64 last_steal;
        u64 accum_steal;
        struct gfn_to_hva_cache stime;
        struct kvm_steal_time steal;
    } st;

    u64 last_guest_tsc;
    u64 last_host_tsc;
    u64 tsc_offset_adjustment;
    u64 this_tsc_nsec;
    u64 this_tsc_write;
    u64 this_tsc_generation;
    bool tsc_catchup;
    bool tsc_always_catchup;
    s8 virtual_tsc_shift;
    u32 virtual_tsc_mult;
    u32 virtual_tsc_khz;
    s64 ia32_tsc_adjust_msr;

    atomic_t nmi_queued;  /* unprocessed asynchronous NMIs */

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-155819-1-1.html 上篇帖子: KVM/Libvirt采用openvswitch网络. 下篇帖子: kernel 3.10代码分析--KVM相关--虚拟机运行
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表