From patchwork Tue Dec 13 18:28:50 2011 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Luiz Capitulino X-Patchwork-Id: 131161 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [140.186.70.17]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (Client did not present a certificate) by ozlabs.org (Postfix) with ESMTPS id 3A7DF1007D3 for ; Wed, 14 Dec 2011 05:29:15 +1100 (EST) Received: from localhost ([::1]:41340 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1RaX6C-0002S0-43 for incoming@patchwork.ozlabs.org; Tue, 13 Dec 2011 13:29:08 -0500 Received: from eggs.gnu.org ([140.186.70.92]:33894) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1RaX62-0002Rq-F8 for qemu-devel@nongnu.org; Tue, 13 Dec 2011 13:29:04 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1RaX61-0004tE-6x for qemu-devel@nongnu.org; Tue, 13 Dec 2011 13:28:58 -0500 Received: from mx1.redhat.com ([209.132.183.28]:8771) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1RaX61-0004tA-0F for qemu-devel@nongnu.org; Tue, 13 Dec 2011 13:28:57 -0500 Received: from int-mx09.intmail.prod.int.phx2.redhat.com (int-mx09.intmail.prod.int.phx2.redhat.com [10.5.11.22]) by mx1.redhat.com (8.14.4/8.14.4) with ESMTP id pBDISrDq010979 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=OK); Tue, 13 Dec 2011 13:28:54 -0500 Received: from doriath (ovpn-113-83.phx2.redhat.com [10.3.113.83]) by int-mx09.intmail.prod.int.phx2.redhat.com (8.14.4/8.14.4) with ESMTP id pBDISp5a020505; Tue, 13 Dec 2011 13:28:52 -0500 Date: Tue, 13 Dec 2011 16:28:50 -0200 From: Luiz Capitulino To: qemu-devel Message-ID: <20111213162850.4cd135a3@doriath> Organization: Red Hat Mime-Version: 1.0 X-Scanned-By: MIMEDefang 2.68 on 10.5.11.22 X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.6 (newer, 3) X-Received-From: 209.132.183.28 Cc: Amit Shah , aliguori@us.ibm.com, mdroth@linux.vnet.ibm.com Subject: [Qemu-devel] [PATCH v2] qemu-ga: Add the guest-suspend command X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org It supports two modes: "hibernate" (which corresponds to S4) and "sleep" (which corresponds to S3). It will try to execute the pm-hibernate or pm-suspend scripts, if the scripts don't exist the command will try to suspend by directly writing to the "/sys/power/state" file. An interesting implementation detail is how to cleanup the child's status on termination, so that we don't create zombies. I've choosen to ignore the SIGCHLD signal. This will cause the kernel to automatically cleanup the child's status on its termination. Signed-off-by: Luiz Capitulino --- I've tested this w/o any virtio driver, as they don't support S4 yet. For S4 it seems to work ok. I couldn't fully test S3 because we lack a way to resume from it, but by checking the logs it seems to work fine. changelog --------- v2 o Rename the command to 'guest-suspend' o Add 'mode' parameter o Use pm-utils scripts o Cleanup child termination status qapi-schema-guest.json | 17 +++++++++++ qemu-ga.c | 11 +++++++- qga/guest-agent-commands.c | 64 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 91 insertions(+), 1 deletions(-) diff --git a/qapi-schema-guest.json b/qapi-schema-guest.json index 29989fe..656bde9 100644 --- a/qapi-schema-guest.json +++ b/qapi-schema-guest.json @@ -219,3 +219,20 @@ ## { 'command': 'guest-fsfreeze-thaw', 'returns': 'int' } + +## +# @guest-suspend +# +# Suspend guest execution by entering ACPI power state S3 or S4. +# +# @mode: 'hibernate' RAM content is saved in the disk and the guest is +# powered down (this corresponds to ACPI S4) +# 'sleep' execution is suspended but the RAM retains its contents +# (this corresponds to ACPI S3) +# +# Notes: This is an asynchronous request. There's no guarantee it will +# succeed. Errors will be logged to guest's syslog. +# +# Since: 1.1 +## +{ 'command': 'guest-suspend', 'data': { 'mode': 'str' } } diff --git a/qemu-ga.c b/qemu-ga.c index 60d4972..b32e96c 100644 --- a/qemu-ga.c +++ b/qemu-ga.c @@ -61,7 +61,7 @@ static void quit_handler(int sig) static void register_signal_handlers(void) { - struct sigaction sigact; + struct sigaction sigact, sigact_chld; int ret; memset(&sigact, 0, sizeof(struct sigaction)); @@ -76,6 +76,15 @@ static void register_signal_handlers(void) if (ret == -1) { g_error("error configuring signal handler: %s", strerror(errno)); } + + /* This should cause the kernel to automatically cleanup child + termination status */ + memset(&sigact_chld, 0, sizeof(struct sigaction)); + sigact_chld.sa_handler = SIG_IGN; + 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 a09c8ca..4799638 100644 --- a/qga/guest-agent-commands.c +++ b/qga/guest-agent-commands.c @@ -574,6 +574,70 @@ int64_t qmp_guest_fsfreeze_thaw(Error **err) } #endif +#define LINUX_PM_UTILS_PATH "/usr/sbin" +#define LINUX_SYS_STATE_FILE "/sys/power/state" + +void qmp_guest_suspend(const char *mode, Error **err) +{ + int ret, fd = -1; + const char *pmutils_bin; + char pmutils_bin_path[PATH_MAX]; + + if (strcmp(mode, "hibernate") == 0) { + pmutils_bin = "pm-hibernate"; + } else if (strcmp(mode, "sleep") == 0) { + pmutils_bin = "pm-suspend"; + } else { + error_set(err, QERR_INVALID_PARAMETER, "mode"); + return; + } + + snprintf(pmutils_bin_path, sizeof(pmutils_bin_path), "%s/%s", + LINUX_PM_UTILS_PATH, pmutils_bin); + + if (access(pmutils_bin_path, X_OK) != 0) { + pmutils_bin = NULL; + fd = open(LINUX_SYS_STATE_FILE, O_WRONLY); + if (fd < 0) { + error_set(err, QERR_OPEN_FILE_FAILED, LINUX_SYS_STATE_FILE); + return; + } + } + + ret = fork(); + if (ret == 0) { + /* child */ + setsid(); + fclose(stdin); + fclose(stdout); + fclose(stderr); + + if (pmutils_bin) { + ret = execl(pmutils_bin_path, pmutils_bin, NULL); + if (ret) { + slog("%s failed: %s", pmutils_bin_path, strerror(errno)); + } + } else { + const char *cmd = strcmp(mode, "sleep") == 0 ? "mem" : "disk"; + ret = write(fd, cmd, strlen(cmd)); + if (ret < 0) { + slog("can't write to %s: %s\n", LINUX_SYS_STATE_FILE, + strerror(errno)); + } + close(fd); + } + + exit(!!ret); + } else if (ret < 0) { + error_set(err, QERR_UNDEFINED_ERROR); + return; + } + + if (!pmutils_bin) { + close(fd); + } +} + /* register init/cleanup routines for stateful command groups */ void ga_command_state_init(GAState *s, GACommandState *cs) {