From 8990e1d2fbca42578b7257bfdd73b34fdad0eee6 Mon Sep 17 00:00:00 2001
From: Gerd Hoffmann <kraxel@redhat.com>
Date: Wed, 7 Mar 2012 08:00:16 +0100
Subject: [PATCH 02/12] suspend: add infrastructure

RH-Author: Gerd Hoffmann <kraxel@redhat.com>
Message-id: <1331107226-21901-3-git-send-email-kraxel@redhat.com>
Patchwork-id: 38354
O-Subject: [RHEL-6.3 qemu-kvm PATCH v3 02/12] suspend: add infrastructure
Bugzilla: 766303
RH-Acked-by: Paolo Bonzini <pbonzini@redhat.com>
RH-Acked-by: Gleb Natapov <gleb@redhat.com>
RH-Acked-by: Alex Williamson <alex.williamson@redhat.com>

This patch adds some infrastructure to handle suspend and resume to
qemu.  First there are two functions to switch state and second there
is a suspend notifier:

 * qemu_system_suspend_request is supposed to be called when the
   guest asks for being be suspended, for example via ACPI.

 * qemu_system_wakeup_request is supposed to be called on events
   which should wake up the guest.

 * qemu_register_suspend_notifier can be used to register a notifier
   which will be called when the guest is suspended.  Machine types
   and device models can hook in there to modify state if needed.

 * qemu_register_wakeup_notifier can be used to register a notifier
   which will be called when the guest is woken up.  Machine types
   and device models can hook in there to modify state if needed.

 * qemu_system_wakeup_enable can be used to enable/disable wakeup
   events.

Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>

[ rhel6: hook into both main_loop and kvm_main_loop ]

upstream: 95b363b5c643d8ad81c5377726e753b84c909037
---
 qemu-kvm.c |    4 ++-
 sysemu.h   |   11 +++++++++
 vl.c       |   72 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 86 insertions(+), 1 deletions(-)

Signed-off-by: Michal Novotny <minovotn@redhat.com>
---
 qemu-kvm.c |    4 ++-
 sysemu.h   |   11 +++++++++
 vl.c       |   72 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 86 insertions(+), 1 deletions(-)

