|
|
dc1fe0 |
From b3ed8e344c733bc8c2223c1b9e424a9fbcea56d4 Mon Sep 17 00:00:00 2001
|
|
|
dc1fe0 |
From: Peter Xu <peterx@redhat.com>
|
|
|
dc1fe0 |
Date: Mon, 7 Feb 2022 20:30:19 +0800
|
|
|
dc1fe0 |
Subject: [PATCH 6/6] memory: Fix qemu crash on starting dirty log twice with
|
|
|
dc1fe0 |
stopped VM
|
|
|
dc1fe0 |
|
|
|
dc1fe0 |
RH-Author: Peter Xu <peterx@redhat.com>
|
|
|
dc1fe0 |
RH-MergeRequest: 77: memory: Fix qemu crash on continuous migrations of stopped VM
|
|
|
dc1fe0 |
RH-Commit: [2/2] 98ed2ef6226ec80a1896ebb554015aded0dc0c18 (peterx/qemu-kvm)
|
|
|
dc1fe0 |
RH-Bugzilla: 2044818
|
|
|
dc1fe0 |
RH-Acked-by: Paolo Bonzini <None>
|
|
|
dc1fe0 |
RH-Acked-by: David Hildenbrand <david@redhat.com>
|
|
|
dc1fe0 |
RH-Acked-by: quintela1 <quintela@redhat.com>
|
|
|
dc1fe0 |
|
|
|
dc1fe0 |
QEMU can now easily crash with two continuous migration carried out:
|
|
|
dc1fe0 |
|
|
|
dc1fe0 |
(qemu) migrate -d exec:cat>out
|
|
|
dc1fe0 |
(qemu) migrate_cancel
|
|
|
dc1fe0 |
(qemu) migrate -d exec:cat>out
|
|
|
dc1fe0 |
[crash] ../softmmu/memory.c:2782: memory_global_dirty_log_start: Assertion
|
|
|
dc1fe0 |
`!(global_dirty_tracking & flags)' failed.
|
|
|
dc1fe0 |
|
|
|
dc1fe0 |
It's because memory API provides a way to postpone dirty log stop if the VM is
|
|
|
dc1fe0 |
stopped, and that'll be re-done until the next VM start. It was added in 2017
|
|
|
dc1fe0 |
with commit 1931076077 ("migration: optimize the downtime", 2017-08-01).
|
|
|
dc1fe0 |
|
|
|
dc1fe0 |
However the recent work on allowing dirty tracking to be bitmask broke it,
|
|
|
dc1fe0 |
which is commit 63b41db4bc ("memory: make global_dirty_tracking a bitmask",
|
|
|
dc1fe0 |
2021-11-01).
|
|
|
dc1fe0 |
|
|
|
dc1fe0 |
The fix proposed in this patch contains two things:
|
|
|
dc1fe0 |
|
|
|
dc1fe0 |
(1) Instead of passing over the flags to postpone stop dirty track, we add a
|
|
|
dc1fe0 |
global variable (along with current vmstate_change variable) to record
|
|
|
dc1fe0 |
what flags to stop dirty tracking.
|
|
|
dc1fe0 |
|
|
|
dc1fe0 |
(2) When start dirty tracking, instead if remove the vmstate hook directly,
|
|
|
dc1fe0 |
we also execute the postponed stop process so that we make sure all the
|
|
|
dc1fe0 |
starts and stops will be paired.
|
|
|
dc1fe0 |
|
|
|
dc1fe0 |
This procedure is overlooked in the bitmask-ify work in 2021.
|
|
|
dc1fe0 |
|
|
|
dc1fe0 |
Cc: Hyman Huang <huangy81@chinatelecom.cn>
|
|
|
dc1fe0 |
Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2044818
|
|
|
dc1fe0 |
Fixes: 63b41db4bc ("memory: make global_dirty_tracking a bitmask")
|
|
|
dc1fe0 |
Signed-off-by: Peter Xu <peterx@redhat.com>
|
|
|
dc1fe0 |
Message-Id: <20220207123019.27223-1-peterx@redhat.com>
|
|
|
dc1fe0 |
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
|
|
|
dc1fe0 |
(cherry picked from commit a5c90c61a118027b86155cffdf4fe4e2e9de1020)
|
|
|
dc1fe0 |
Signed-off-by: Peter Xu <peterx@redhat.com>
|
|
|
dc1fe0 |
---
|
|
|
dc1fe0 |
softmmu/memory.c | 61 +++++++++++++++++++++++++++++++++++-------------
|
|
|
dc1fe0 |
1 file changed, 45 insertions(+), 16 deletions(-)
|
|
|
dc1fe0 |
|
|
|
dc1fe0 |
diff --git a/softmmu/memory.c b/softmmu/memory.c
|
|
|
dc1fe0 |
index 81d4bf1454..0311e362ee 100644
|
|
|
dc1fe0 |
--- a/softmmu/memory.c
|
|
|
dc1fe0 |
+++ b/softmmu/memory.c
|
|
|
dc1fe0 |
@@ -2769,19 +2769,32 @@ void memory_global_after_dirty_log_sync(void)
|
|
|
dc1fe0 |
MEMORY_LISTENER_CALL_GLOBAL(log_global_after_sync, Forward);
|
|
|
dc1fe0 |
}
|
|
|
dc1fe0 |
|
|
|
dc1fe0 |
+/*
|
|
|
dc1fe0 |
+ * Dirty track stop flags that are postponed due to VM being stopped. Should
|
|
|
dc1fe0 |
+ * only be used within vmstate_change hook.
|
|
|
dc1fe0 |
+ */
|
|
|
dc1fe0 |
+static unsigned int postponed_stop_flags;
|
|
|
dc1fe0 |
static VMChangeStateEntry *vmstate_change;
|
|
|
dc1fe0 |
+static void memory_global_dirty_log_stop_postponed_run(void);
|
|
|
dc1fe0 |
|
|
|
dc1fe0 |
void memory_global_dirty_log_start(unsigned int flags)
|
|
|
dc1fe0 |
{
|
|
|
dc1fe0 |
- unsigned int old_flags = global_dirty_tracking;
|
|
|
dc1fe0 |
+ unsigned int old_flags;
|
|
|
dc1fe0 |
+
|
|
|
dc1fe0 |
+ assert(flags && !(flags & (~GLOBAL_DIRTY_MASK)));
|
|
|
dc1fe0 |
|
|
|
dc1fe0 |
if (vmstate_change) {
|
|
|
dc1fe0 |
- qemu_del_vm_change_state_handler(vmstate_change);
|
|
|
dc1fe0 |
- vmstate_change = NULL;
|
|
|
dc1fe0 |
+ /* If there is postponed stop(), operate on it first */
|
|
|
dc1fe0 |
+ postponed_stop_flags &= ~flags;
|
|
|
dc1fe0 |
+ memory_global_dirty_log_stop_postponed_run();
|
|
|
dc1fe0 |
}
|
|
|
dc1fe0 |
|
|
|
dc1fe0 |
- assert(flags && !(flags & (~GLOBAL_DIRTY_MASK)));
|
|
|
dc1fe0 |
- assert(!(global_dirty_tracking & flags));
|
|
|
dc1fe0 |
+ flags &= ~global_dirty_tracking;
|
|
|
dc1fe0 |
+ if (!flags) {
|
|
|
dc1fe0 |
+ return;
|
|
|
dc1fe0 |
+ }
|
|
|
dc1fe0 |
+
|
|
|
dc1fe0 |
+ old_flags = global_dirty_tracking;
|
|
|
dc1fe0 |
global_dirty_tracking |= flags;
|
|
|
dc1fe0 |
trace_global_dirty_changed(global_dirty_tracking);
|
|
|
dc1fe0 |
|
|
|
dc1fe0 |
@@ -2809,29 +2822,45 @@ static void memory_global_dirty_log_do_stop(unsigned int flags)
|
|
|
dc1fe0 |
}
|
|
|
dc1fe0 |
}
|
|
|
dc1fe0 |
|
|
|
dc1fe0 |
+/*
|
|
|
dc1fe0 |
+ * Execute the postponed dirty log stop operations if there is, then reset
|
|
|
dc1fe0 |
+ * everything (including the flags and the vmstate change hook).
|
|
|
dc1fe0 |
+ */
|
|
|
dc1fe0 |
+static void memory_global_dirty_log_stop_postponed_run(void)
|
|
|
dc1fe0 |
+{
|
|
|
dc1fe0 |
+ /* This must be called with the vmstate handler registered */
|
|
|
dc1fe0 |
+ assert(vmstate_change);
|
|
|
dc1fe0 |
+
|
|
|
dc1fe0 |
+ /* Note: postponed_stop_flags can be cleared in log start routine */
|
|
|
dc1fe0 |
+ if (postponed_stop_flags) {
|
|
|
dc1fe0 |
+ memory_global_dirty_log_do_stop(postponed_stop_flags);
|
|
|
dc1fe0 |
+ postponed_stop_flags = 0;
|
|
|
dc1fe0 |
+ }
|
|
|
dc1fe0 |
+
|
|
|
dc1fe0 |
+ qemu_del_vm_change_state_handler(vmstate_change);
|
|
|
dc1fe0 |
+ vmstate_change = NULL;
|
|
|
dc1fe0 |
+}
|
|
|
dc1fe0 |
+
|
|
|
dc1fe0 |
static void memory_vm_change_state_handler(void *opaque, bool running,
|
|
|
dc1fe0 |
RunState state)
|
|
|
dc1fe0 |
{
|
|
|
dc1fe0 |
- unsigned int flags = (unsigned int)(uintptr_t)opaque;
|
|
|
dc1fe0 |
if (running) {
|
|
|
dc1fe0 |
- memory_global_dirty_log_do_stop(flags);
|
|
|
dc1fe0 |
-
|
|
|
dc1fe0 |
- if (vmstate_change) {
|
|
|
dc1fe0 |
- qemu_del_vm_change_state_handler(vmstate_change);
|
|
|
dc1fe0 |
- vmstate_change = NULL;
|
|
|
dc1fe0 |
- }
|
|
|
dc1fe0 |
+ memory_global_dirty_log_stop_postponed_run();
|
|
|
dc1fe0 |
}
|
|
|
dc1fe0 |
}
|
|
|
dc1fe0 |
|
|
|
dc1fe0 |
void memory_global_dirty_log_stop(unsigned int flags)
|
|
|
dc1fe0 |
{
|
|
|
dc1fe0 |
if (!runstate_is_running()) {
|
|
|
dc1fe0 |
+ /* Postpone the dirty log stop, e.g., to when VM starts again */
|
|
|
dc1fe0 |
if (vmstate_change) {
|
|
|
dc1fe0 |
- return;
|
|
|
dc1fe0 |
+ /* Batch with previous postponed flags */
|
|
|
dc1fe0 |
+ postponed_stop_flags |= flags;
|
|
|
dc1fe0 |
+ } else {
|
|
|
dc1fe0 |
+ postponed_stop_flags = flags;
|
|
|
dc1fe0 |
+ vmstate_change = qemu_add_vm_change_state_handler(
|
|
|
dc1fe0 |
+ memory_vm_change_state_handler, NULL);
|
|
|
dc1fe0 |
}
|
|
|
dc1fe0 |
- vmstate_change = qemu_add_vm_change_state_handler(
|
|
|
dc1fe0 |
- memory_vm_change_state_handler,
|
|
|
dc1fe0 |
- (void *)(uintptr_t)flags);
|
|
|
dc1fe0 |
return;
|
|
|
dc1fe0 |
}
|
|
|
dc1fe0 |
|
|
|
dc1fe0 |
--
|
|
|
dc1fe0 |
2.27.0
|
|
|
dc1fe0 |
|