All of lore.kernel.org
 help / color / mirror / Atom feed
* [Qemu-devel] [PULL 0/6] qemu-ga queue and qemu-tool fixes
@ 2012-01-25 15:26 Michael Roth
  2012-01-25 15:26 ` [Qemu-devel] [PATCH 1/6] main-loop: Fix SetEvent() on uninitialized handle on win32 Michael Roth
                   ` (5 more replies)
  0 siblings, 6 replies; 7+ messages in thread
From: Michael Roth @ 2012-01-25 15:26 UTC (permalink / raw)
  To: qemu-devel; +Cc: aliguori

This pull adds 2 RPCs (guest-suspend and guest-set-support-level) to qemu-ga and some includes fixes/workarounds for building tools on Windows.

The following changes since commit 5b4448d27d7c6ff6e18a1edc8245cb1db783e37c:

  Merge remote-tracking branch 'qemu-kvm/uq/master' into staging (2012-01-23 11:00:26 -0600)

are available in the git repository at:

  git://github.com/mdroth/qemu.git qga-pull-2012-01-23

Luiz Capitulino (2):
      qemu-ga: set O_NONBLOCK for serial channels
      qemu-ga: Add the guest-suspend command

Michael Roth (4):
      main-loop: Fix SetEvent() on uninitialized handle on win32
      main-loop: For tools, initialize timers as part of qemu_init_main_loop()
      qemu-ga: Add schema documentation for types
      qemu-ga: add guest-set-support-level command

 main-loop.c                |    7 +-
 main-loop.h                |   12 ++
 qapi-schema-guest.json     |  217 ++++++++++++++++++++++++++++++++++++----
 qemu-ga.c                  |   66 ++++++++++++-
 qemu-tool.c                |    3 +-
 qga/guest-agent-commands.c |  239 ++++++++++++++++++++++++++++++++++++++++++++
 qga/guest-agent-core.h     |   11 ++
 vl.c                       |    5 +
 8 files changed, 533 insertions(+), 27 deletions(-)

^ permalink raw reply	[flat|nested] 7+ messages in thread

* [Qemu-devel] [PATCH 1/6] main-loop: Fix SetEvent() on uninitialized handle on win32
  2012-01-25 15:26 [Qemu-devel] [PULL 0/6] qemu-ga queue and qemu-tool fixes Michael Roth
@ 2012-01-25 15:26 ` Michael Roth
  2012-01-25 15:26 ` [Qemu-devel] [PATCH 2/6] main-loop: For tools, initialize timers as part of qemu_init_main_loop() Michael Roth
                   ` (4 subsequent siblings)
  5 siblings, 0 replies; 7+ messages in thread
From: Michael Roth @ 2012-01-25 15:26 UTC (permalink / raw)
  To: qemu-devel; +Cc: aliguori

The __attribute__((constructor)) init_main_loop() automatically get
called if qemu-tool.o is linked in. On win32, this leads to
a qemu_notify_event() call which attempts to SetEvent() on a HANDLE that
won't be initialized until qemu_init_main_loop() is manually called,
breaking qemu-tools.o programs on Windows at runtime.

This patch checks for an initialized event handle before attempting to
set it, which is analoguous to how we deal with an unitialized
io_thread_fd in the posix implementation.

Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
---
 main-loop.c |    5 ++++-
 1 files changed, 4 insertions(+), 1 deletions(-)

diff --git a/main-loop.c b/main-loop.c
index 692381c..62d95b9 100644
--- a/main-loop.c
+++ b/main-loop.c
@@ -164,7 +164,7 @@ static int qemu_signal_init(void)
 
 #else /* _WIN32 */
 
-HANDLE qemu_event_handle;
+HANDLE qemu_event_handle = NULL;
 
 static void dummy_event_handler(void *opaque)
 {
@@ -183,6 +183,9 @@ static int qemu_event_init(void)
 
 void qemu_notify_event(void)
 {
+    if (!qemu_event_handle) {
+        return;
+    }
     if (!SetEvent(qemu_event_handle)) {
         fprintf(stderr, "qemu_notify_event: SetEvent failed: %ld\n",
                 GetLastError());
-- 
1.7.4.1

^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [Qemu-devel] [PATCH 2/6] main-loop: For tools, initialize timers as part of qemu_init_main_loop()
  2012-01-25 15:26 [Qemu-devel] [PULL 0/6] qemu-ga queue and qemu-tool fixes Michael Roth
  2012-01-25 15:26 ` [Qemu-devel] [PATCH 1/6] main-loop: Fix SetEvent() on uninitialized handle on win32 Michael Roth
