[libvirt] [RFC v3] Export KVM Host Power Management capabilities

This patch exports KVM Host Power Management capabilities as XML so that higher-level systems management software can make use of these features available in the host. The script "pm-is-supported" (from pm-utils package) is run to discover if Suspend-to-RAM (S3) or Suspend-to-Disk (S4) is supported by the host. If either of them are supported, then a new tag "<power_management>" is introduced in the XML under the <host> tag. Eg: When the host supports both S3 and S4, the XML looks like this: <capabilities> <host> <uuid>dc699581-48a2-11cb-b8a8-9a0265a79bbe</uuid> <cpu> <arch>i686</arch> <model>coreduo</model> <vendor>Intel</vendor> <topology sockets='1' cores='2' threads='1'/> <feature name='xtpr'/> <feature name='tm2'/> <feature name='est'/> <feature name='vmx'/> <feature name='pbe'/> <feature name='tm'/> <feature name='ht'/> <feature name='ss'/> <feature name='acpi'/> <feature name='ds'/> </cpu> <power_management> <<<=== New host power management features <S3/> <S4/> </power_management> <migration_features> <live/> <uri_transports> <uri_transport>tcp</uri_transport> </uri_transports> </migration_features> </host> . . . However in case the query to check for power management features succeeded, but the host does not support any such feature, then the XML will contain an empty <power_management/> tag. In the event that the PM query itself failed, the XML will not contain any "power_management" tag. Open issues: ----------- 1. Design new APIs in libvirt to actually exploit the host power management features instead of relying on external programs. This was discussed in [4]. 2. Decide on whether to include "pm-utils" package in the libvirt.spec file considering the fact that the package name (pm-utils) may differ from one Linux distribution to another. Please let me know your comments and feedback. Changelog: --------- v1: The idea of exporting host power management capabilities through libvirt was discussed in [1]. The choice to name the new tag as "power_management" was discussed in [2]. v2: A working implementation was presented for review in [3]. References: ---------- [1] Exporting KVM host power saving capabilities through libvirt http://thread.gmane.org/gmane.comp.emulators.libvirt/40886 [2] http://article.gmane.org/gmane.comp.emulators.libvirt/41688 [3] http://www.redhat.com/archives/libvir-list/2011-August/msg00238.html [4] http://www.redhat.com/archives/libvir-list/2011-August/msg00248.html Signed-off-by: Srivatsa S. Bhat <srivatsa.bhat@linux.vnet.ibm.com> --- docs/formatcaps.html.in | 19 ++++++++++--- docs/schemas/capability.rng | 23 ++++++++++++++++ include/libvirt/virterror.h | 1 + src/conf/capabilities.c | 50 ++++++++++++++++++++++++++++++++++ src/conf/capabilities.h | 8 ++++++ src/libvirt_private.syms | 2 + src/qemu/qemu_capabilities.c | 27 +++++++++++++++++++ src/util/util.c | 61 ++++++++++++++++++++++++++++++++++++++++++ src/util/util.h | 14 ++++++++++ src/util/virterror.c | 3 ++ 10 files changed, 203 insertions(+), 5 deletions(-) diff --git a/docs/formatcaps.html.in b/docs/formatcaps.html.in index a4297ce..ce6f9a6 100644 --- a/docs/formatcaps.html.in +++ b/docs/formatcaps.html.in @@ -28,6 +28,10 @@ BIOS you will see</p> <feature name='xtpr'/> ... </cpu> + <power_management> + <S3/> + <S4/> + <power_management/> </host></span> <!-- xen-3.0-x86_64 --> @@ -61,11 +65,16 @@ BIOS you will see</p> ... </capabilities></pre> <p>The first block (in red) indicates the host hardware capabilities, currently -it is limited to the CPU properties but other information may be available, -it shows the CPU architecture, topology, model name, and additional features -which are not included in the model but the CPU provides them. Features of the -chip are shown within the feature block (the block is similar to what you will -find in a Xen fully virtualized domain description).</p> +it is limited to the CPU properties and the power management features of +the host platform, but other information may be available, it shows the CPU architecture, +topology, model name, and additional features which are not included in the model but the +CPU provides them. Features of the chip are shown within the feature block (the block is +similar to what you will find in a Xen fully virtualized domain description). Further, +the power management features supported by the host are shown, such as Suspend-to-RAM (S3) +and Suspend-to-Disk (S4). In case the query for power management features succeeded but the +host does not support any such feature, then an empty <power_management/> +tag will be shown. Otherwise, if the query itself failed, no such tag will +be displayed (i.e., there will not be any power_management block or empty tag in the XML).</p> <p>The second block (in blue) indicates the paravirtualization support of the Xen support, you will see the os_type of xen to indicate a paravirtual kernel, then architecture information and potential features.</p> diff --git a/docs/schemas/capability.rng b/docs/schemas/capability.rng index 99b4a9a..930374c 100644 --- a/docs/schemas/capability.rng +++ b/docs/schemas/capability.rng @@ -35,6 +35,9 @@ </optional> </element> <optional> + <ref name='power_management'/> + </optional> + <optional> <ref name='migration'/> </optional> <optional> @@ -105,6 +108,26 @@ </zeroOrMore> </define> + <define name='power_management'> + <choice> + <element name='power_management'> + <optional> + <element name='S3'> + <empty/> + </element> + </optional> + <optional> + <element name='S4'> + <empty/> + </element> + </optional> + </element> + <element name='power_management'> + <empty/> + </element> + </choice> + </define> + <define name='migration'> <element name='migration_features'> <optional> diff --git a/include/libvirt/virterror.h b/include/libvirt/virterror.h index 9cac437..a831c73 100644 --- a/include/libvirt/virterror.h +++ b/include/libvirt/virterror.h @@ -82,6 +82,7 @@ typedef enum { VIR_FROM_EVENT = 40, /* Error from event loop impl */ VIR_FROM_LIBXL = 41, /* Error from libxenlight driver */ VIR_FROM_LOCKING = 42, /* Error from lock manager */ + VIR_FROM_CAPABILITIES = 43, /* Error from capabilities */ } virErrorDomain; diff --git a/src/conf/capabilities.c b/src/conf/capabilities.c index 2f243ae..d39a3f9 100644 --- a/src/conf/capabilities.c +++ b/src/conf/capabilities.c @@ -29,6 +29,13 @@ #include "util.h" #include "uuid.h" #include "cpu_conf.h" +#include "virterror_internal.h" + + +#define VIR_FROM_THIS VIR_FROM_CAPABILITIES + +VIR_ENUM_IMPL(virHostPMCapability, VIR_HOST_PM_LAST, + "S3", "S4") /** * virCapabilitiesNew: @@ -166,6 +173,8 @@ virCapabilitiesFree(virCapsPtr caps) { virCapabilitiesFreeNUMAInfo(caps); + VIR_FREE(caps->host.powerMgmt); + for (i = 0 ; i < caps->host.nmigrateTrans ; i++) VIR_FREE(caps->host.migrateTrans[i]); VIR_FREE(caps->host.migrateTrans); @@ -201,6 +210,28 @@ virCapabilitiesAddHostFeature(virCapsPtr caps, return 0; } +/** + * virCapabilitiesAddHostPowerManagement: + * @caps: capabilities to extend + * @feature: the power management feature to be added + * + * Registers a new host power management feature, eg: 'S3' or 'S4' + */ +int +virCapabilitiesAddHostPowerManagement(virCapsPtr caps, + int feature) +{ + if(VIR_RESIZE_N(caps->host.powerMgmt, caps->host.npowerMgmt_max, + caps->host.npowerMgmt, 1) < 0) { + virReportOOMError(); + return -1; + } + + caps->host.powerMgmt[caps->host.npowerMgmt] = feature; + caps->host.npowerMgmt++; + + return 0; +} /** * virCapabilitiesAddHostMigrateTransport: @@ -686,6 +717,25 @@ virCapabilitiesFormatXML(virCapsPtr caps) virBufferAddLit(&xml, " </cpu>\n"); + if(caps->host.isPMQuerySuccess) { + if(caps->host.npowerMgmt) { + /* The PM Query was successful and the host supports + * some PM features. + */ + virBufferAddLit(&xml, " <power_management>\n"); + for (i = 0; i < caps->host.npowerMgmt ; i++) { + virBufferAsprintf(&xml, " <%s/>\n", + virHostPMCapabilityTypeToString(caps->host.powerMgmt[i])); + } + virBufferAddLit(&xml, " </power_management>\n"); + } else { + /* The PM Query was successful but the host does not + * support any PM feature. + */ + virBufferAddLit(&xml, " <power_management/>\n"); + } + } + if (caps->host.offlineMigrate) { virBufferAddLit(&xml, " <migration_features>\n"); if (caps->host.liveMigrate) diff --git a/src/conf/capabilities.h b/src/conf/capabilities.h index e2fa1d6..afbf732 100644 --- a/src/conf/capabilities.h +++ b/src/conf/capabilities.h @@ -105,6 +105,10 @@ struct _virCapsHost { size_t nfeatures; size_t nfeatures_max; char **features; + bool isPMQuerySuccess; + size_t npowerMgmt; + size_t npowerMgmt_max; + int *powerMgmt; /* enum virHostPMCapability */ int offlineMigrate; int liveMigrate; size_t nmigrateTrans; @@ -186,6 +190,10 @@ virCapabilitiesAddHostFeature(virCapsPtr caps, const char *name); extern int +virCapabilitiesAddHostPowerManagement(virCapsPtr caps, + int feature); + +extern int virCapabilitiesAddHostMigrateTransport(virCapsPtr caps, const char *name); diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 830222b..5754fdd 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -41,6 +41,7 @@ virCapabilitiesAddGuestFeature; virCapabilitiesAddHostFeature; virCapabilitiesAddHostMigrateTransport; virCapabilitiesAddHostNUMACell; +virCapabilitiesAddHostPowerManagement; virCapabilitiesAllocMachines; virCapabilitiesDefaultGuestArch; virCapabilitiesDefaultGuestEmulator; @@ -1025,6 +1026,7 @@ safezero; virArgvToString; virAsprintf; virBuildPathInternal; +virCheckPMCapability; virDirCreate; virEmitXMLWarning; virEnumFromString; diff --git a/src/qemu/qemu_capabilities.c b/src/qemu/qemu_capabilities.c index 3f36212..f3d0c0a 100644 --- a/src/qemu/qemu_capabilities.c +++ b/src/qemu/qemu_capabilities.c @@ -794,6 +794,7 @@ virCapsPtr qemuCapsInit(virCapsPtr old_caps) struct utsname utsname; virCapsPtr caps; int i; + int status = -1; char *xenner = NULL; /* Really, this never fails - look at the man-page. */ @@ -824,6 +825,32 @@ virCapsPtr qemuCapsInit(virCapsPtr old_caps) old_caps->host.cpu = NULL; } + /* Add the power management features of the host */ + + /* Check for Suspend-to-RAM support (S3) */ + status = virCheckPMCapability(VIR_HOST_PM_S3); + if(status < 0) { + caps->host.isPMQuerySuccess = false; + VIR_WARN("Failed to get host power management features"); + } else { + /* The PM Query succeeded */ + caps->host.isPMQuerySuccess = true; + if(status == 1) /* S3 is supported */ + virCapabilitiesAddHostPowerManagement(caps, VIR_HOST_PM_S3); + } + + /* Check for Suspend-to-Disk support (S4) */ + status = virCheckPMCapability(VIR_HOST_PM_S4); + if(status < 0) { + caps->host.isPMQuerySuccess = false; + VIR_WARN("Failed to get host power management features"); + } else { + /* The PM Query succeeded */ + caps->host.isPMQuerySuccess = true; + if(status == 1) /* S4 is supported */ + virCapabilitiesAddHostPowerManagement(caps, VIR_HOST_PM_S4); + } + virCapabilitiesAddHostMigrateTransport(caps, "tcp"); diff --git a/src/util/util.c b/src/util/util.c index 03a9e1a..489c4d6 100644 --- a/src/util/util.c +++ b/src/util/util.c @@ -2641,3 +2641,64 @@ or other application using the libvirt API.\n\ return 0; } + +/** + * Check the Power Management Capabilities of the host system. + * The script 'pm-is-supported' (from the pm-utils package) is run + * to find out if the capability is supported by the host. + * + * @capability: capability to check for + * VIR_HOST_PM_S3: Check for Suspend-to-RAM support + * VIR_HOST_PM_S4: Check for Suspend-to-Disk support + * + * Return values: + * 1 if the capability is supported. + * 0 if the query was successful but the capability is + * not supported by the host. + * -1 on error like 'pm-is-supported' is not found. + */ +int +virCheckPMCapability(int capability) +{ + + char *path = NULL; + int status = -1; + int ret = -1; + virCommandPtr cmd; + + if((path = virFindFileInPath("pm-is-supported")) == NULL) { + virUtilError(VIR_ERR_INTERNAL_ERROR, + "%s", _("Failed to get the path of pm-is-supported")); + return -1; + } + + cmd = virCommandNew(path); + switch(capability) { + case VIR_HOST_PM_S3: + /* Check support for suspend (S3) */ + virCommandAddArg(cmd, "--suspend"); + break; + + case VIR_HOST_PM_S4: + /* Check support for hibernation (S4) */ + virCommandAddArg(cmd, "--hibernate"); + break; + + default: + goto cleanup; + } + + if(virCommandRun(cmd, &status) < 0) + goto cleanup; + + /* Check return code of command == 0 for success + * (i.e., the PM capability is supported) + */ + ret = (status == 0) ? 1 : 0; + +cleanup: + virCommandFree(cmd); + VIR_FREE(path); + return ret; +} + diff --git a/src/util/util.h b/src/util/util.h index af8b15d..dfb8c1a 100644 --- a/src/util/util.h +++ b/src/util/util.h @@ -272,4 +272,18 @@ bool virIsDevMapperDevice(const char *devname) ATTRIBUTE_NONNULL(1); int virEmitXMLWarning(int fd, const char *name, const char *cmd) ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(3); + +/* Power Management Capabilities of the host system */ + +enum virHostPMCapability { + VIR_HOST_PM_S3, /* Suspend-to-RAM */ + VIR_HOST_PM_S4, /* Suspend-to-Disk */ + + VIR_HOST_PM_LAST +}; + +VIR_ENUM_DECL(virHostPMCapability) + +int virCheckPMCapability(int capability); + #endif /* __VIR_UTIL_H__ */ diff --git a/src/util/virterror.c b/src/util/virterror.c index 9a27feb..26d6011 100644 --- a/src/util/virterror.c +++ b/src/util/virterror.c @@ -148,6 +148,9 @@ static const char *virErrorDomainName(virErrorDomain domain) { case VIR_FROM_CPU: dom = "CPU "; break; + case VIR_FROM_CAPABILITIES: + dom = "Capabilities "; + break; case VIR_FROM_NWFILTER: dom = "Network Filter "; break;

