Blame SOURCES/kvm-memory-Fix-qemu-crash-on-starting-dirty-log-twice-wi.patch

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