@ 2012-01-25 15:26 ` Michael Roth
  2012-01-25 15:26 ` [Qemu-devel] [PATCH 3/6] qemu-ga: Add schema documentation for types Michael Roth
                   ` (3 subsequent siblings)
  5 siblings, 0 replies; 7+ messages in thread
From: Michael Roth @ 2012-01-25 15:26 UTC (permalink / raw)
  To: qemu-devel; +Cc: aliguori

In some cases initializing the alarm timers can lead to non-negligable
overhead from programs that link against qemu-tool.o. At least,
setting a max-resolution WinMM alarm timer via mm_start_timer() (the
current default for Windows) can increase the "tick rate" on Windows
OSs and affect frequency scaling, and in the case of tools that run
in guest OSs such has qemu-ga, the impact can be fairly dramatic
(+20%/20% user/sys time on a core 2 processor was observed from an idle
Windows XP guest).

This patch doesn't address the issue directly (not sure what a good
solution would be for Windows, or what other situations it might be
noticeable), but it at least limits the scope of the issue to programs
that "opt-in" to using the main-loop.c functions by only enabling alarm
timers when qemu_init_main_loop() is called, which is already required
to make use of those facilities, so existing users shouldn't be
affected.

Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
---
 main-loop.c |    2 +-
 main-loop.h |   12 ++++++++++++
 qemu-tool.c |    3 ++-
 vl.c        |    5 +++++
 4 files changed, 20 insertions(+), 2 deletions(-)

diff --git a/main-loop.c b/main-loop.c
index 62d95b9..db23de0 100644
--- a/main-loop.c
+++ b/main-loop.c
@@ -199,7 +199,7 @@ static int qemu_signal_init(void)
 }
 #endif
 
-int qemu_init_main_loop(void)
+int main_loop_init(void)
 {
     int ret;
 
diff --git a/main-loop.h b/main-loop.h
index f971013..4987041 100644
--- a/main-loop.h
+++ b/main-loop.h
@@ -41,10 +41,22 @@
  * SIGUSR2, thread signals (SIGFPE, SIGILL, SIGSEGV, SIGBUS) and real-time
  * signals if available.  Remember that Windows in practice does not have
  * signals, though.
+ *
+ * In the case of QEMU tools, this will also start/initialize timers.
  */
 int qemu_init_main_loop(void);
 
 /**
+ * main_loop_init: Initializes main loop
+ *
+ * Internal (but shared for compatibility reasons) initialization routine
+ * for the main loop. This should not be used by applications directly,
+ * use qemu_init_main_loop() instead.
+ *
+ */
+int main_loop_init(void);
+
+/**
  * main_loop_wait: Run one iteration of the main loop.
  *
  * If @nonblocking is true, poll for events, otherwise suspend until
diff --git a/qemu-tool.c b/qemu-tool.c
index 6b69668..183a583 100644
--- a/qemu-tool.c
+++ b/qemu-tool.c
@@ -83,11 +83,12 @@ void qemu_clock_warp(QEMUClock *clock)
 {
 }
 
-static void __attribute__((constructor)) init_main_loop(void)
+int qemu_init_main_loop(void)
 {
     init_clocks();
     init_timer_alarm();
     qemu_clock_enable(vm_clock, false);
+    return main_loop_init();
 }
 
 void slirp_select_fill(int *pnfds, fd_set *readfds,
diff --git a/vl.c b/vl.c
index 57378b6..9d14cc8 100644
--- a/vl.c
+++ b/vl.c
@@ -2159,6 +2159,11 @@ static void free_and_trace(gpointer mem)
     free(mem);
 }
 
+int qemu_init_main_loop(void)
+{
+    return main_loop_init();
+}
+
 int main(int argc, char **argv, char **envp)
 {
     const char *gdbstub_dev = NULL;
-- 
1.7.4.1

^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [Qemu-devel] [PATCH 3/6] qemu-ga: Add schema documentation for types
  2012-01-25 15:26 [Qemu-devel] [PULL 0/6] qemu-ga queue and qemu-tool fixes Michael Roth
  2012-01-25 15:26 ` [Qemu-devel] [PATCH 1/6] main-loop: Fix SetEvent() on uninitialized handle on win32 Michael Roth
  2012-01-25 15:26 ` [Qemu-devel] [PATCH 2/6] main-loop: For tools, initialize timers as part of qemu_init_main_loop() Michael Roth
@ 2012-01-25 15:26 ` Michael Roth
  2012-01-25 15:26 ` [Qemu-devel] [PATCH 4/6] qemu-ga: add guest-set-support-level command Michael Roth
                   ` (2 subsequent siblings)
  5 siblings, 0 replies; 7+ messages in thread
From: Michael Roth @ 2012-01-25 15:26 UTC (permalink / raw)
  To: qemu-devel; +Cc: aliguori

Document guest agent schema types in similar fashion as qmp schema
types.

Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
---
 qapi-schema-guest.json |  118 +++++++++++++++++++++++++++++++++++++++---------
 1 files changed, 97 insertions(+), 21 deletions(-)

diff --git a/qapi-schema-guest.json b/qapi-schema-guest.json
index 5f8a18d..706925d 100644
--- a/qapi-schema-guest.json
+++ b/qapi-schema-guest.json
@@ -37,17 +37,42 @@
 { 'command': 'guest-ping' }
 
 ##
-# @guest-info:
+# @GuestAgentCommandInfo:
 #
-# Get some information about the guest agent.
+# Information about guest agent commands.
 #
-# Since: 0.15.0
+# @name: name of the command
+#
+# @enabled: whether command is currently enabled by guest admin
+#
+# Since 1.1.0
 ##
 { 'type': 'GuestAgentCommandInfo',
   'data': { 'name': 'str', 'enabled': 'bool' } }
+
+##
+# @GuestAgentInfo
+#
+# Information about guest agent.
+#
+# @version: guest agent version
+#
+# @supported_commands: Information about guest agent commands
+#
+# Since 0.15.0
+##
 { 'type': 'GuestAgentInfo',
   'data': { 'version': 'str',
             'supported_commands': ['GuestAgentCommandInfo'] } }
