1. VCPU是线程不?
是
2. VCPU的线程是在用户态创建还是在KVM创建?
用户态,qemu先创建用户态线程,然后调用KVM ioctl的KVM_CREATE_VCPU;
最终会执行到kvm_main.c中kvm_vm_ioctl_create_vcpu,创建出VCPU;
而用户态线程调用KVM ioctl的KVM_RUN时,发起调用的线程将会作为VCPU的线程执行Guest代码;
3. 进入Guest代码的点?
在__kvm_vpu_run函数会将当前EL1寄存器状态保存,并打开EL2的trap,配置VTTBR、VPIDR等寄存器;
配置VGIC并将Guest的EL1相关寄存器更改为Guest的context;
真实进入Guest代码的点是__guest_enter(vcpu, host_ctxt),保存X19-X30寄存器并eret到EL1
4. 退出Guest代码的点?
而Guest的退出必然是由exception(包括IRQ)触发的,最终又会走到__guest_exit(),保存Guest X2-X30;
然后恢复Host X19-X30,将退出码放到X0并ret(X0再gcc中用于存函数返回值),回到__kvm_vpu_run函数。
5. VCPU的调度与用户线程的调度有何不同?
VCPU的调度与线程是一样的;
6. 傻子也能看得出,VCPU的context比一个thread得多出一堆EL1的特殊寄存器吧,你告诉我VCPU的调度跟线程一样?
是的,就是一样的,虽然KVM在task_struct中多注册了preempt_notifiers元素;
我仔细分析了这个notifier:
这个notifier中的preempt_ops有sched_in/sched_out两个方法:
schedule调度入口在执行context_switch时,
在prepare_task_switch中调用了fire_sched_out_preempt_notifier,
在finish_task_switch中调用了fire_sched_in_preempt_notifier;
struct kvm_vcpu->preempt_notifier中的sched_in/sched_out在上述两处被调用
但这仅在CONFIG_PREEMPT_NOTIFIERS打开时,才可能有用(还受preempt_notifier_key变量控制);
并且我自己查找并确认,#####sched_in/sched_out并没有保存/恢复EL1的各个特殊寄存器#####
7. 那么EL1的特殊寄存器集合是如何在每次context切换中保存/恢复的:
我认为是在__kvm_vpu_run
下面的分析没有Google到太权威的支撑,但是经过对kernel4.15代码的分析、搜索,我认为这是对的:
首先要说,当VCPU在物理CPU中运行时,物理中断发生,是可以对其打断并进入异常向量处理的,
根据__kvm_hyp_vector中的指示,大概就是陷入EL2 context,然后走el1_irq向量,然后执行__guest_exit,也就是4.中所说退出Guest的点
在Guest退出点会恢复之前的线程上下文(依旧在EL2中),也就是回到__kvm_vcpu_run中,若事件可直接处理,在处理完毕后可直接再回到Guest上下文;
若无法直接处理则保存Guest上下文并退出EL2。
在整个退出EL2之前,我认为没有机会触发Host schedule,Host调度Timer等需要Host处理的中断,自然会使VCPU保存Guest上下文并走到Host处理流程。