diff --git a/qemu-kvm.c b/qemu-kvm.c
index 6edb5f8..bd3b4a5 100644
--- a/qemu-kvm.c
+++ b/qemu-kvm.c
@@ -2242,7 +2242,9 @@ int kvm_main_loop(void)
 
     while (1) {
         main_loop_wait(1000);
-        if (qemu_shutdown_requested()) {
+        if (qemu_suspend_requested()) {
+            qemu_system_suspend();
+        } else if (qemu_shutdown_requested()) {
             qemu_kill_report();
             monitor_protocol_event(QEVENT_SHUTDOWN, NULL);
             if (qemu_no_shutdown()) {
diff --git a/sysemu.h b/sysemu.h
index 45fd915..e968915 100644
--- a/sysemu.h
+++ b/sysemu.h
@@ -67,17 +67,28 @@ int64_t cpu_get_ticks(void);
 void cpu_enable_ticks(void);
 void cpu_disable_ticks(void);
 
+typedef enum WakeupReason {
+    QEMU_WAKEUP_REASON_OTHER = 0,
+} WakeupReason;
+
 void qemu_system_reset_request(void);
+void qemu_system_suspend_request(void);
+void qemu_register_suspend_notifier(Notifier *notifier);
+void qemu_system_wakeup_request(WakeupReason reason);
+void qemu_system_wakeup_enable(WakeupReason reason, bool enabled);
+void qemu_register_wakeup_notifier(Notifier *notifier);
 void qemu_system_shutdown_request(void);
 void qemu_system_powerdown_request(void);
 int qemu_no_shutdown(void);
 int qemu_shutdown_requested(void);
 int qemu_reset_requested(void);
+int qemu_suspend_requested(void);
 int qemu_powerdown_requested(void);
 void qemu_system_killed(int signal, pid_t pid);
 void qemu_kill_report(void);
 extern qemu_irq qemu_system_powerdown;
 void qemu_system_reset(void);
+void qemu_system_suspend(void);
 
 void qemu_add_machine_init_done_notifier(Notifier *notify);
 void qemu_add_exit_notifier(Notifier *notify);
diff --git a/vl.c b/vl.c
index 38fb0a5..51cff6e 100644
--- a/vl.c
+++ b/vl.c
@@ -3283,6 +3283,13 @@ static int shutdown_requested, shutdown_signal = -1;
 static pid_t shutdown_pid;
 static int powerdown_requested;
 static int debug_requested;
+static int suspend_requested;
+static bool is_suspended;
+static NotifierList suspend_notifiers =
+    NOTIFIER_LIST_INITIALIZER(suspend_notifiers);
+static NotifierList wakeup_notifiers =
+    NOTIFIER_LIST_INITIALIZER(wakeup_notifiers);
+static uint32_t wakeup_reason_mask = ~0;
 static int vmstop_requested = RUN_STATE_NO_STATE;
 
 int qemu_no_shutdown(void)
@@ -3321,6 +3328,13 @@ int qemu_reset_requested(void)
     return r;
 }
 
+int qemu_suspend_requested(void)
+{
+    int r = suspend_requested;
+    suspend_requested = 0;
+    return r;
+}
+
 int qemu_powerdown_requested(void)
 {
     int r = powerdown_requested;
@@ -3400,6 +3414,59 @@ void qemu_system_reset_request(void)
     qemu_notify_event();
 }
 
+void qemu_system_suspend(void)
+{
+    pause_all_vcpus();
+    notifier_list_notify(&suspend_notifiers, NULL);
+    is_suspended = true;
+}
+
+void qemu_system_suspend_request(void)
+{
+    if (is_suspended) {
+        return;
+    }
+    suspend_requested = 1;
+    if (cpu_single_env) {
+        cpu_single_env->stopped = 1;
+        cpu_exit(cpu_single_env);
+    }
+    qemu_notify_event();
+}
+
+void qemu_register_suspend_notifier(Notifier *notifier)
+{
+    notifier_list_add(&suspend_notifiers, notifier);
+}
+
+void qemu_system_wakeup_request(WakeupReason reason)
+{
+    if (!is_suspended) {
+        return;
+    }
+    if (!(wakeup_reason_mask & (1 << reason))) {
+        return;
+    }
+    notifier_list_notify(&wakeup_notifiers, &reason);
+    reset_requested = 1;
+    qemu_notify_event();
+    is_suspended = false;
+}
+
+void qemu_system_wakeup_enable(WakeupReason reason, bool enabled)
+{
+    if (enabled) {
+        wakeup_reason_mask |= (1 << reason);
+    } else {
+        wakeup_reason_mask &= ~(1 << reason);
+    }
+}
+
+void qemu_register_wakeup_notifier(Notifier *notifier)
+{
+    notifier_list_add(&wakeup_notifiers, notifier);
+}
+
 void qemu_system_killed(int signal, pid_t pid)
 {
     shutdown_signal = signal;
@@ -4218,6 +4285,8 @@ static int vm_can_run(void)
         return 0;
     if (debug_requested)
         return 0;
+    if (suspend_requested)
+        return 0;
     return 1;
 }
 
@@ -4258,6 +4327,9 @@ static void main_loop(void)
         if (qemu_debug_requested()) {
             vm_stop(RUN_STATE_DEBUG);
         }
+        if (qemu_suspend_requested()) {
+            qemu_system_suspend();
+        }
         if (qemu_shutdown_requested()) {
             monitor_protocol_event(QEVENT_SHUTDOWN, NULL);
             if (no_shutdown) {
-- 
1.7.7.6