+##
+# @guest-info:
+#
+# Get some information about the guest agent.
+#
+# Returns: @GuestAgentInfo
+#
+# Since: 0.15.0
+##
 { 'command': 'guest-info',
   'returns': 'GuestAgentInfo' }
 
@@ -98,6 +123,23 @@
   'data': { 'handle': 'int' } }
 
 ##
+# @GuestFileRead
+#
+# Result of guest agent file-read operation
+#
+# @count: number of bytes read (note: count is *before*
+#         base64-encoding is applied)
+#
+# @buf-b64: base64-encoded bytes read
+#
+# @eof: whether EOF was encountered during read operation.
+#
+# Since: 0.15.0
+##
+{ 'type': 'GuestFileRead',
+  'data': { 'count': 'int', 'buf-b64': 'str', 'eof': 'bool' } }
+
+##
 # @guest-file-read:
 #
 # Read from an open file in the guest. Data will be base64-encoded
@@ -106,19 +148,30 @@
 #
 # @count: #optional maximum number of bytes to read (default is 4KB)
 #
-# Returns: GuestFileRead on success. Note: count is number of bytes read
-#          *before* base64 encoding bytes read.
+# Returns: @GuestFileRead on success.
 #
 # Since: 0.15.0
 ##
-{ 'type': 'GuestFileRead',
-  'data': { 'count': 'int', 'buf-b64': 'str', 'eof': 'bool' } }
-
 { 'command': 'guest-file-read',
   'data':    { 'handle': 'int', '*count': 'int' },
   'returns': 'GuestFileRead' }
 
 ##
+# @GuestFileWrite
+#
+# Result of guest agent file-write operation
+#
+# @count: number of bytes written (note: count is actual bytes
+#         written, after base64-decoding of provided buffer)
+#
+# @eof: whether EOF was encountered during write operation.
+#
+# Since: 0.15.0
+##
+{ 'type': 'GuestFileWrite',
+  'data': { 'count': 'int', 'eof': 'bool' } }
+
+##
 # @guest-file-write:
 #
 # Write to an open file in the guest.
@@ -130,17 +183,29 @@
 # @count: #optional bytes to write (actual bytes, after base64-decode),
 #         default is all content in buf-b64 buffer after base64 decoding
 #
-# Returns: GuestFileWrite on success. Note: count is the number of bytes
-#          base64-decoded bytes written
+# Returns: @GuestFileWrite on success.
 #
 # Since: 0.15.0
 ##
-{ 'type': 'GuestFileWrite',
-  'data': { 'count': 'int', 'eof': 'bool' } }
 { 'command': 'guest-file-write',
   'data':    { 'handle': 'int', 'buf-b64': 'str', '*count': 'int' },
   'returns': 'GuestFileWrite' }
 
+
+##
+# @GuestFileSeek
+#
+# Result of guest agent file-seek operation
+#
+# @position: current file position
+#
+# @eof: whether EOF was encountered during file seek
+#
+# Since: 0.15.0
+##
+{ 'type': 'GuestFileSeek',
+  'data': { 'position': 'int', 'eof': 'bool' } }
+
 ##
 # @guest-file-seek:
 #
@@ -154,13 +219,10 @@
 #
 # @whence: SEEK_SET, SEEK_CUR, or SEEK_END, as with fseek()
 #
-# Returns: GuestFileSeek on success.
+# Returns: @GuestFileSeek on success.
 #
 # Since: 0.15.0
 ##
-{ 'type': 'GuestFileSeek',
-  'data': { 'position': 'int', 'eof': 'bool' } }
-
 { 'command': 'guest-file-seek',
   'data':    { 'handle': 'int', 'offset': 'int', 'whence': 'int' },
   'returns': 'GuestFileSeek' }
@@ -180,18 +242,32 @@
   'data': { 'handle': 'int' } }
 
 ##
-# @guest-fsfreeze-status:
+# @GuestFsFreezeStatus
 #
-# Get guest fsfreeze state. error state indicates failure to thaw 1 or more
-# previously frozen filesystems, or failure to open a previously cached
-# filesytem (filesystem unmounted/directory changes, etc).
+# An enumation of filesystem freeze states
 #
-# Returns: GuestFsfreezeStatus ("thawed", "frozen", etc., as defined below)
+# @thawed: filesystems thawed/unfrozen
+#
+# @frozen: all non-network guest filesystems frozen
+#
+# @error: failure to thaw 1 or more
+#         previously frozen filesystems, or failure to open a previously
+#         cached filesytem (filesystem unmounted/directory changes, etc).
 #
 # Since: 0.15.0
 ##
 { 'enum': 'GuestFsfreezeStatus',
   'data': [ 'thawed', 'frozen', 'error' ] }
+
+##
+# @guest-fsfreeze-status:
+#
+# Get guest fsfreeze state. error state indicates
+#
+# Returns: GuestFsfreezeStatus ("thawed", "frozen", etc., as defined below)
+#
+# Since: 0.15.0
+##
 { 'command': 'guest-fsfreeze-status',
   'returns': 'GuestFsfreezeStatus' }
 
-- 
1.7.4.1

^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [Qemu-devel] [PATCH 4/6] qemu-ga: add guest-set-support-level command
  2012-01-25 15:26 [Qemu-devel] [PULL 0/6] qemu-ga queue and qemu-tool fixes Michael Roth
                   ` (2 preceding siblings ...)
  2012-01-25 15:26 ` [Qemu-devel] [PATCH 3/6] qemu-ga: Add schema documentation for types Michael Roth
