migration: optimize the downtime

Qemu_savevm_state_cleanup takes about 300ms in my ram migration tests
with a 8U24G vm(20G is really occupied), the main cost comes from
KVM_SET_USER_MEMORY_REGION ioctl when mem.memory_size = 0 in
kvm_set_user_memory_region. In kmod, the main cost is
kvm_zap_obsolete_pages, which traverses the active_mmu_pages list to
zap the unsync sptes.

It can be optimized by delaying memory_global_dirty_log_stop to the next
vm_start.

Changes v2->v3:
 - NULL VMChangeStateHandler if it is deleted and protect the scenario
   of nested invocations of memory_global_dirty_log_start/stop [Paolo]

Changes v1->v2:
 - create a VMChangeStateHandler in memory.c to reduce the coupling [Paolo]

Signed-off-by: Jay Zhou <jianjay.zhou@huawei.com>
Message-Id: <1501237733-2736-1-git-send-email-jianjay.zhou@huawei.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
This commit is contained in:
Jay Zhou 2017-07-28 18:28:53 +08:00 committed by Paolo Bonzini
parent 8bfce83a3b
commit 1931076077

View File

@ -2357,8 +2357,15 @@ void memory_global_dirty_log_sync(void)
} }
} }
static VMChangeStateEntry *vmstate_change;
void memory_global_dirty_log_start(void) void memory_global_dirty_log_start(void)
{ {
if (vmstate_change) {
qemu_del_vm_change_state_handler(vmstate_change);
vmstate_change = NULL;
}
global_dirty_log = true; global_dirty_log = true;
MEMORY_LISTENER_CALL_GLOBAL(log_global_start, Forward); MEMORY_LISTENER_CALL_GLOBAL(log_global_start, Forward);
@ -2369,7 +2376,7 @@ void memory_global_dirty_log_start(void)
memory_region_transaction_commit(); memory_region_transaction_commit();
} }
void memory_global_dirty_log_stop(void) static void memory_global_dirty_log_do_stop(void)
{ {
global_dirty_log = false; global_dirty_log = false;
@ -2381,6 +2388,33 @@ void memory_global_dirty_log_stop(void)
MEMORY_LISTENER_CALL_GLOBAL(log_global_stop, Reverse); MEMORY_LISTENER_CALL_GLOBAL(log_global_stop, Reverse);
} }
static void memory_vm_change_state_handler(void *opaque, int running,
RunState state)
{
if (running) {
memory_global_dirty_log_do_stop();
if (vmstate_change) {
qemu_del_vm_change_state_handler(vmstate_change);
vmstate_change = NULL;
}
}
}
void memory_global_dirty_log_stop(void)
{
if (!runstate_is_running()) {
if (vmstate_change) {
return;
}
vmstate_change = qemu_add_vm_change_state_handler(
memory_vm_change_state_handler, NULL);
return;
}
memory_global_dirty_log_do_stop();
}
static void listener_add_address_space(MemoryListener *listener, static void listener_add_address_space(MemoryListener *listener,
AddressSpace *as) AddressSpace *as)
{ {