[libvirt] [ PATCH v5 0/4] API to invoke S3/S4 on a host and also resume from within libvirt

This patchset adds a new API to put a host to a suspended state (Suspend-to-RAM, Suspend-to-Disk or Hybrid-Suspend) and setup a timed resume to get the host back online, from within libvirt. This uses the RTC wakeup mechanism to set up a timer alarm before suspending the host, so that in-band resume is facilitated by the firing of the RTC alarm, which wakes up the host. This patch applies on top of the Hybrid-Suspend patch posted in [1]. v5: * Added MIN_TIME_REQ_FOR_SUSPEND. * Incorporated the review comments in [3]. v4: * Applies on top of the Hybrid-Suspend patch posted in [1]. * Incorporated the review comments in [2]. v3: * Rebased to libvirt 0.9.7 * Added a check to see if alarmTime (suspend duration) is within an acceptable range. v2: * Added an init function which finds out if S3/S4 is supported by the host, upon the first request to suspend/hibernate. * Added synchronization/locking to ensure that only one suspend operation is active at a time. v1: http://www.redhat.com/archives/libvir-list/2011-September/msg00830.html v2: http://comments.gmane.org/gmane.comp.emulators.libvirt/46729 References: [1]. http://www.redhat.com/archives/libvir-list/2011-November/msg01407.html [2]. http://thread.gmane.org/gmane.comp.emulators.libvirt/47950 [3]. http://thread.gmane.org/gmane.comp.emulators.libvirt/49054 Srivatsa S. Bhat (4): Add a public API to invoke suspend/resume on the host Add the remote protocol implementation for virNodeSuspendForDuration Implement the core API to suspend/resume the host Add virsh command to initiate suspend on the host include/libvirt/libvirt.h.in | 16 ++ src/Makefile.am | 1 src/driver.h | 6 + src/libvirt.c | 65 ++++++++++ src/libvirt_private.syms | 5 + src/libvirt_public.syms | 5 + src/lxc/lxc_driver.c | 2 src/qemu/qemu_driver.c | 2 src/remote/remote_driver.c | 1 src/remote/remote_protocol.x | 10 +- src/uml/uml_driver.c | 2 src/util/virnodesuspend.c | 269 ++++++++++++++++++++++++++++++++++++++++++ src/util/virnodesuspend.h | 36 ++++++ src/xen/xen_driver.c | 2 tools/virsh.c | 64 ++++++++++ tools/virsh.pod | 7 + 16 files changed, 491 insertions(+), 2 deletions(-) create mode 100644 src/util/virnodesuspend.c create mode 100644 src/util/virnodesuspend.h