@ 2012-01-25 15:26 ` Michael Roth
  2012-01-25 15:26 ` [Qemu-devel] [PATCH 5/6] qemu-ga: set O_NONBLOCK for serial channels Michael Roth
  2012-01-25 15:26 ` [Qemu-devel] [PATCH 6/6] qemu-ga: Add the guest-suspend command Michael Roth
  5 siblings, 0 replies; 7+ messages in thread
From: Michael Roth @ 2012-01-25 15:26 UTC (permalink / raw)
  To: qemu-devel; +Cc: aliguori

Recently commands where introduced on the mailing that involved adding
commands to the guest agent that could potentially break older versions
of QEMU. While it's okay to expect that qemu-ga can be updated to support
newer host features, it's unrealistic to require a host to be updated to
support qemu-ga features, or to expect that qemu-ga should be downgraded
in these circumstances, especially considering that a large mix of
guests/agents may be running on a particular host.

To address this, we introduce here a mechanism to set qemu-ga's
feature-set to one that is known to be compatible with
the host/QEMU running the guest. As new commands/options are added, we
can maintain backward-compatibility by adding conditional checks to filter
out host-incompatible functionality based on the host-specified support
level (generally analogous to the host QEMU version) set by the
client.

The current default/minimum support level supports all versions of QEMU
that have had qemu-ga in-tree (0.15.0, 1.0.0) and so should be
backward-compatible with existing hosts/clients.

Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
---
 qapi-schema-guest.json     |   67 +++++++++++++++++++++++++++++++++++++++++++-
 qemu-ga.c                  |   46 ++++++++++++++++++++++++++++++
 qga/guest-agent-commands.c |   13 ++++++++
 qga/guest-agent-core.h     |   11 +++++++
 4 files changed, 136 insertions(+), 1 deletions(-)

diff --git a/qapi-schema-guest.json b/qapi-schema-guest.json
index 706925d..81cd773 100644
--- a/qapi-schema-guest.json
+++ b/qapi-schema-guest.json
@@ -51,6 +51,27 @@
   'data': { 'name': 'str', 'enabled': 'bool' } }
 
 ##
+# @GuestAgentSupportLevel
+#
+# Guest agent support/compatability level (as configured by
+# guest agent defaults of guest-set-support-level command)
+#
+# Three numerical fields describe the support level:
+#
+# <major>.<minor>.<micro>
+#
+# @major: As above
+#
+# @minor: As above
+#
+# @micro: As above
+#
+# Since 1.1.0
+##
+{ 'type': 'GuestAgentSupportLevel',
+  'data': { 'major': 'int', 'minor': 'int', 'micro': 'int' } }
+
+##
 # @GuestAgentInfo
 #
 # Information about guest agent.
@@ -59,11 +80,17 @@
 #
 # @supported_commands: Information about guest agent commands
 #
+# @support_level: Current support/compatability level, as set by
+#                 guest agent defaults or guest-set-support-level
+#                 command.
+#
 # Since 0.15.0
 ##
 { 'type': 'GuestAgentInfo',
   'data': { 'version': 'str',
-            'supported_commands': ['GuestAgentCommandInfo'] } }
+            'supported_commands': ['GuestAgentCommandInfo']
+            'support_level': 'GuestAgentSupportLevel' } }
+
 ##
 # @guest-info:
 #
@@ -77,6 +104,44 @@
   'returns': 'GuestAgentInfo' }
 
 ##
+# @guest-set-support-level:
+#
+# Set guest agent feature-set to one that is compatible with/supported by
+# the host.
+#
+# Certain commands/options may have dependencies on host
+# version/support-level, such as specific QEMU features (such
+# dependencies will be noted in this schema). By default we assume 1.0.0,
+# which is backward-compatible with QEMU 0.15.0/1.0, and should be compatible
+# with later versions of QEMU as well. To enable newer guest agent features,
+# this command must be issued to raise the support-level to one corresponding
+# to supported host QEMU/KVM/etc capabilities.
+#
+# The host support-level is generally <= host QEMU version
+# level. Note that undefined behavior may occur if a support-level is
+# provided that exceeds the capabilities of the version of QEMU currently
+# running the guest. Any level specified below 1.0.0 will default to 1.0.0.
+#
+# The currently set support level is obtainable via the guest-info command.
+#
+# Three numerical fields describe the host support/compatability level:
+#
+# <major>.<minor>.<micro>
+#
+# @major: As above
+#
+# @minor: As above
+#
+# @micro: As above
+#
+# Returns: Nothing on success
+#
+# Since: 1.1.0
+##
+{ 'command': 'guest-set-support-level',
+  'data':    { 'major': 'int', 'minor': 'int', '*micro': 'int' } }
+
+##
 # @guest-shutdown:
 #
 # Initiate guest-activated shutdown. Note: this is an asynchronous
diff --git a/qemu-ga.c b/qemu-ga.c
index 29e4f64..8e5b075 100644
--- a/qemu-ga.c
+++ b/qemu-ga.c
@@ -28,6 +28,7 @@
 #include "qerror.h"
 #include "error_int.h"
 #include "qapi/qmp-core.h"
+#include "qga-qapi-types.h"
 
 #define QGA_VIRTIO_PATH_DEFAULT "/dev/virtio-ports/org.qemu.guest_agent.0"
 #define QGA_PIDFILE_DEFAULT "/var/run/qemu-ga.pid"
@@ -102,6 +103,45 @@ static void usage(const char *cmd)
 
 static void conn_channel_close(GAState *s);
 
+static GuestAgentSupportLevel ga_support_level;
+
+static int ga_cmp_support_levels(GuestAgentSupportLevel a,
+                                 GuestAgentSupportLevel b)
+{
+    if (a.major == b.major) {
+        if (a.minor == b.minor) {
+            return a.micro - b.micro;
+        }
+        return a.minor - b.minor;
+    }
+    return a.major - b.major;
+}
+
+bool ga_has_support_level(int major, int minor, int micro)
+{
+    const GuestAgentSupportLevel level = { major, minor, micro };
+    return ga_cmp_support_levels(ga_support_level, level) >= 0;
+}
+
+void ga_set_support_level(GuestAgentSupportLevel level)
+{
+    const GuestAgentSupportLevel min_support_level = {
+        QGA_SUPPORT_LEVEL_MAJOR_MIN,
+        QGA_SUPPORT_LEVEL_MINOR_MIN,
+        QGA_SUPPORT_LEVEL_MICRO_MIN
+    };
+    if (ga_cmp_support_levels(level, min_support_level) <= 0) {
+        ga_support_level = min_support_level;
+    } else {
+        ga_support_level = level;
+    }
+}
+
+GuestAgentSupportLevel ga_get_support_level(void)
+{
+    return ga_support_level;
+}
+
 static const char *ga_log_level_str(GLogLevelFlags level)
 {
     switch (level & G_LOG_LEVEL_MASK) {
@@ -569,6 +609,11 @@ int main(int argc, char **argv)
     GLogLevelFlags log_level = G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL;
     FILE *log_file = stderr;
     GAState *s;
+    const GuestAgentSupportLevel default_support_level = {
+        QGA_SUPPORT_LEVEL_MAJOR_DEFAULT,
+        QGA_SUPPORT_LEVEL_MINOR_DEFAULT,
+        QGA_SUPPORT_LEVEL_MICRO_DEFAULT
+    };
 
     module_call_init(MODULE_INIT_QAPI);
 
@@ -642,6 +687,7 @@ int main(int argc, char **argv)
         become_daemon(pidfile);
     }
 
+    ga_set_support_level(default_support_level);
     s = g_malloc0(sizeof(GAState));
     s->conn_channel = NULL;
     s->path = path;
diff --git a/qga/guest-agent-commands.c b/qga/guest-agent-commands.c
index a09c8ca..656dde8 100644
--- a/qga/guest-agent-commands.c
+++ b/qga/guest-agent-commands.c
@@ -62,6 +62,8 @@ struct GuestAgentInfo *qmp_guest_info(Error **err)
     char **cmd_list_head, **cmd_list;
 
     info->version = g_strdup(QGA_VERSION);
+    info->support_level = g_malloc0(sizeof(GuestAgentSupportLevel));
+    *info->support_level = ga_get_support_level();
 
     cmd_list_head = cmd_list = qmp_get_command_list();
     if (*cmd_list_head == NULL) {
@@ -87,6 +89,17 @@ out:
     return info;
 }
 
+void qmp_guest_set_support_level(int64_t major, int64_t minor, bool has_micro,
+                                 int64_t micro, Error **errp)
+{
+    GuestAgentSupportLevel level = {
+        major,
+        minor,
+        has_micro ? micro : 0
+    };
+    ga_set_support_level(level);
+}
+
 void qmp_guest_shutdown(bool has_mode, const char *mode, Error **err)
 {
     int ret;
diff --git a/qga/guest-agent-core.h b/qga/guest-agent-core.h
index e42b91d..7327e73 100644
--- a/qga/guest-agent-core.h
+++ b/qga/guest-agent-core.h
@@ -12,8 +12,16 @@
  */
 #include "qapi/qmp-core.h"
 #include "qemu-common.h"
+#include "qga-qapi-types.h"
 
 #define QGA_VERSION "1.0"
+#define QGA_SUPPORT_LEVEL_MAJOR_DEFAULT 1
+#define QGA_SUPPORT_LEVEL_MINOR_DEFAULT 0
+#define QGA_SUPPORT_LEVEL_MICRO_DEFAULT 0
+/* lowest possible support level */
+#define QGA_SUPPORT_LEVEL_MAJOR_MIN 1
+#define QGA_SUPPORT_LEVEL_MINOR_MIN 0
+#define QGA_SUPPORT_LEVEL_MICRO_MIN 0
 #define QGA_READ_COUNT_DEFAULT 4 << 10
 
 typedef struct GAState GAState;
@@ -29,3 +37,6 @@ GACommandState *ga_command_state_new(void);
 bool ga_logging_enabled(GAState *s);
 void ga_disable_logging(GAState *s);
 void ga_enable_logging(GAState *s);
+bool ga_has_support_level(int major, int minor, int micro);
+void ga_set_support_level(GuestAgentSupportLevel level);
+GuestAgentSupportLevel ga_get_support_level(void);
-- 
1.7.4.1

^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [Qemu-devel] [PATCH 5/6] qemu-ga: set O_NONBLOCK for serial channels
  2012-01-25 15:26 [Qemu-devel] [PULL 0/6] qemu-ga queue and qemu-tool fixes Michael Roth
                   ` (3 preceding siblings ...)
  2012-01-25 15:26 ` [Qemu-devel] [PATCH 4/6] qemu-ga: add guest-set-support-level command Michael Roth
@ 2012-01-25 15:26 ` Michael Roth
  2012-01-25 15:26 ` [Qemu-devel] [PATCH 6/6] qemu-ga: Add the guest-suspend command Michael Roth
  5 siblings, 0 replies; 7+ messages in thread
From: Michael Roth @ 2012-01-25 15:26 UTC (permalink / raw)
  To: qemu-devel; +Cc: aliguori

From: Luiz Capitulino <lcapitulino@redhat.com>

This fixes a bug when using -m isa-serial where qemu-ga will
hang on a read()'s when communicating to the host via isa-serial.

Original fix by Michael Roth.

Signed-off-by: Luiz Capitulino <lcapitulino@redhat.com>
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
---
 qemu-ga.c |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)

diff --git a/qemu-ga.c b/qemu-ga.c
index 8e5b075..d63b17c 100644
--- a/qemu-ga.c
+++ b/qemu-ga.c
@@ -544,7 +544,7 @@ static void init_guest_agent(GAState *s)
             exit(EXIT_FAILURE);
         }
     } else if (strcmp(s->method, "isa-serial") == 0) {
-        fd = qemu_open(s->path, O_RDWR | O_NOCTTY);
+        fd = qemu_open(s->path, O_RDWR | O_NOCTTY | O_NONBLOCK);
         if (fd == -1) {
             g_critical("error opening channel: %s", strerror(errno));
             exit(EXIT_FAILURE);
-- 
1.7.4.1

^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [Qemu-devel] [PATCH 6/6] qemu-ga: Add the guest-suspend command
  2012-01-25 15:26 [Qemu-devel] [PULL 0/6] qemu-ga queue and qemu-tool fixes Michael Roth
                   ` (4 preceding siblings ...)
  2012-01-25 15:26 ` [Qemu-devel] [PATCH 5/6] qemu-ga: set O_NONBLOCK for serial channels Michael Roth
@ 2012-01-25 15:26 ` Michael Roth
  5 siblings, 0 replies; 7+ messages in thread
