qemu-kvm虚拟化时钟机制
LanceLiu
2019-10-16
qemu-kvm timer 相关设备实现机制
tsc实现机制
rtc实现机制
lapic timer实现机制
acpi_pm实现机制
kvm-clock实现机制
VMCS 存在VMCS OFFSET
static void kvm_vcpu_write_tsc_offset(struct kvm_vcpu *vcpu, u64 offset)用于写offset
kvm_vm_ioctl_create_vcpu()
kvm_arch_vcpu_postcreate()
kvm_write_tsc(vcpu, &msr);
offset = 0 - rdtsc()
Sub Topic
迁移为何引起tsc落后?
kvm_arch_init()中kvm_timer_init()会设置vcp->arch.virtual_tsc_khz
读取
guest_read_tsc()
kvm_scale_tsc(vcpu, host_tsc) + tsc_offset
启动时为何出现时钟超前或落后?
测试5次
1214、585、87、1232、1322、-43、586、-172、170
suspend为何引起kvm-clock时钟落后
虚拟机启动时读取时间?
配置了kvm-clock
未配置kvm-clock
调用kvm_get_wallclock读取时间
mach_get_cmos_time
configure_rtc()
base
utc 标准时间
localtime 使用host时间
offset 指定从何时开始
driftfix
slew 快速定位 lost_tick_policy ---> LOST_TICK_SLEW
启动 coalesced_timer
读取
初始化
会设置base rtc, 虚拟机通过cmos相关函数接口读取时得到时间精度为妙级
见RTC实现机制
初始化
相关问题
如何从kvm内核态进入用户态?
同其他应用程序一样
getboottime()为何调用set_normalized_timespec(ts, -boottime.tv_sec, -
boottime.tv_nsec);
kvm_write_wall_clock()
wc = boot - ns_to_timespec(kvm_clock_offset)
kvm_clock_offset = -ktime_get_boot_ns() = ktime_get_boottime()
该函数由启动时kvm_arch_init_vm()函数确定
diable kvm-clock,使用rtc机制
kvm_clock机制
kvm_write_wall_clock使用的是getboottime()没有算上monotonic中nsec部分
修改后: 464, 430, 598、401
基础部分
write_msr流程
wrmsr index --> ecx data --> edx << 32 + eax
在start_kernel()中的setup_arc() 调用kvmclock_init()
1. 为hv_clock分配一个PAGE页(4KB)
2. kvm_register_clock() 该步骤通过native_write_msr_safe()的方式通知VMM
将&hvclock[cpu].pvti传递给VMM
vm_stop resume的影响
qemu-kvm在初始化时的kvmclock_init()会注册vm changer handler
kvm_clock_vm_state_change()
do_vm_stop() vm_start()中分别会调用vm_state_notify(),调用
vm_change_state_head链中的callback函数,最终调用kvmclock_vm_state_change()
kvmclock_vm_state_change()
kvmclock_current_nsec从guest中system_time_msr获取time
包含tsc_timestamp env->tsc
kvm-clock guest time更新
vcpu->hv_clock [tsc_timestamp, system_time, ...]
vcpu->last_guest_tsc
vcpu_enter_guest() check 到 KVM_REQ_CLOCK_UPDATE 调用
kvm_guest_time_update()
host kvm模块
kvm_arch_init_vm()中kvm->arch.kvmclock_offset = -ktime_get_boot_ns();
vcpu->hv_clock.tsc_timestamp = tsc_timestamp;
vcpu->hv_clock.system_time = kernel_ns + v->kvm->arch.kvmclock_offset;
vcpu->last_guest_tsc = tsc_timestamp;
迁移时对CPU状态的处理机制
process_incoming_migration_co() 中qemu_loadvm_state(QEMUFile *f)会调用
cpu_synchronize_all_post_init()函数,该函数会调用kvm_arch_put_registers(cpu,
KVM_PUT_FULL_STATE);恢复CPU状态,其中包括CPU的tsc offset
源端cpu_pre_save()负责保存虚拟CPU状态
目的端cpu_post_load()负责加载虚拟CPU状态
见kvm-clock实现机制
cpu stop 相关
do_vm_stop()
pause_all_cpus()
1. diable vm_clock
Sub Topic
vcpu thread持续处于qemu_kvm_wait_io_evnet()的
while(cpu_thread_is_idle(env))中
改进
bc20403598702fac96b5a732bdb184ccbe1fcb48 vs
6053a86fe7bd3d5b07b49dae6c05f2cd0d44e687
diff --git a/hw/i386/kvm/clock.c b/hw/i386/kvm/clock.c
+ * This reduces kvmclock difference on migration from 5s
diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h
diff --git a/target/i386/kvm.c b/target/i386/kvm.c
diff --git a/target/i386/kvm_i386.h b/target/i386/kvm_i386.h
Machine compat_props用于何处?
限制及问题
rhel7.2 版本 内核 3.10.0-327 无KVM_CLOCK_TSC_STABLE
社区改进
2. 从pre_save_kvmclock()到真正的resume时间可能过长
self 改进
虚拟机cpu_presave()时同步最新tsc
resume使用最新tsc更新kvm clock
测试结果
(1) 将迁移给wall time落后降低到10ms级别以内
tsc_deadline
tsc_adjust
MSR_IA32_TSC_DEADLINE
lapic_next_deadline()
wrmsr(MSR_IA32_TSC_DEADLINE, tsc + ((u64)delta) * TSC_DIVISOR)
默认: lapic_next_event()
apic_write(APIC_TMICT, delta)
HV模式
SW模式
当vmx支持preemption timer及 使能preemption timer时,使用hv模式
基于host hrtimer机制实现,基于guest 当前tsc和tscdeadline,设置hrtimer超时时间,当
hrtimer到达后,触发器callback apic_timer_fn,调用apic_timer_expired(apic) pend lapic
中断
set
vmx->hv_deadline_tsc = tsc + delta_tsc;
vmcs_set_bits(PIN_BASED_VM_EXEC_CONTROL,
PIN_BASED_VMX_PREEMPTION_TIMER);
vmx_vcpu_run() 中vmx_arm_hv_timer(cpu),
vmcs_write32(VMX_PREEMPTION_TIMER_VALUE, delta_tsc)
计算delta_tsc,然后写入VMCS的VMX_PREEMPTION_TIMER_VALUE
当时间戳到达后,CPU退出guest模式,reason为
EXIT_REASON_PREEMPTION_TIMER,如此,调用handle_preemption_timer(),向
vcpu->arc.apic->lapic_timer.pending写入1,最终通过kvm_inject_apic_timer_irqs()注入
到vcpu中
kvm_set_msr_common()
kvm_x2apic_msr_write()
kvm_lapic_set_reg()
set_target_expiration()
restart_apic_timer(apic);
通过kvm_lapic_get_regs()获取apic->lapic_timer.period
再设置lapic_timer.target_expiration,后基于此设置hrtimer超时时间
tsc_adjust基本概念(Intel X86手册)
软件可以通过WRMSR指令写入IA32_TIME_STAMP_COUNTER_MSR修改
TSC值。由于这个写操作只会应用到逻辑CPU,软件寻求在多个逻辑处理器上同步TSC
值必须在每个逻辑处理器中执行这一操作。对软件来说这相比于让所有TSC在给定时间
有相同值更难。
通过使用64位的IA32_TSC_ADJUST MSR简化TSC修正同步。类似
IA32_TIME_STAMP_COUNTER_MSR, 每个逻辑处理器都各自维护
IA32_TSC_ADJUST
工作机制
1. RESET后IA32_TSC_ADJUST为0
2. 如果WRMSR 从TSC加(减)值X,逻辑处理器也会从IA32_TSC_ADJUST MSR中加或
减X
Linux内核的使用
qemu-kvm虚拟化场景的影响
1. CPU初始化
2. cpu_enter_idle()
1. tsc_adjust在虚拟化场景下其实意义不大
2. 开启tsc_adjust特性,rdmsrl(IA32_TSC_ADJUST_MSR)会导致vmexit
3.若WRMSR从IA32_TSC_ADJUST MSR加(减)值X, 处理器中TSC也会加(减)相应值
void arch_cpu_idle_enter(void)
{
tsc_verify_tsc_adjust();
local_touch_nmi();
enter_idle();
}
rdmsrl(MSR_IA32_TSC_ADJUST, curval);
if (adj->adjusted == curval)
return;
/* Restore the original value */
wrmsrl(MSR_IA32_TSC_ADJUST, adj->adjusted);
VMX_PREEMPTION_TIMER
Sub Topic
HOST
kvm_set_lapic_tscdeadline_msr()
start_apic_timer()
Created With
MindMaster