-
Notifications
You must be signed in to change notification settings - Fork 42
Steal Time
Steal time is the percentage of time a virtual CPU waits for a real CPU while the hypervisor is servicing another virtual processor.
Steal time是在虚拟化环境下,当前VM(vCPU)等待CPU的时间百分比,即VMM把同一个CPU分配给其他VM(vCPU)的时间,比如0.5表示当前VM只能拿到50%的CPU时间。1表示当前VM拿不到一点CPU时间。
steal time带来的直接后果是VM的指令执行速度降低,比如2GHz的CPU在0.4的steal time下vCPU的频率为1.2GHz。
steal time通过top的st进行暴露。意义在于让guest感知自己真正占用CPU的时间比例,并据此调整自己的行为,以免影响业务,如果st值比较高,则说明当前vCPU分到的的CPU比例太小,整个VMM任务比较繁重,有些高计算任务可以跟着自我限制。
在KVM中,steal time通过 kvm_steal_time 结构进行维护:
struct kvm_steal_time {
__u64 steal; // 当前vCPU没有运行的时间(ns),在vCPU空闲时不计算
__u32 version; // 版本号,奇数表示正在更新
__u32 flags; // 目前为0
__u32 pad[12];
}
当VM初始化了该数据结构后,把地址写入 MSR_KVM_STEAL_TIME(0x4b564d03)中。VMM会定时更新该数据结构的内容。和kvmclock一样,采用半虚拟化,guest需要安装驱动。
The interval between updates of this structure is arbitrary and implementation-dependent
The hypervisor may update this structure at any time it sees fit until anything with bit0 == 0 is written to it
setup_arch => kvm_guest_init => pv_time_ops.steal_clock = kvm_steal_clock 如果支持 KVM_FEATURE_STEAL_TIME,注册steal函数 => smp_ops.smp_prepare_boot_cpu = kvm_smp_prepare_boot_cpu => smp_prepare_boot_cpu => smp_ops.smp_prepare_boot_cpu(kvm_smp_prepare_boot_cpu) => kvm_guest_cpu_init => kvm_register_steal_time
不断尝试读per-cpu变量steal_time(类型为kvm_steal_time),直到version为偶数且不再变化为止。
通过写MSR告知VMM自己strcut kvm_steal_time的地址。
获取当前的steal time。会被account_process_tick / irqtime_account_process_tick等函数调用,即定时器更新时也更新steal time。
=> paravirt_steal_clock 读取steal_clock结构,获得当前steal time => steal -= this_rq()->prev_steal_time; 减去prev_steal_time,得到steal time的差值 => account_steal_time 将当前steal time的差值累加到kcpustat_this_cpu->cpustat[CPUTIME_STEAL] => this_rq()->prev_steal_time += cputime_to_nsecs(steal_cputime) 加上差值,更新prev_steal_time
注意,kcpustat_this_cpu 是top命令看到的cpu数据的来源,CPUTIME_STEAL项对应的就是st。
进程调度中rq计算task运行时间。steal time不会被计算到具体的调度队列的运行时间中。
=> update_rq_clock_task 开启CONFIG_PARAVIRT_TIME_ACCOUNTING后,在计算task运行时间会从delta中减去steal time
当VM设置了MSR后,触发VM exit,VMM执行以下流程
handle_wrmsr => kvm_set_msr => kvm_x86_ops->set_msr 即 vmx_x86_ops->set_msr (vmx_set_msr) => kvm_set_msr_common => (MSR_KVM_STEAL_TIME)
初始化VM的steal_time区域,该区域被映射到vcpu->arch.st.stime
=> kvm_gfn_to_hva_cache_init(vcpu->kvm, &vcpu->arch.st.stime, data & KVM_STEAL_VALID_BITS, sizeof(struct kvm_steal_time)) => kvm_make_request(KVM_REQ_STEAL_UPDATE, vcpu) 产生 KVM_REQ_STEAL_UPDATE 请求,在enter guest时会处理
设置MSR后,每次在 vcpu_enter_guest 时都会去执行 record_steal_time
record_steal_time计算vCPU的steal time,更新VM的 steal_time 区域,即更新vcpu->arch.st.stime
// run_delay是任务(qemu的vcpu线程)没有运行的时间
vcpu->arch.st.steal.steal += current->sched_info.run_delay - vcpu->arch.st.last_steal; // 得到vCPU当前没有运行的时间
vcpu->arch.st.last_steal = current->sched_info.run_delay;
通过 kvm_write_guest_cached 将vcpu->arch.st.stime中的值更新到VM中对应地址的数据结构中。