On 08/08/2011 09:18 AM, Srivatsa S. Bhat wrote:
Open issues: ----------- 1. Design new APIs in libvirt to actually exploit the host power management features instead of relying on external programs. This was discussed in [4].
As pointed out in [4], until we have additional use for this feature, then this feature in isolation is unlikely to be pushed. That is not meant to discourage you from developing further patches, rather it is just stating that this patch will probably be deferred until it can be pushed as part of a larger series.
2. Decide on whether to include "pm-utils" package in the libvirt.spec file considering the fact that the package name (pm-utils) may differ from one Linux distribution to another.
No decision to make - libvirt.spec is solely for Fedora, where the package is always named pm-utils. Other distros use other packaging files, and can adjust accordingly, it's just that none of those other packaging files have been contributed for inclusion in upstream libvirt.git.
+ <define name='power_management'> + <choice>
No need for a <choice> here.
+ <element name='power_management'> + <optional> + <element name='S3'> + <empty/> + </element> + </optional> + <optional> + <element name='S4'> + <empty/> + </element> + </optional> + </element>
The two <optional> blocks are sufficient for allowing <power_management/> as an empty element, without any further rng grammar.
+/** + * virCapabilitiesAddHostPowerManagement: + * @caps: capabilities to extend + * @feature: the power management feature to be added + * + * Registers a new host power management feature, eg: 'S3' or 'S4' + */ +int +virCapabilitiesAddHostPowerManagement(virCapsPtr caps, + int feature) +{ + if(VIR_RESIZE_N(caps->host.powerMgmt, caps->host.npowerMgmt_max, + caps->host.npowerMgmt, 1)< 0) { + virReportOOMError(); + return -1; + } + + caps->host.powerMgmt[caps->host.npowerMgmt] = feature; + caps->host.npowerMgmt++;
After thinking about this more, I'm wondering if it would be smarter to represent power management as a bitmask: caps->hst.powerMgmt |= 1U << feature; at which point, you still need isPMQuerySuccess (but see below on naming), but you don't need npowerMgmt. Instead, output would be:
@@ -686,6 +717,25 @@ virCapabilitiesFormatXML(virCapsPtr caps)
virBufferAddLit(&xml, "</cpu>\n");
+ if(caps->host.isPMQuerySuccess) {
if (caps->host.powerMgmt) { unsigned int pm = caps->host.powerMgmt; virBufferAddLit(&xml, "<power_management>\n"); while (pm) { int bit = ffs(pm) - 1; virBufferAsprintf(&xml, "<%s/>\n", virHostPMCapabilityTypeToString(bit); pm &= ~bit; } virBufferAddLit(&xml, "</power_management>\n"); } else { virBufferAddLit(&xml, "<power_management/>\n"); }
+++ b/src/conf/capabilities.h @@ -105,6 +105,10 @@ struct _virCapsHost { size_t nfeatures; size_t nfeatures_max; char **features; + bool isPMQuerySuccess;
This isn't typical naming. For consistency, I'm thinking: bool powerMgmt_valid; defaulting to false (powerMgmt is irrelavant), but set to true when powerMgmt contains valid data (where 0 can include valid data).
+ size_t npowerMgmt; + size_t npowerMgmt_max; + int *powerMgmt; /* enum virHostPMCapability */
See above about just using 'unsigned int powerMgmt' as a bit-mask of valid capabilities, rather than a dynamically allocated array of ints.
@@ -824,6 +825,32 @@ virCapsPtr qemuCapsInit(virCapsPtr old_caps) old_caps->host.cpu = NULL; }
+ /* Add the power management features of the host */ + + /* Check for Suspend-to-RAM support (S3) */ + status = virCheckPMCapability(VIR_HOST_PM_S3); + if(status< 0) { + caps->host.isPMQuerySuccess = false; + VIR_WARN("Failed to get host power management features"); + } else { + /* The PM Query succeeded */ + caps->host.isPMQuerySuccess = true; + if(status == 1) /* S3 is supported */ + virCapabilitiesAddHostPowerManagement(caps, VIR_HOST_PM_S3); + } + + /* Check for Suspend-to-Disk support (S4) */ + status = virCheckPMCapability(VIR_HOST_PM_S4); + if(status< 0) { + caps->host.isPMQuerySuccess = false; + VIR_WARN("Failed to get host power management features"); + } else { + /* The PM Query succeeded */ + caps->host.isPMQuerySuccess = true; + if(status == 1) /* S4 is supported */ + virCapabilitiesAddHostPowerManagement(caps, VIR_HOST_PM_S4); + }
You don't want to set caps->host.isPMQuerySuccess (by whatever name) to true unless both commands succeeded. If the first command succeeds but the second fails, then you are better off treating the entire operation as failed. Hmm, maybe virCheckPMCapability should take no arguments, and return the bitmask directly, rather than making qemuCapsInit have to compute the right bitmask.
} + +/** + * Check the Power Management Capabilities of the host system. + * The script 'pm-is-supported' (from the pm-utils package) is run + * to find out if the capability is supported by the host. + * + * @capability: capability to check for + * VIR_HOST_PM_S3: Check for Suspend-to-RAM support + * VIR_HOST_PM_S4: Check for Suspend-to-Disk support + * + * Return values: + * 1 if the capability is supported. + * 0 if the query was successful but the capability is + * not supported by the host. + * -1 on error like 'pm-is-supported' is not found. + */ +int +virCheckPMCapability(int capability) +{
As I mentioned above, it would be nicer to have this return the bitmask up front, rather than to make each caller have to repeatedly call this function for as many capabilities as are currently defined. -1 for unknown, 0 for success but no support, or positive for a bitmask of 1<<bit for each supported bit.
+++ b/src/util/virterror.c @@ -148,6 +148,9 @@ static const char *virErrorDomainName(virErrorDomain domain) { case VIR_FROM_CPU: dom = "CPU "; break; + case VIR_FROM_CAPABILITIES: + dom = "Capabilities "; + break; case VIR_FROM_NWFILTER: dom = "Network Filter "; break;
Better to make these case statements line up with declaration order (VIR_FROM_CAPABILITIES is new, so it should be at the end of the list just before default:). -- Eric Blake eblake@redhat.com +1-801-349-2682 Libvirt virtualization library http://libvirt.org

On 08/08/2011 10:18 AM, Eric Blake wrote: Correction on my bitmask idea:
+ if(caps->host.isPMQuerySuccess) {
if (caps->host.powerMgmt) { unsigned int pm = caps->host.powerMgmt; virBufferAddLit(&xml, "<power_management>\n"); while (pm) { int bit = ffs(pm) - 1; virBufferAsprintf(&xml, "<%s/>\n", virHostPMCapabilityTypeToString(bit); pm &= ~bit;
pm &= ~(1U << bit); -- Eric Blake eblake@redhat.com +1-801-349-2682 Libvirt virtualization library http://libvirt.org
participants (2)
-
Eric Blake
-
Srivatsa S. Bhat