From: Michael Roth @ 2012-01-25 15:26 UTC (permalink / raw)
  To: qemu-devel; +Cc: aliguori

From: Luiz Capitulino <lcapitulino@redhat.com>

The guest-suspend command supports three modes:

 o hibernate (suspend to disk)
 o sleep     (suspend to ram)
 o hybrid    (save RAM contents to disk, but suspend instead of
              powering off)

Before trying to suspend, the command queries the guest in order
to know whether the given mode is supported. The sleep and hybrid
modes are only supported in QEMU 1.1 and later though, because
QEMU's S3 support is broken in previous versions.

The guest-suspend command will use the scripts provided by the
pm-utils package if they are available. If they aren't, a manual
process which directly writes to the "/sys/power/state" file is
used.

To reap terminated children, a new signal handler is installed in
the parent to catch SIGCHLD signals and a non-blocking call to
waitpid() is done to collect their exit statuses. The statuses,
however, are discarded.

The approach used to query the guest for suspend support deserves
some explanation. It's implemented by bios_supports_mode() and shown
below:

   qemu-ga
     |
 create pipe
     |
   fork()
     -----------------
     |               |
     |               |
     |             fork()
     |               --------------------------
     |               |                        |
     |               |                        |
     |               |               exec('pm-is-supported')
     |               |
     |              wait()
     |       write exit status to pipe
     |              exit
     |
  read pipe

