]> git.proxmox.com Git - mirror_qemu.git/blobdiff - replay/replay-internal.c
block: add ability to specify list of blockdevs during snapshot
[mirror_qemu.git] / replay / replay-internal.c
index bea7b4aa6b18a2875f413ab9292b248aa723a093..2e8a3e947a5b22faaae14ca8c1c85439dafce720 100644 (file)
  */
 
 #include "qemu/osdep.h"
-#include "qemu-common.h"
 #include "sysemu/replay.h"
+#include "sysemu/runstate.h"
 #include "replay-internal.h"
 #include "qemu/error-report.h"
-#include "sysemu/sysemu.h"
+#include "qemu/main-loop.h"
 
 /* Mutex to protect reading and writing events to the log.
    data_kind and has_unread_data are also protected
    It also protects replay events queue which stores events to be
    written or read to the log. */
 static QemuMutex lock;
+/* Condition and queue for fair ordering of mutex lock requests. */
+static QemuCond mutex_cond;
+static unsigned long mutex_head, mutex_tail;
 
 /* File for replay writing */
+static bool write_error;
 FILE *replay_file;
 
+static void replay_write_error(void)
+{
+    if (!write_error) {
+        error_report("replay write error");
+        write_error = true;
+    }
+}
+
+static void replay_read_error(void)
+{
+    error_report("error reading the replay data");
+    exit(1);
+}
+
 void replay_put_byte(uint8_t byte)
 {
     if (replay_file) {
-        putc(byte, replay_file);
+        if (putc(byte, replay_file) == EOF) {
+            replay_write_error();
+        }
     }
 }
 
@@ -62,7 +82,9 @@ void replay_put_array(const uint8_t *buf, size_t size)
 {
     if (replay_file) {
         replay_put_dword(size);
-        fwrite(buf, 1, size, replay_file);
+        if (fwrite(buf, 1, size, replay_file) != size) {
+            replay_write_error();
+        }
     }
 }
 
@@ -70,7 +92,11 @@ uint8_t replay_get_byte(void)
 {
     uint8_t byte = 0;
     if (replay_file) {
-        byte = getc(replay_file);
+        int r = getc(replay_file);
+        if (r == EOF) {
+            replay_read_error();
+        }
+        byte = r;
     }
     return byte;
 }
@@ -113,7 +139,7 @@ void replay_get_array(uint8_t *buf, size_t *size)
     if (replay_file) {
         *size = replay_get_dword();
         if (fread(buf, 1, *size, replay_file) != *size) {
-            error_report("replay read error");
+            replay_read_error();
         }
     }
 }
@@ -124,7 +150,7 @@ void replay_get_array_alloc(uint8_t **buf, size_t *size)
         *size = replay_get_dword();
         *buf = g_malloc(*size);
         if (fread(*buf, 1, *size, replay_file) != *size) {
-            error_report("replay read error");
+            replay_read_error();
         }
     }
 }
@@ -150,7 +176,7 @@ void replay_fetch_data_kind(void)
         if (!replay_state.has_unread_data) {
             replay_state.data_kind = replay_get_byte();
             if (replay_state.data_kind == EVENT_INSTRUCTION) {
-                replay_state.instructions_count = replay_get_dword();
+                replay_state.instruction_count = replay_get_dword();
             }
             replay_check_error();
             replay_state.has_unread_data = 1;
@@ -169,37 +195,70 @@ void replay_finish_event(void)
     replay_fetch_data_kind();
 }
 
+static __thread bool replay_locked;
+
 void replay_mutex_init(void)
 {
     qemu_mutex_init(&lock);
+    qemu_cond_init(&mutex_cond);
+    /* Hold the mutex while we start-up */
+    replay_locked = true;
+    ++mutex_tail;
 }
 
-void replay_mutex_destroy(void)
+bool replay_mutex_locked(void)
 {
-    qemu_mutex_destroy(&lock);
+    return replay_locked;
 }
 
+/* Ordering constraints, replay_lock must be taken before BQL */
 void replay_mutex_lock(void)
 {
-    qemu_mutex_lock(&lock);
+    if (replay_mode != REPLAY_MODE_NONE) {
+        unsigned long id;
+        g_assert(!qemu_mutex_iothread_locked());
+        g_assert(!replay_mutex_locked());
+        qemu_mutex_lock(&lock);
+        id = mutex_tail++;
+        while (id != mutex_head) {
+            qemu_cond_wait(&mutex_cond, &lock);
+        }
+        replay_locked = true;
+        qemu_mutex_unlock(&lock);
+    }
 }
 
 void replay_mutex_unlock(void)
 {
-    qemu_mutex_unlock(&lock);
+    if (replay_mode != REPLAY_MODE_NONE) {
+        g_assert(replay_mutex_locked());
+        qemu_mutex_lock(&lock);
+        ++mutex_head;
+        replay_locked = false;
+        qemu_cond_broadcast(&mutex_cond);
+        qemu_mutex_unlock(&lock);
+    }
+}
+
+void replay_advance_current_icount(uint64_t current_icount)
+{
+    int diff = (int)(current_icount - replay_state.current_icount);
+
+    /* Time can only go forward */
+    assert(diff >= 0);
+
+    if (diff > 0) {
+        replay_put_event(EVENT_INSTRUCTION);
+        replay_put_dword(diff);
+        replay_state.current_icount += diff;
+    }
 }
 
 /*! Saves cached instructions. */
 void replay_save_instructions(void)
 {
     if (replay_file && replay_mode == REPLAY_MODE_RECORD) {
-        replay_mutex_lock();
-        int diff = (int)(replay_get_current_step() - replay_state.current_step);
-        if (diff > 0) {
-            replay_put_event(EVENT_INSTRUCTION);
-            replay_put_dword(diff);
-            replay_state.current_step += diff;
-        }
-        replay_mutex_unlock();
+        g_assert(replay_mutex_locked());
+        replay_advance_current_icount(replay_get_current_icount());
     }
 }