From: "zhoujiajing.vergil" <zhoujiajing.ver...@bytedance.com>
It is possible enter this function when the cpu not finished creating but is already in the cpu list. The value of dirty_gfns is null, causing vm crash here. The call stack is as follows: kvm_dirty_ring_reaper_thread -> kvm_dirty_ring_reap ->kvm_dirty_ring_reap_locked ->kvm_dirty_ring_reap_one When both dirty-ring and memorybacking are set, creating a vm will assert on kvm_dirty_ring_reap_one. Part of the xml as follows: <domain type='kvm' id='9'> ... <memoryBacking> <hugepages> <page size='2048' unit='KiB' memAccess='shared'/> </hugepages> </memoryBacking> ... <features> <acpi/> <kvm> <dirty-ring state='on' size='4096'/> </kvm> </features> ... <domain/> The kvm-reaper thread was created before vcpu thread, and the value of cpu->kvm_dirty_gfns is assigned at cpu thread. In the x86_cpu_realizefn function, the cpu is inserted into the cpu list first, and then the cpu thread is created for initialization. The entry functions are cpu_exec_realizefn and qemu_init_vcpu. In the existing logic, the kvm-reaper thread traverses the cpu list every second and finally call kvm_dirty_ring_reap_one for each cpu in the list. If cpu has been inserted into cpu list but has not been initialized so that the value of dirty_gfns is null, kvm-reaper thread call kvm_dirty_ring_reap_one will cause vm crash. Signed-off-by: zhoujiajing.vergil <zhoujiajing.ver...@bytedance.com> --- accel/kvm/kvm-all.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/accel/kvm/kvm-all.c b/accel/kvm/kvm-all.c index f2a6ea6a68..ecd873fe73 100644 --- a/accel/kvm/kvm-all.c +++ b/accel/kvm/kvm-all.c @@ -685,6 +685,10 @@ static uint32_t kvm_dirty_ring_reap_one(KVMState *s, CPUState *cpu) uint32_t ring_size = s->kvm_dirty_ring_size; uint32_t count = 0, fetch = cpu->kvm_fetch_index; + /* return 0 when cpu not finished creating */ + if(!cpu->created) + return 0; + assert(dirty_gfns && ring_size); trace_kvm_dirty_ring_reap_vcpu(cpu->cpu_index); -- 2.20.1