This might look complex, but the resulting code is quite simple.
The purpose of that approach is to allow qemu-ga to reap its
children (semi-)automatically from its SIGCHLD handler.

Implementing this the obvious way, that's, doing the exec() call
from the first child process, would force us to introduce a more
complex way to reap qemu-ga's children. Like registering PIDs to
be reaped and having a way to wait for them when returning their
exit status to qemu-ga is necessary. The approach explained
above avoids that complexity.

Signed-off-by: Luiz Capitulino <lcapitulino@redhat.com>
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
---
 qapi-schema-guest.json     |   32 ++++++
 qemu-ga.c                  |   18 ++++-
 qga/guest-agent-commands.c |  226 ++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 275 insertions(+), 1 deletions(-)

diff --git a/qapi-schema-guest.json b/qapi-schema-guest.json
index 81cd773..9b1ab1c 100644
--- a/qapi-schema-guest.json
+++ b/qapi-schema-guest.json
@@ -360,3 +360,35 @@
 ##
 { 'command': 'guest-fsfreeze-thaw',
   'returns': 'int' }
+
+##
+# @guest-suspend
+#
+# Suspend guest execution by entering ACPI power state S3 or S4.
+#
+# This command tries to execute the scripts provided by the pm-utils
+# package. If they are not available, it will perform the suspend
+# operation by manually writing to a sysfs file.
+#
+# For the best results it's strongly recommended to have the pm-utils
+# package installed in the guest.
+#
+# @mode: 'hibernate' RAM content is saved to the disk and the guest is
+#                    powered off (this corresponds to ACPI S4)
+#        'sleep'     execution is suspended but the RAM retains its contents
+#                    (this corresponds to ACPI S3)
+#        'hybrid'    RAM content is saved to the disk but the guest is
+#                    suspended instead of powering off
+#
+# Returns: nothing on success
+#          If @mode is not supported by the guest, Unsupported
+#
+# Notes: o This is an asynchronous request. There's no guarantee a response
+#          will be sent.
+#        o Errors will be logged to guest's syslog.
+#        o It's strongly recommended to issue the guest-sync command before
+#          sending commands when the guest resumes.
+#
+# Since: 1.1
+##
+{ 'command': 'guest-suspend', 'data': { 'mode': 'str' } }
diff --git a/qemu-ga.c b/qemu-ga.c
index d63b17c..f8b12c7 100644
--- a/qemu-ga.c
+++ b/qemu-ga.c
@@ -17,6 +17,7 @@
 #include <getopt.h>
 #include <termios.h>
 #include <syslog.h>
