guest linux reboot
显然,linux的reboot流程在guest和host上是一致的,这里写成guest linux reboot只是为了
突出下主题。
1 2 3 4 5 6 7 8 9 10 11 12
| /* kernel/reboot.c */ reboot +-> kernel_restart /* arch/arm64/kernel/process.c */ +-> machine_restart /* 发IPI停止其它core */ +-> smp_send_stop /* * 支持efi runtime service的话,调用对应接口efi.reset_system接口,这里 * 看不见了,姑且认为这个接口使用PSCI的PSCI_1_1_FN64_SYSTEM_RESET2实现。 */ +-> efi_reboot
|
host KVM处理
PSCI使用smc/hvc实现,虚拟机里调用PSCI会trap到KVM,KVM会模拟实现对应的请求,调用
流程如下:
1 2 3 4 5 6 7 8 9 10 11
| kvm_arch_vcpu_ioctl_run -> handle_exit -> handle_trap_exceptions -> handle_smc/handle_hvc -> kvm_smccc_call_handler -> kvm_psci_call: +-> kvm_psci_1_x_call +-> kvm_psci_system_reset2 /* * 记录kvm退出的原因和类型,后续会传给qemu。可见这时从kvm退出的原因 * 是退出系统,退出的类型是reset。最后从ioctl_run返回到qemu。 */ +-> kvm_prepare_system_event +-> vcpu->run->system_event.type = KVM_SYSTEM_EVENT_RESET vcpu->run->exit_reason = KVM_EXIT_SYSTEM_EVENT
|
qemu处理
qemu vCPU线程的主线逻辑如下,退出到qemu后先stop当前的vCPU线程,然后给qemu主线程
发消息,在主线程里调用CPU以及各种外设之前注册的reset函数把整个虚机复位到初始状态,
然后再触发各个vCPU线程继续运行。整个过程vCPU线程只是挂起一下。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| kvm_vcpu_thread_fn kvm_init_vcpu do { if (cpu_can_run(cpu)) {
kvm_cpu_exec(cpu); | kvm_vcpu_ioctl <--- 运行vCPU | switch (run->exit_reason) <--- 处理kvm退出 | ... | case KVM_EXIT_SYSTEM_EVENT <--- 处理退出系统/reset,注意除了reset | case KVM_SYSTEM_EVENT_RESET 还有shutdown/crash/suspend/wakeup等 | qemu_system_reset_request | cpu_stop_current <--- stop当前vCPU | qemu_notify_event <--- 触发qemu主线程
if (r == EXCP_DEBUG) { cpu_handle_guest_debug(cpu); } } qemu_wait_io_event(cpu); } while (!cpu->unplug || cpu_can_run(cpu)); ...
|
qemu主线程逻辑如下,上面的qemu_notify_event解开main_loop_wait的等待后,qemu主线程
进入main_loop_should_exit执行。
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
| main +-> qemu_init +-> qemu_main_loop +-> while (!main_loop_should_exit(&status)) { main_loop_wait(false); }
main_loop_should_exit ... +-> qemu_reset_requested +-> pause_all_vcpus +-> qemu_system_reset
cpu_synchronize_all_states +-> cpu_synchronize_state /* kvm的core寄存器信息读到qemu里来 */ +-> kvm_cpu_synchronize_state
qemu_devices_reset / mc->reset +-> resettable_reset
+-> arm_cpu_reset_hold ... +-> kvm_arm_reset_vcpu <--- core reset /* 下发KVM_ARM_VCPU_INIT ioctl,KVM会初始化vCPU */ +-> kvm_arm_vcpu_init +-> write_kvmstate_to_list +-> write_list_to_cpustate
+-> kvm_arm_gicv3_reset_hold <--- gicv3 reset /* reset qemu gicv3的状态 */ +-> kgc->parent_phases.hold(obj, type) // arm_gicv3_common_reset_hold /* * 调用KVM GICv3的接口,把qemu GICv3的状态配置到KVM里。热迁移 * 在目的端恢复GICv3的状态的时候也会调用这个接口。 */ +-> kvm_arm_gicv3_put
+-> kvm_arm_its_reset_hold <--- gicv3 ITS reset +-> c->parent_phases.hold(obj, type) // gicv3_its_common_reset_hold +-> 下发KVM_DEV_ARM_ITS_CTRL_RESET以及ITS寄存器刷新ioctl
cpu_synchronize_all_post_reset +-> cpu_synchronize_post_reset /* qemu的core寄存器信息写回KVM */ +-> kvm_cpu_synchronize_post_reset
+-> resume_all_vcpus
|
下面列出上面各个设备reset_hold回调注册的地方:
1 2 3 4
| /* target/arm/cpu.c */ arm_cpu_class_init +-> resettable_class_set_parent_phases(rc, NULL, arm_cpu_reset_hold, NULL, &acc->parent_phases);
|
1 2 3 4
| /* hw/intc/arm_gicv3_kvm.c */ kvm_arm_gicv3_class_init +-> resettable_class_set_parent_phases(rc, NULL, kvm_arm_gicv3_reset_hold, NULL, &kgc->parent_phases);
|
1 2 3 4
| /* hw/intc/arm_gicv3_its_kvm.c */ kvm_arm_its_class_init +-> resettable_class_set_parent_phases(rc, NULL, kvm_arm_its_reset_hold, NULL, &ic->parent_phases);
|