Implement the public definitions for the new API virNodeSuspendForDuration() which will be subsequently used to do a timed suspend on the host. Signed-off-by: Srivatsa S. Bhat <srivatsa.bhat@linux.vnet.ibm.com> --- include/libvirt/libvirt.h.in | 16 +++++++++++ src/driver.h | 6 ++++ src/libvirt.c | 61 ++++++++++++++++++++++++++++++++++++++++++ src/libvirt_public.syms | 5 +++ 4 files changed, 88 insertions(+), 0 deletions(-) diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in index 2ab89f5..c89448b 100644 --- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -250,6 +250,17 @@ typedef enum { } virDomainCreateFlags; +/** + * virNodeSuspendTarget: + * + * Flags to indicate which system-wide sleep state the host must be + * transitioned to. + */ +typedef enum { + VIR_NODE_SUSPEND_TARGET_MEM = (1 << 0), + VIR_NODE_SUSPEND_TARGET_DISK = (1 << 1), + VIR_NODE_SUSPEND_TARGET_HYBRID = (1 << 2), +} virNodeSuspendTarget; /** * virStream: @@ -1085,6 +1096,11 @@ unsigned long long virNodeGetFreeMemory (virConnectPtr conn); int virNodeGetSecurityModel (virConnectPtr conn, virSecurityModelPtr secmodel); +int virNodeSuspendForDuration (virConnectPtr conn, + unsigned int target, + unsigned long long duration, + unsigned int flags); + /* * Gather list of running domains */ diff --git a/src/driver.h b/src/driver.h index 4c14aaa..5f3665d 100644 --- a/src/driver.h +++ b/src/driver.h @@ -740,6 +740,11 @@ typedef int (*virDrvDomainBlockPull)(virDomainPtr dom, const char *path, unsigned long bandwidth, unsigned int flags); +typedef int + (*virDrvNodeSuspendForDuration)(virConnectPtr conn, unsigned int target, + unsigned long long duration, + unsigned int flags); + /** * _virDriver: @@ -899,6 +904,7 @@ struct _virDriver { virDrvDomainGetBlockJobInfo domainGetBlockJobInfo; virDrvDomainBlockJobSetSpeed domainBlockJobSetSpeed; virDrvDomainBlockPull domainBlockPull; + virDrvNodeSuspendForDuration nodeSuspendForDuration; }; typedef int diff --git a/src/libvirt.c b/src/libvirt.c index 1518ed2..7a13b11 100644 --- a/src/libvirt.c +++ b/src/libvirt.c @@ -6359,6 +6359,67 @@ error: } /** + * virNodeSuspendForDuration: + * @conn: pointer to the hypervisor connection + * @target: the state to which the host must be suspended to, + * such as: VIR_NODE_SUSPEND_TARGET_MEM (Suspend-to-RAM) + * VIR_NODE_SUSPEND_TARGET_DISK (Suspend-to-Disk) + * VIR_NODE_SUSPEND_TARGET_HYBRID (Hybrid-Suspend, + * which is a combination of the former modes). + * @duration: the time duration in seconds for which the host + * has to be suspended + * @flags: any flag values that might need to be passed; + * currently unused (0). + * + * Attempt to suspend the node (host machine) for the given duration of + * time in the specified state (Suspend-to-RAM, Suspend-to-Disk or + * Hybrid-Suspend). Schedule the node's Real-Time-Clock interrupt to + * resume the node after the duration is complete. + * + * Returns 0 on success (i.e., the node will be suspended after a short + * delay), -1 on failure (the operation is not supported, or an attempted + * suspend is already underway). + */ +int +virNodeSuspendForDuration(virConnectPtr conn, + unsigned int target, + unsigned long long duration, + unsigned int flags) +{ + + VIR_DEBUG("conn=%p, target=%d, duration=%lld", conn, target, duration); + + virResetLastError(); + + if (!VIR_IS_CONNECT(conn)) { + virLibConnError(VIR_ERR_INVALID_CONN, __FUNCTION__); + virDispatchError(NULL); + return -1; + } + + if (conn->flags & VIR_CONNECT_RO) { + virLibConnError(VIR_ERR_OPERATION_DENIED, __FUNCTION__); + goto error; + } + + if (conn->driver->nodeSuspendForDuration) { + int ret; + ret = conn->driver->nodeSuspendForDuration(conn, target, + duration, flags); + if (ret < 0) + goto error; + return ret; + } + + virLibConnError(VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: + virDispatchError(conn); + return -1; +} + + +/** * virDomainGetSchedulerType: * @domain: pointer to domain object * @nparams: pointer to number of scheduler parameters, can be NULL diff --git a/src/libvirt_public.syms b/src/libvirt_public.syms index bcefb10..a1eede6 100644 --- a/src/libvirt_public.syms +++ b/src/libvirt_public.syms @@ -498,4 +498,9 @@ LIBVIRT_0.9.7 { virDomainSnapshotNumChildren; } LIBVIRT_0.9.5; +LIBVIRT_0.9.8 { + global: + virNodeSuspendForDuration; +} LIBVIRT_0.9.7; + # .... define new API here using predicted next version number ....

On Tue, Nov 29, 2011 at 03:12:18AM +0530, Srivatsa S. Bhat wrote:
Implement the public definitions for the new API virNodeSuspendForDuration() which will be subsequently used to do a timed suspend on the host.
Signed-off-by: Srivatsa S. Bhat <srivatsa.bhat@linux.vnet.ibm.com> ---
include/libvirt/libvirt.h.in | 16 +++++++++++ src/driver.h | 6 ++++ src/libvirt.c | 61 ++++++++++++++++++++++++++++++++++++++++++ src/libvirt_public.syms | 5 +++ 4 files changed, 88 insertions(+), 0 deletions(-)
ACK Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|

Implement the remote protocol for virNodeSuspendForDuration() API. Signed-off-by: Srivatsa S. Bhat <srivatsa.bhat@linux.vnet.ibm.com> --- src/remote/remote_driver.c | 1 + src/remote/remote_protocol.x | 10 +++++++++- 2 files changed, 10 insertions(+), 1 deletions(-) diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c index 94fd3e7..e784161 100644 --- a/src/remote/remote_driver.c +++ b/src/remote/remote_driver.c @@ -4550,6 +4550,7 @@ static virDriver remote_driver = { .domainGetBlockJobInfo = remoteDomainGetBlockJobInfo, /* 0.9.4 */ .domainBlockJobSetSpeed = remoteDomainBlockJobSetSpeed, /* 0.9.4 */ .domainBlockPull = remoteDomainBlockPull, /* 0.9.4 */ + .nodeSuspendForDuration = remoteNodeSuspendForDuration, /* 0.9.8 */ }; static virNetworkDriver network_driver = { diff --git a/src/remote/remote_protocol.x b/src/remote/remote_protocol.x index 5ea1114..7d4adfc 100644 --- a/src/remote/remote_protocol.x +++ b/src/remote/remote_protocol.x @@ -2269,6 +2269,13 @@ struct remote_domain_open_graphics_args { unsigned int flags; }; +struct remote_node_suspend_for_duration_args { + unsigned int target; + unsigned hyper duration; + unsigned int flags; +}; + + /*----- Protocol. -----*/ /* Define the program number, protocol version and procedure numbers here. */ @@ -2564,7 +2571,8 @@ enum remote_procedure { REMOTE_PROC_DOMAIN_SNAPSHOT_NUM_CHILDREN = 246, /* autogen autogen priority:high */ REMOTE_PROC_DOMAIN_SNAPSHOT_LIST_CHILDREN_NAMES = 247, /* autogen autogen priority:high */ REMOTE_PROC_DOMAIN_EVENT_DISK_CHANGE = 248, /* skipgen skipgen */ - REMOTE_PROC_DOMAIN_OPEN_GRAPHICS = 249 /* skipgen skipgen */ + REMOTE_PROC_DOMAIN_OPEN_GRAPHICS = 249, /* skipgen skipgen */ + REMOTE_PROC_NODE_SUSPEND_FOR_DURATION = 250 /* autogen autogen */ /* * Notice how the entries are grouped in sets of 10 ?

On Tue, Nov 29, 2011 at 03:12:26AM +0530, Srivatsa S. Bhat wrote:
Implement the remote protocol for virNodeSuspendForDuration() API.
Signed-off-by: Srivatsa S. Bhat <srivatsa.bhat@linux.vnet.ibm.com> ---
src/remote/remote_driver.c | 1 + src/remote/remote_protocol.x | 10 +++++++++- 2 files changed, 10 insertions(+), 1 deletions(-)
ACK Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|

Add the core functions that implement the functionality of the API. Suspend is done by using an asynchronous mechanism so that we can return the status to the caller before the host gets suspended. This asynchronous operation is achieved by suspending the host in a separate thread of execution. However, returning the status to the caller is only best-effort, but not guaranteed. To resume the host, an RTC alarm is set up (based on how long we want to suspend) before suspending the host. When this alarm fires, the host gets woken up. Suspend-to-RAM operation on a host running Linux can take upto more than 20 seconds, depending on the load of the system. (Freezing of tasks, an operation preceding any suspend operation, is given up after a 20 second timeout). And Suspend-to-Disk can take even more time, considering the time required for compaction, creating the memory image and writing it to disk etc. So, we do not allow the user to specify a suspend duration of less than 60 seconds, to be on the safer side, since we don't want to prematurely declare failure when we only had to wait for some more time. Signed-off-by: Srivatsa S. Bhat <srivatsa.bhat@linux.vnet.ibm.com> --- src/Makefile.am | 1 src/libvirt.c | 4 + src/libvirt_private.syms | 5 + src/lxc/lxc_driver.c | 2 src/qemu/qemu_driver.c | 2 src/uml/uml_driver.c | 2 src/util/virnodesuspend.c | 269 +++++++++++++++++++++++++++++++++++++++++++++ src/util/virnodesuspend.h | 36 ++++++ src/xen/xen_driver.c | 2 9 files changed, 322 insertions(+), 1 deletions(-) create mode 100644 src/util/virnodesuspend.c create mode 100644 src/util/virnodesuspend.h diff --git a/src/Makefile.am b/src/Makefile.am index 252c64f..762c6a7 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -85,6 +85,7 @@ UTIL_SOURCES = \ util/util.c util/util.h \ util/viraudit.c util/viraudit.h \ util/virfile.c util/virfile.h \ + util/virnodesuspend.c util/virnodesuspend.h \ util/virpidfile.c util/virpidfile.h \ util/xml.c util/xml.h \ util/virterror.c util/virterror_internal.h \ diff --git a/src/libvirt.c b/src/libvirt.c index 7a13b11..626d34b 100644 --- a/src/libvirt.c +++ b/src/libvirt.c @@ -43,6 +43,7 @@ #include "conf.h" #include "rpc/virnettlscontext.h" #include "command.h" +#include "virnodesuspend.h" #ifndef WITH_DRIVER_MODULES # ifdef WITH_TEST @@ -399,7 +400,8 @@ virInitialize(void) if (virThreadInitialize() < 0 || virErrorInitialize() < 0 || - virRandomInitialize(time(NULL) ^ getpid())) + virRandomInitialize(time(NULL) ^ getpid()) || + virNodeSuspendInit() < 0) return -1; gcry_control(GCRYCTL_SET_THREAD_CBS, &virTLSThreadImpl); diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 2cf50d3..6c08e56 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -1320,6 +1320,11 @@ virNetTLSContextNewServer; virNetTLSContextNewServerPath; +# virnodesuspend.h +nodeSuspendForDuration; +virNodeSuspendInit; + + # virpidfile.h virPidFileAcquire; virPidFileAcquirePath; diff --git a/src/lxc/lxc_driver.c b/src/lxc/lxc_driver.c index 1110c45..3f52129 100644 --- a/src/lxc/lxc_driver.c +++ b/src/lxc/lxc_driver.c @@ -56,6 +56,7 @@ #include "domain_nwfilter.h" #include "network/bridge_driver.h" #include "virnetdev.h" +#include "virnodesuspend.h" #define VIR_FROM_THIS VIR_FROM_LXC @@ -3273,6 +3274,7 @@ static virDriver lxcDriver = { .domainEventRegisterAny = lxcDomainEventRegisterAny, /* 0.8.0 */ .domainEventDeregisterAny = lxcDomainEventDeregisterAny, /* 0.8.0 */ .domainOpenConsole = lxcDomainOpenConsole, /* 0.8.6 */ + .nodeSuspendForDuration = nodeSuspendForDuration, /* 0.9.8 */ }; static virStateDriver lxcStateDriver = { diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index fe2ab85..16adbaa 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -88,6 +88,7 @@ #include "locking/lock_manager.h" #include "locking/domain_lock.h" #include "virkeycode.h" +#include "virnodesuspend.h" #define VIR_FROM_THIS VIR_FROM_QEMU @@ -10918,6 +10919,7 @@ static virDriver qemuDriver = { .domainGetBlockJobInfo = qemuDomainGetBlockJobInfo, /* 0.9.4 */ .domainBlockJobSetSpeed = qemuDomainBlockJobSetSpeed, /* 0.9.4 */ .domainBlockPull = qemuDomainBlockPull, /* 0.9.4 */ + .nodeSuspendForDuration = nodeSuspendForDuration, /* 0.9.8 */ }; diff --git a/src/uml/uml_driver.c b/src/uml/uml_driver.c index a0e086a..30e25bf 100644 --- a/src/uml/uml_driver.c +++ b/src/uml/uml_driver.c @@ -62,6 +62,7 @@ #include "fdstream.h" #include "configmake.h" #include "virnetdevtap.h" +#include "virnodesuspend.h" #define VIR_FROM_THIS VIR_FROM_UML @@ -2597,6 +2598,7 @@ static virDriver umlDriver = { .domainEventRegisterAny = umlDomainEventRegisterAny, /* 0.9.4 */ .domainEventDeregisterAny = umlDomainEventDeregisterAny, /* 0.9.4 */ .domainOpenConsole = umlDomainOpenConsole, /* 0.8.6 */ + .nodeSuspendForDuration = nodeSuspendForDuration, /* 0.9.8 */ }; static int diff --git a/src/util/virnodesuspend.c b/src/util/virnodesuspend.c new file mode 100644 index 0000000..f095f7d --- /dev/null +++ b/src/util/virnodesuspend.c @@ -0,0 +1,269 @@ +/* + * virnodesuspend.c: Support for suspending a node (host machine) + * + * Copyright (C) 2011 Srivatsa S. Bhat <srivatsa.bhat@linux.vnet.ibm.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include <config.h> +#include "virnodesuspend.h" + +#include "command.h" +#include "threads.h" +#include "datatypes.h" + +#include "memory.h" +#include "logging.h" +#include "virterror_internal.h" + +#define VIR_FROM_THIS VIR_FROM_NONE + +#define virNodeSuspendError(code, ...) \ + virReportErrorHelper(VIR_FROM_NONE, code, __FILE__, \ + __FUNCTION__, __LINE__, __VA_ARGS__) + + +# define SUSPEND_DELAY 10 /* in seconds */ + +/* Give sufficient time for performing the suspend operation on the host */ +# define MIN_TIME_REQ_FOR_SUSPEND 60 /* in seconds */ + +/* + * Bitmask to hold the Power Management features supported by the host, + * such as Suspend-to-RAM, Suspend-to-Disk, Hybrid-Suspend etc. + */ +static unsigned int hostPMFeatures; + +virMutex virNodeSuspendMutex; + +static bool aboutToSuspend; + +static void virNodeSuspendLock(void) +{ + virMutexLock(&virNodeSuspendMutex); +} + +static void virNodeSuspendUnlock(void) +{ + virMutexUnlock(&virNodeSuspendMutex); +} + + +/** + * virNodeSuspendInit: + * + * Get the system-wide sleep states supported by the host, such as + * Suspend-to-RAM, Suspend-to-Disk, or Hybrid-Suspend, so that a request + * to suspend/hibernate the host can be handled appropriately based on + * this information. + * + * Returns 0 if successful, and -1 in case of error. + */ +int virNodeSuspendInit(void) +{ + + if (virMutexInit(&virNodeSuspendMutex) < 0) + return -1; + + /* Get the power management capabilities supported by the host */ + if (virGetPMCapabilities(&hostPMFeatures) < 0) { + VIR_ERROR(_("Failed to get host power management features")); + return -1; + } + + return 0; +} + + +/** + * virNodeSuspendSetNodeWakeup: + * @alarmTime: time in seconds from now, at which the RTC alarm has to be set. + * + * Set up the RTC alarm to the specified time. + * Return 0 on success, -1 on failure. + */ +static int virNodeSuspendSetNodeWakeup(unsigned long long alarmTime) +{ + virCommandPtr setAlarmCmd; + int ret = -1; + + if (alarmTime <= MIN_TIME_REQ_FOR_SUSPEND) { + virNodeSuspendError(VIR_ERR_INVALID_ARG, "%s", _("Suspend duration is too short")); + return -1; + } + + setAlarmCmd = virCommandNewArgList("rtcwake", "-m", "no", "-s", NULL); + virCommandAddArgFormat(setAlarmCmd, "%lld", alarmTime); + + if (virCommandRun(setAlarmCmd, NULL) < 0) + goto cleanup; + + ret = 0; + +cleanup: + virCommandFree(setAlarmCmd); + return ret; +} + +/** + * virNodeSuspend: + * @cmdString: pointer to the command string this thread has to execute. + * + * Actually perform the suspend operation by invoking the command. + * Give a short delay before executing the command so as to give a chance + * to virNodeSuspendForDuration() to return the status to the caller. + * If we don't give this delay, that function will not be able to return + * the status, since the suspend operation would have begun and hence no + * data can be sent through the connection to the caller. However, with + * this delay added, the status return is best-effort only. + */ +static void virNodeSuspend(void *cmdString) +{ + virCommandPtr suspendCmd = virCommandNew((char *)cmdString); + + VIR_FREE(cmdString); + + /* + * Delay for sometime so that the function nodeSuspendForDuration() + * can return the status to the caller. + */ + sleep(SUSPEND_DELAY); + if (virCommandRun(suspendCmd, NULL) < 0) + VIR_WARN("Failed to suspend the host"); + + virCommandFree(suspendCmd); + + /* + * Now that we have resumed from suspend or the suspend failed, + * reset 'aboutToSuspend' flag. + */ + virNodeSuspendLock(); + aboutToSuspend = false; + virNodeSuspendUnlock(); +} + +/** + * nodeSuspendForDuration: + * @conn: pointer to the hypervisor connection + * @target: the state to which the host must be suspended to - + * VIR_NODE_SUSPEND_TARGET_MEM (Suspend-to-RAM), + * VIR_NODE_SUSPEND_TARGET_DISK (Suspend-to-Disk), + * VIR_NODE_SUSPEND_TARGET_HYBRID (Hybrid-Suspend) + * @duration: the time duration in seconds, for which the host must be + * suspended + * @flags: any flag values that might need to be passed; currently unused. + * + * Suspend the node (host machine) for the given duration of time in the + * specified state (such as Mem, Disk or Hybrid-Suspend). Attempt to resume + * the node after the time duration is complete. + * + * First, an RTC alarm is set appropriately to wake up the node from its + * sleep state. Then the actual node suspend operation is carried out + * asynchronously in another thread, after a short time delay so as to + * give enough time for this function to return status to its caller + * through the connection. However this returning of status is best-effort + * only, and should generally happen due to the short delay but might be + * missed if the machine suspends first. + * + * Returns 0 in case the node is going to be suspended after a short delay, + * -1 if suspending the node is not supported, or if a previous suspend + * operation is still in progress. + */ +int nodeSuspendForDuration(virConnectPtr conn ATTRIBUTE_UNUSED, + unsigned int target, + unsigned long long duration, + unsigned int flags ATTRIBUTE_UNUSED) +{ + static virThread thread; + char *cmdString = NULL; + + /* + * Ensure that we are the only ones trying to suspend. + * Fail if somebody has already initiated a suspend. + */ + virNodeSuspendLock(); + + if (aboutToSuspend) { + /* A suspend operation is already in progress */ + virNodeSuspendUnlock(); + return -1; + } else { + aboutToSuspend = true; + } + + virNodeSuspendUnlock(); + + /* Check if the host supports the requested suspend target */ + switch (target) { + case VIR_NODE_SUSPEND_TARGET_MEM: + if (hostPMFeatures & VIR_NODE_SUSPEND_TARGET_MEM) { + cmdString = strdup("pm-suspend"); + if (cmdString == NULL) { + virReportOOMError(); + goto cleanup; + } + break; + } + virNodeSuspendError(VIR_ERR_ARGUMENT_UNSUPPORTED, "%s", _("Suspend-to-RAM")); + goto cleanup; + + case VIR_NODE_SUSPEND_TARGET_DISK: + if (hostPMFeatures & VIR_NODE_SUSPEND_TARGET_DISK) { + cmdString = strdup("pm-hibernate"); + if (cmdString == NULL) { + virReportOOMError(); + goto cleanup; + } + break; + } + virNodeSuspendError(VIR_ERR_ARGUMENT_UNSUPPORTED, "%s", _("Suspend-to-Disk")); + goto cleanup; + + case VIR_NODE_SUSPEND_TARGET_HYBRID: + if (hostPMFeatures & VIR_NODE_SUSPEND_TARGET_HYBRID) { + cmdString = strdup("pm-suspend-hybrid"); + if (cmdString == NULL) { + virReportOOMError(); + goto cleanup; + } + break; + } + virNodeSuspendError(VIR_ERR_ARGUMENT_UNSUPPORTED, "%s", _("Hybrid-Suspend")); + goto cleanup; + + default: + virNodeSuspendError(VIR_ERR_INVALID_ARG, "%s", _("Invalid suspend target")); + goto cleanup; + } + + /* Just set the RTC alarm. Don't suspend yet. */ + if (virNodeSuspendSetNodeWakeup(duration) < 0) + goto cleanup; + + if (virThreadCreate(&thread, false, virNodeSuspend, (void *)cmdString) < 0) { + virNodeSuspendError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Failed to create thread to suspend the host\n")); + goto cleanup; + } + + return 0; + +cleanup: + VIR_FREE(cmdString); + return -1; +} + diff --git a/src/util/virnodesuspend.h b/src/util/virnodesuspend.h new file mode 100644 index 0000000..1432b5c --- /dev/null +++ b/src/util/virnodesuspend.h @@ -0,0 +1,36 @@ +/* + * virnodesuspend.h: Support for suspending a node (host machine) + * + * Copyright (C) 2011 Srivatsa S. Bhat <srivatsa.bhat@linux.vnet.ibm.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + + +#ifndef __VIR_NODE_SUSPEND_H__ +# define __VIR_NODE_SUSPEND_H__ + +#include "internal.h" + +int nodeSuspendForDuration(virConnectPtr conn, + unsigned int target, + unsigned long long duration, + unsigned int flags); + +int virNodeSuspendInit(void); + + +#endif /* __VIR_NODE_SUSPEND_H__ */ diff --git a/src/xen/xen_driver.c b/src/xen/xen_driver.c index beb2ba3..1837862 100644 --- a/src/xen/xen_driver.c +++ b/src/xen/xen_driver.c @@ -51,6 +51,7 @@ #include "fdstream.h" #include "virfile.h" #include "command.h" +#include "virnodesuspend.h" #define VIR_FROM_THIS VIR_FROM_XEN @@ -2249,6 +2250,7 @@ static virDriver xenUnifiedDriver = { .domainEventRegisterAny = xenUnifiedDomainEventRegisterAny, /* 0.8.0 */ .domainEventDeregisterAny = xenUnifiedDomainEventDeregisterAny, /* 0.8.0 */ .domainOpenConsole = xenUnifiedDomainOpenConsole, /* 0.8.6 */ + .nodeSuspendForDuration = nodeSuspendForDuration, /* 0.9.8 */ }; /**

On Tue, Nov 29, 2011 at 03:12:35AM +0530, Srivatsa S. Bhat wrote:
Add the core functions that implement the functionality of the API. Suspend is done by using an asynchronous mechanism so that we can return the status to the caller before the host gets suspended. This asynchronous operation is achieved by suspending the host in a separate thread of execution. However, returning the status to the caller is only best-effort, but not guaranteed.
To resume the host, an RTC alarm is set up (based on how long we want to suspend) before suspending the host. When this alarm fires, the host gets woken up.
Suspend-to-RAM operation on a host running Linux can take upto more than 20 seconds, depending on the load of the system. (Freezing of tasks, an operation preceding any suspend operation, is given up after a 20 second timeout). And Suspend-to-Disk can take even more time, considering the time required for compaction, creating the memory image and writing it to disk etc. So, we do not allow the user to specify a suspend duration of less than 60 seconds, to be on the safer side, since we don't want to prematurely declare failure when we only had to wait for some more time.
Signed-off-by: Srivatsa S. Bhat <srivatsa.bhat@linux.vnet.ibm.com> ---
src/Makefile.am | 1 src/libvirt.c | 4 + src/libvirt_private.syms | 5 + src/lxc/lxc_driver.c | 2 src/qemu/qemu_driver.c | 2 src/uml/uml_driver.c | 2 src/util/virnodesuspend.c | 269 +++++++++++++++++++++++++++++++++++++++++++++ src/util/virnodesuspend.h | 36 ++++++ src/xen/xen_driver.c | 2 9 files changed, 322 insertions(+), 1 deletions(-) create mode 100644 src/util/virnodesuspend.c create mode 100644 src/util/virnodesuspend.h
ACK Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|

Add a new command 'nodesuspend' to perform a timed suspend on the host. Signed-off-by: Srivatsa S. Bhat <srivatsa.bhat@linux.vnet.ibm.com> --- tools/virsh.c | 64 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ tools/virsh.pod | 7 ++++++ 2 files changed, 71 insertions(+), 0 deletions(-) diff --git a/tools/virsh.c b/tools/virsh.c index 89fb4e7..2b5320b 100644 --- a/tools/virsh.c +++ b/tools/virsh.c @@ -5198,6 +5198,69 @@ cmdNodeMemStats(vshControl *ctl, const vshCmd *cmd) } /* + * "nodesuspend" command + */ +static const vshCmdInfo info_nodesuspend[] = { + {"help", N_("suspend the host node for a given time duration")}, + {"desc", N_("Suspend the host node for a given time duration " + "and attempt to resume thereafter.")}, + {NULL, NULL} +}; + +static const vshCmdOptDef opts_node_suspend[] = { + {"target", VSH_OT_DATA, VSH_OFLAG_REQ, N_("mem(Suspend-to-RAM), " + "disk(Suspend-to-Disk), hybrid(Hybrid-Suspend)")}, + {"duration", VSH_OT_INT, VSH_OFLAG_REQ, N_("Suspend duration in seconds")}, + {"flags", VSH_OT_INT, VSH_OFLAG_NONE, N_("Suspend flags, 0 for default")}, + {NULL, 0, 0, NULL} +}; + +static bool +cmdNodeSuspend(vshControl *ctl, const vshCmd *cmd) +{ + const char *target = NULL; + unsigned int suspendTarget; + long long duration; + unsigned int flags = 0; + + if (!vshConnectionUsability(ctl, ctl->conn)) + return false; + + if (vshCommandOptString(cmd, "target", &target) < 0) + return false; + + if (vshCommandOptLongLong(cmd, "duration", &duration) < 0) + return false; + + if (vshCommandOptUInt(cmd, "flags", &flags) < 0) + return false; + + if (!strcmp(target, "mem")) + suspendTarget = VIR_NODE_SUSPEND_TARGET_MEM; + else if (!strcmp(target, "disk")) + suspendTarget = VIR_NODE_SUSPEND_TARGET_DISK; + else if (!strcmp(target, "hybrid")) + suspendTarget = VIR_NODE_SUSPEND_TARGET_HYBRID; + else { + vshError(ctl, "%s", _("Invalid target")); + return false; + } + + if (duration <= 0) { + vshError(ctl, "%s", _("Invalid duration")); + return false; + } + + if (virNodeSuspendForDuration(ctl->conn, suspendTarget, duration, + flags) < 0) { + vshError(ctl, "%s", _("The host was not suspended")); + return false; + } + return true; +} + + +/* * "capabilities" command */ static const vshCmdInfo info_capabilities[] = { @@ -14712,6 +14775,7 @@ static const vshCmdDef hostAndHypervisorCmds[] = { {"nodecpustats", cmdNodeCpuStats, opts_node_cpustats, info_nodecpustats, 0}, {"nodeinfo", cmdNodeinfo, NULL, info_nodeinfo, 0}, {"nodememstats", cmdNodeMemStats, opts_node_memstats, info_nodememstats, 0}, + {"nodesuspend", cmdNodeSuspend, opts_node_suspend, info_nodesuspend, 0}, {"qemu-attach", cmdQemuAttach, opts_qemu_attach, info_qemu_attach}, {"qemu-monitor-command", cmdQemuMonitorCommand, opts_qemu_monitor_command, info_qemu_monitor_command, 0}, diff --git a/tools/virsh.pod b/tools/virsh.pod index db872dd..f5183b4 100644 --- a/tools/virsh.pod +++ b/tools/virsh.pod @@ -253,6 +253,13 @@ statistics during 1 second. Returns memory stats of the node. If I<cell> is specified, this will prints specified cell statistics only. +=item B<nodesuspend> [I<target>] [I<duration>] [I<flags>] + +Puts the node (host machine) into a system-wide sleep state such as +Suspend-to-RAM, Suspend-to-Disk or Hybrid-Suspend and sets up a +Real-Time-Clock interrupt to fire (to wake up the node) after a time delay +specified by the 'duration' parameter. + =item B<capabilities> Print an XML document describing the capabilities of the hypervisor

On Tue, Nov 29, 2011 at 03:12:44AM +0530, Srivatsa S. Bhat wrote:
Add a new command 'nodesuspend' to perform a timed suspend on the host.
Signed-off-by: Srivatsa S. Bhat <srivatsa.bhat@linux.vnet.ibm.com> ---
tools/virsh.c | 64 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ tools/virsh.pod | 7 ++++++ 2 files changed, 71 insertions(+), 0 deletions(-)
ACK Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|

On Tue, Nov 29, 2011 at 03:12:07AM +0530, Srivatsa S. Bhat wrote:
This patchset adds a new API to put a host to a suspended state (Suspend-to-RAM, Suspend-to-Disk or Hybrid-Suspend) and setup a timed resume to get the host back online, from within libvirt. This uses the RTC wakeup mechanism to set up a timer alarm before suspending the host, so that in-band resume is facilitated by the firing of the RTC alarm, which wakes up the host.
This patch applies on top of the Hybrid-Suspend patch posted in [1]. [...]
Srivatsa S. Bhat (4): Add a public API to invoke suspend/resume on the host Add the remote protocol implementation for virNodeSuspendForDuration Implement the core API to suspend/resume the host Add virsh command to initiate suspend on the host
Okay I finally pushed this series. But all patches except 4/4 failed to apply cleanly on git head, thay all also raised various "make syntax-check" error, which I fixed in the standard way I think. The main issue is that patch 3/4 made "make check" fail all virshtest for some reason (maybe the restricted PATH) the call to virNodeSuspendInit() in virInitialize() was failing, an well in that case nothing work. So I changed virNodeSuspendInit: - to not return -1 if virGetPMCapabilities() fails - only log an error if virGetPMCapabilities() fails and the effective user id is 0 - initialize hostPMFeatures to 0 before checking This code should probably still be revisited, because IMHO: - when run as non-root we should not expose suspend capabilities that we may not be able to use as non-root (pm-suspend works only as root for example, and pm-is-supported seems to work differently as root or as non-root) - the error log was getting in the way of the virshtest checks and we should probably not log error as non root. - I was tempted to not call virNodeSuspendInit() from virInitialize() but I'm afraid the drivers code may rely on those data including the lock being initialized. So my changes certainly need reviews, but the first version is commited now. Thanks, but in the future, please run "make check" and "make syntax-check" on all commits before posting them to the list, Daniel -- Daniel Veillard | libxml Gnome XML XSLT toolkit http://xmlsoft.org/ daniel@veillard.com | Rpmfind RPM search engine http://rpmfind.net/ http://veillard.com/ | virtualization library http://libvirt.org/

On 11/29/2011 03:12 PM, Daniel Veillard wrote:
On Tue, Nov 29, 2011 at 03:12:07AM +0530, Srivatsa S. Bhat wrote:
This patchset adds a new API to put a host to a suspended state (Suspend-to-RAM, Suspend-to-Disk or Hybrid-Suspend) and setup a timed resume to get the host back online, from within libvirt. This uses the RTC wakeup mechanism to set up a timer alarm before suspending the host, so that in-band resume is facilitated by the firing of the RTC alarm, which wakes up the host.
This patch applies on top of the Hybrid-Suspend patch posted in [1]. [...]
Srivatsa S. Bhat (4): Add a public API to invoke suspend/resume on the host Add the remote protocol implementation for virNodeSuspendForDuration Implement the core API to suspend/resume the host Add virsh command to initiate suspend on the host
Okay I finally pushed this series. But all patches except 4/4 failed to apply cleanly on git head, thay all also raised various "make syntax-check" error, which I fixed in the standard way I think. The main issue is that patch 3/4 made "make check" fail all virshtest for some reason (maybe the restricted PATH) the call to virNodeSuspendInit() in virInitialize() was failing, an well in that case nothing work. So I changed virNodeSuspendInit: - to not return -1 if virGetPMCapabilities() fails - only log an error if virGetPMCapabilities() fails and the effective user id is 0 - initialize hostPMFeatures to 0 before checking
This code should probably still be revisited, because IMHO: - when run as non-root we should not expose suspend capabilities that we may not be able to use as non-root (pm-suspend works only as root for example, and pm-is-supported seems to work differently as root or as non-root) - the error log was getting in the way of the virshtest checks and we should probably not log error as non root. - I was tempted to not call virNodeSuspendInit() from virInitialize() but I'm afraid the drivers code may rely on those data including the lock being initialized.
So my changes certainly need reviews, but the first version is commited now.
Thanks a lot!
Thanks, but in the future, please run "make check" and "make syntax-check" on all commits before posting them to the list,
Very sorry, it was an oversight from my side, caused by an eagerness to quickly address all the review comments, to aid further reviews/pushing the patches before the freeze deadline. But in future, I will definitely remember to run all the sanity checks and only then post patches. My sincere apologies for all the trouble I caused because of that! -- Regards, Srivatsa S. Bhat IBM Linux Technology Center
participants (3)
-
Daniel P. Berrange
-
Daniel Veillard
-
Srivatsa S. Bhat