+#include <sys/wait.h>
 #include "qemu_socket.h"
 #include "json-streamer.h"
 #include "json-parser.h"
@@ -60,9 +61,16 @@ static void quit_handler(int sig)
     }
 }
 
+/* reap _all_ terminated children */
+static void child_handler(int sig)
+{
+    int status;
+    while (waitpid(-1, &status, WNOHANG) > 0) /* NOTHING */;
+}
+
 static void register_signal_handlers(void)
 {
-    struct sigaction sigact;
+    struct sigaction sigact, sigact_chld;
     int ret;
 
     memset(&sigact, 0, sizeof(struct sigaction));
@@ -77,6 +85,14 @@ static void register_signal_handlers(void)
     if (ret == -1) {
         g_error("error configuring signal handler: %s", strerror(errno));
     }
+
+    memset(&sigact_chld, 0, sizeof(struct sigaction));
+    sigact_chld.sa_handler = child_handler;
+    sigact_chld.sa_flags = SA_NOCLDSTOP;
+    ret = sigaction(SIGCHLD, &sigact_chld, NULL);
+    if (ret == -1) {
+        g_error("error configuring signal handler: %s", strerror(errno));
+    }
 }
 
 static void usage(const char *cmd)
diff --git a/qga/guest-agent-commands.c b/qga/guest-agent-commands.c
index 656dde8..925a765 100644
--- a/qga/guest-agent-commands.c
+++ b/qga/guest-agent-commands.c
@@ -23,6 +23,7 @@
 
 #include <sys/types.h>
 #include <sys/ioctl.h>
+#include <sys/wait.h>
 #include "qga/guest-agent-core.h"
 #include "qga-qmp-commands.h"
 #include "qerror.h"
@@ -44,6 +45,22 @@ static void slog(const char *fmt, ...)
     va_end(ap);
 }
 
+static void reopen_fd_to_null(int fd)
+{
+    int nullfd;
+
+    nullfd = open("/dev/null", O_RDWR);
+    if (nullfd < 0) {
+        return;
+    }
+
+    dup2(nullfd, fd);
+
+    if (nullfd != fd) {
+        close(nullfd);
+    }
+}
+
 int64_t qmp_guest_sync(int64_t id, Error **errp)
 {
     return id;
@@ -587,6 +604,215 @@ int64_t qmp_guest_fsfreeze_thaw(Error **err)
 }
 #endif
 
+#define LINUX_SYS_STATE_FILE "/sys/power/state"
+#define SUS_MODE_SUPPORTED 0
+#define SUS_MODE_NOT_SUPPORTED 1
+
+/**
+ * This function forks twice and the information about the mode support
+ * status is passed to the qemu-ga process via a pipe.
+ *
+ * This approach allows us to keep the way we reap terminated children
+ * in qemu-ga quite simple.
+ */
+static bool bios_supports_mode(const char *mode, Error **err)
+{
+    pid_t pid;
+    ssize_t ret;
+    int status, pipefds[2];
+    char *pmutils_path;
+    const char *pmutils_bin = "pm-is-supported";
+
+    if (pipe(pipefds) < 0) {
+        error_set(err, QERR_UNDEFINED_ERROR);
+        return false;
+    }
+
+    pmutils_path = g_find_program_in_path(pmutils_bin);
+
+    pid = fork();
+    if (!pid) {
+        struct sigaction act;
+
+        memset(&act, 0, sizeof(act));
+        act.sa_handler = SIG_DFL;
+        sigaction(SIGCHLD, &act, NULL);
+
+        setsid();
+        close(pipefds[0]);
+        reopen_fd_to_null(0);
+        reopen_fd_to_null(1);
+        reopen_fd_to_null(2);
+
+        pid = fork();
+        if (!pid) {
+            int fd;
+            char buf[32]; /* hopefully big enough */
+            const char *arg;
+
+            if (strcmp(mode, "hibernate") == 0) {
+                arg = "--hibernate";
+            } else if (strcmp(mode, "sleep") == 0) {
+                arg = "--suspend";
+            } else if (strcmp(mode, "hybrid") == 0) {
+                arg = "--suspend-hybrid";
+            } else {
+                _exit(SUS_MODE_NOT_SUPPORTED);
+            }
+
+            if (pmutils_path) {
+                execle(pmutils_path, pmutils_bin, arg, NULL, environ);
+            }
+
+            /*
+             * If we get here either pm-utils is not installed or  execle() has
+             * failed. Let's try the manual approach if mode is not hybrid (as
+             * it's only supported by pm-utils)
+             */
+
+            if (strcmp(mode, "hybrid") == 0) {
+                _exit(SUS_MODE_NOT_SUPPORTED);
+            }
+
+            fd = open(LINUX_SYS_STATE_FILE, O_RDONLY);
+            if (fd < 0) {
+                _exit(SUS_MODE_NOT_SUPPORTED);
+            }
+
+            ret = read(fd, buf, sizeof(buf)-1);
+            if (ret <= 0) {
+                _exit(SUS_MODE_NOT_SUPPORTED);
+            }
+            buf[ret] = '\0';
+
+            if (strcmp(mode, "hibernate") == 0 && strstr(buf, "disk")) {
+                _exit(SUS_MODE_SUPPORTED);
+            } else if (strcmp(mode, "sleep") == 0 && strstr(buf, "mem")) {
+                _exit(SUS_MODE_SUPPORTED);
+            }
+
+            _exit(SUS_MODE_NOT_SUPPORTED);
+        }
+
+        if (pid > 0) {
+            wait(&status);
+        } else {
+            status = SUS_MODE_NOT_SUPPORTED;
+        }
+
+        ret = write(pipefds[1], &status, sizeof(status));
+        if (ret != sizeof(status)) {
+            _exit(EXIT_FAILURE);
+        }
+
+        _exit(EXIT_SUCCESS);
+    }
+
+    close(pipefds[1]);
+    g_free(pmutils_path);
+
+    if (pid > 0) {
+        ret = read(pipefds[0], &status, sizeof(status));
+        if (ret == sizeof(status) && WIFEXITED(status) &&
+            WEXITSTATUS(status) == SUS_MODE_SUPPORTED) {
+            close(pipefds[0]);
+            return true;
+        }
+    }
+
+    close(pipefds[0]);
+    return false;
+}
+
+static bool host_supports_mode(const char *mode)
+{
+    if (strcmp(mode, "hibernate")) {
+        /* sleep & hybrid are only supported in qemu 1.1.0 and above */
+        return ga_has_support_level(1, 1, 0);
+    }
+    return true;
+}
+
+void qmp_guest_suspend(const char *mode, Error **err)
+{
+    pid_t pid;
+    char *pmutils_path;
+    const char *pmutils_bin;
+    Error *local_err = NULL;
+
+    if (strcmp(mode, "hibernate") == 0) {
+        pmutils_bin = "pm-hibernate";
+    } else if (strcmp(mode, "sleep") == 0) {
+        pmutils_bin = "pm-suspend";
+    } else if (strcmp(mode, "hybrid") == 0) {
+        pmutils_bin = "pm-suspend-hybrid";
+    } else {
+        error_set(err, QERR_INVALID_PARAMETER, "mode");
+        return;
+    }
+
+    if (!host_supports_mode(mode)) {
+        error_set(err, QERR_UNSUPPORTED);
+        return;
+    }
+
+    if (!bios_supports_mode(mode, &local_err)) {
+        if (error_is_set(&local_err)) {
+            error_propagate(err, local_err);
+        } else {
+            error_set(err, QERR_UNSUPPORTED);
+        }
+        return;
+    }
+
+    pmutils_path = g_find_program_in_path(pmutils_bin);
+
+    pid = fork();
+    if (pid == 0) {
+        /* child */
+        int fd;
+        const char *cmd;
+
+        setsid();
+        reopen_fd_to_null(0);
+        reopen_fd_to_null(1);
+        reopen_fd_to_null(2);
+
+        if (pmutils_path) {
+            execle(pmutils_path, pmutils_bin, NULL, environ);
+        }
+
+        /*
+         * If we get here either pm-utils is not installed or  execle() has
+         * failed. Let's try the manual approach if mode is not hybrid (as
+         * it's only supported by pm-utils)
+         */
+
+        if (strcmp(mode, "hybrid") == 0) {
+            _exit(EXIT_FAILURE);
+        }
+
+        fd = open(LINUX_SYS_STATE_FILE, O_WRONLY);
+        if (fd < 0) {
+            _exit(EXIT_FAILURE);
+        }
+
+        cmd = strcmp(mode, "sleep") == 0 ? "mem" : "disk";
+        if (write(fd, cmd, strlen(cmd)) < 0) {
+            _exit(EXIT_FAILURE);
+        }
+
+        _exit(EXIT_SUCCESS);
+    }
+
+    g_free(pmutils_path);
+
+    if (pid < 0) {
+        error_set(err, QERR_UNDEFINED_ERROR);
+        return;
+    }
+}
+
 /* register init/cleanup routines for stateful command groups */
 void ga_command_state_init(GAState *s, GACommandState *cs)
 {
-- 
1.7.4.1

^ permalink raw reply related	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2012-01-25 15:27 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2012-01-25 15:26 [Qemu-devel] [PULL 0/6] qemu-ga queue and qemu-tool fixes Michael Roth
2012-01-25 15:26 ` [Qemu-devel] [PATCH 1/6] main-loop: Fix SetEvent() on uninitialized handle on win32 Michael Roth
2012-01-25 15:26 ` [Qemu-devel] [PATCH 2/6] main-loop: For tools, initialize timers as part of qemu_init_main_loop() Michael Roth
2012-01-25 15:26 ` [Qemu-devel] [PATCH 3/6] qemu-ga: Add schema documentation for types Michael Roth
2012-01-25 15:26 ` [Qemu-devel] [PATCH 4/6] qemu-ga: add guest-set-support-level command Michael Roth
2012-01-25 15:26 ` [Qemu-devel] [PATCH 5/6] qemu-ga: set O_NONBLOCK for serial channels Michael Roth
2012-01-25 15:26 ` [Qemu-devel] [PATCH 6/6] qemu-ga: Add the guest-suspend command Michael Roth

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.