From nobody Tue Jun 9 21:16:12 2026 Delivered-To: importer@patchew.org Received-SPF: pass (zohomail.com: domain of lists.libvirt.org designates 38.145.34.151 as permitted sender) client-ip=38.145.34.151; envelope-from=devel-bounces@lists.libvirt.org; helo=lists.libvirt.org; Authentication-Results: mx.zohomail.com; dkim=pass header.i=@intel.com; spf=pass (zohomail.com: domain of lists.libvirt.org designates 38.145.34.151 as permitted sender) smtp.mailfrom=devel-bounces@lists.libvirt.org; dmarc=pass(p=none dis=none) header.from=intel.com ARC-Seal: i=1; a=rsa-sha256; t=1778592663; cv=none; d=zohomail.com; s=zohoarc; b=gpT7bbLmgTWL4Q6SgM7qXKUT42zt2u6MPc+PMjYSot6Du4hJec20vHJ3TJXb/Jv2JkrcuL0vPX/Fa0EqJSah+8cM1oiy2d4Or3mHt0Fv3tM76Q3s99CHx5eLb5gEduMngGqhimImMay4ORi2oYnUfjgkCJzg3mXo6pRQiqRw3Lk= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1778592663; h=Content-Type:Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:List-Subscribe:List-Post:List-Owner:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:Subject:Subject:To:To:Message-Id:Reply-To; bh=9T7cZuYEQKROw8XMCNPa20zVkamW8LPMJp6m8fhyrSM=; b=axM8w5TE0UY6tbY3UIZOUAVPJeUyPqWFh6hRwIZixg7rs+3GmFf9oTi1bpWLSBLBPofJa1rPErUiQNY41wEqtx/zsjeiK1WhF9AIVsjqZjCuXSjlbNZ6EP5h+9IDna2ECP8Lu70h+NOtyUv8MMLP3C2KlEOTLhXVQgRh6YGnxEM= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass header.i=@intel.com; spf=pass (zohomail.com: domain of lists.libvirt.org designates 38.145.34.151 as permitted sender) smtp.mailfrom=devel-bounces@lists.libvirt.org; dmarc=pass header.from= (p=none dis=none) Return-Path: Received: from lists.libvirt.org (lists.libvirt.org [38.145.34.151]) by mx.zohomail.com with SMTPS id 1778592663471213.57998381561458; Tue, 12 May 2026 06:31:03 -0700 (PDT) Received: by lists.libvirt.org (Postfix, from userid 993) id 1DD4D418BD; Tue, 12 May 2026 09:31:02 -0400 (EDT) Received: from [172.19.199.9] (unknown [10.16.107.18]) by lists.libvirt.org (Postfix) with ESMTP id BB80841852; Tue, 12 May 2026 09:22:39 -0400 (EDT) Received: by lists.libvirt.org (Postfix, from userid 993) id 1E37B4185F; Tue, 12 May 2026 09:17:30 -0400 (EDT) Received: from mgamail.intel.com (mgamail.intel.com [198.175.65.9]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by lists.libvirt.org (Postfix) with ESMTPS id 47B8B41846 for ; Tue, 12 May 2026 09:17:25 -0400 (EDT) Received: from orviesa008.jf.intel.com ([10.64.159.148]) by orvoesa101.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 12 May 2026 06:16:20 -0700 Received: from jwasiuki-pc.ger.corp.intel.com ([10.237.154.244]) by orviesa008-auth.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 12 May 2026 06:16:20 -0700 X-Spam-Checker-Version: SpamAssassin 4.0.1 (2024-03-26) on lists.libvirt.org X-Spam-Level: X-Spam-Status: No, score=-5.5 required=5.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS, MAILING_LIST_MULTI,RCVD_IN_DNSWL_MED,SPF_HELO_NONE autolearn=unavailable autolearn_force=no version=4.0.1 X-Greylist: delayed 63 seconds by postgrey-1.37 at lists.libvirt.org; Tue, 12 May 2026 09:17:25 EDT DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1778591845; x=1810127845; h=from:to:cc:subject:date:message-id:mime-version: content-transfer-encoding; bh=+fJGvdbePpOw84SwsYDbgk/OBB5IAZJt2w5mhIDcgVc=; b=XGFJNN4e6UcrMnPDEicqZJeVm2DGrhCPfFHLsxJlmJJUZ8qzg5V+ubDe K5qnaNzCV/oETsQFjH8IWRRAsHOC+PjAdCD/8AVnf8YZ1JXI0jECIC/SD cmXrH4XFwW0iPzDDmTVkavpT98CLAE8Hb7pB5QmBXVhmZrbSs3yUX5Cpy oQTyyNRslXxeJzagAZtrd+g6A3N+2eV55ss8hd+JrYlrM6x181wJ/+BGo YMKT2BChIwLAc0PjAy5Bnhq4/HSgS2+bnhTw3iouvHJgx/tRwjUvQkZei M0PFMwTGDPt+trp20EMwQI2BYSsX0Xaw43aLvbehyDaLXDOQF18Zz4fYu Q==; X-CSE-ConnectionGUID: 8HNkHvGzTZmWzZDwwDa/gQ== X-CSE-MsgGUID: g3fONnRXQeyrdybmjMqZPw== X-IronPort-AV: E=McAfee;i="6800,10657,11783"; a="102163432" X-IronPort-AV: E=Sophos;i="6.23,230,1770624000"; d="scan'208";a="102163432" X-CSE-ConnectionGUID: XavyiphESGqi1HJJMXoV0g== X-CSE-MsgGUID: 9lCj7TLtRACKlfuLNMA6Qw== X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="6.23,230,1770624000"; d="scan'208";a="237698200" From: Jedrzej Wasiukiewicz To: devel@lists.libvirt.org Subject: [RFC PATCH] resctrl: add energy monitoring via resctrl's PERF_PKG_MON Date: Tue, 12 May 2026 15:13:58 +0200 Message-Id: <20260512131358.614802-1-jedrzej.wasiukiewicz@intel.com> X-Mailer: git-send-email 2.34.1 MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-MailFrom: jedrzej.wasiukiewicz@intel.com X-Mailman-Rule-Hits: nonmember-moderation X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; loop; banned-address; header-match-devel.lists.libvirt.org-0; emergency; member-moderation Message-ID-Hash: ZVX3TMA7E2O5HUJHSSLGRKRU6YQ25KLE X-Message-ID-Hash: ZVX3TMA7E2O5HUJHSSLGRKRU6YQ25KLE X-Mailman-Approved-At: Tue, 12 May 2026 13:22:20 +0000 CC: Jedrzej Wasiukiewicz , "Christopher M . Cantalupo" X-Mailman-Version: 3.3.10 Precedence: list List-Id: Development discussions about the libvirt library & tools Archived-At: List-Archive: List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: X-ZohoMail-DKIM: pass (identity @intel.com) X-ZM-MESSAGEID: 1778592665224158500 Content-Type: text/plain; charset="utf-8" Linux kernel 7.0 introduced PERF_PKG_MON support in resctrl filesystem which exposes per-workload energy and performance monitoring. This patch enables per-VM energy monitoring via core_energy (Joules) and activity (Farads) counters. Energy monitors can be configured through new element under following earlier cachetune and memorytune patterns. Design notes: Energy values from resctrl are floating-point. I added separate dvals/ndvals pair to virResctrlMonitorStats to handle them. I kept them in a single struct for easier integration with performance counters (integers and floats within same monitor) that might be integrated in another patch. The new XML element is under following earlier patte= rn for resctrl features (cachetune, memorytune). Energytune doesn't currently supp= ort the "tuning" part, only monitoring. I added it as energytune for consistenc= y with cache and memory features, keeping all resctrl handling under cputune. This= also makes sense with current resctrl architecture - all monitoring groups are part of= an allocation group. This approach allows for easy opt-in/out on the monitorin= g features. Signed-off-by: Jedrzej Wasiukiewicz Signed-off-by: Christopher M. Cantalupo --- Please let me know if there's a better approach for this. I'm happy to rewo= rk the design based on feedback. I can also split this patch if necessary. NEWS.rst | 6 + docs/formatdomain.rst | 20 ++ include/libvirt/libvirt-domain.h | 65 +++++++ src/conf/capabilities.c | 42 ++++ src/conf/capabilities.h | 6 + src/conf/domain_conf.c | 99 ++++++++++ src/conf/schemas/capability.rng | 26 +++ src/conf/schemas/domaincommon.rng | 19 ++ src/conf/virconftypes.h | 2 + src/qemu/qemu_driver.c | 70 ++++++- src/util/virresctrl.c | 180 +++++++++++++++--- src/util/virresctrl.h | 12 +- .../genericxml2xmlindata/energytune-basic.xml | 32 ++++ tests/genericxml2xmltest.c | 1 + 14 files changed, 543 insertions(+), 37 deletions(-) create mode 100644 tests/genericxml2xmlindata/energytune-basic.xml diff --git a/NEWS.rst b/NEWS.rst index 105a398ca8..02dda023ed 100644 --- a/NEWS.rst +++ b/NEWS.rst @@ -17,6 +17,12 @@ v12.4.0 (unreleased) =20 * **New features** =20 + * resctrl: Add energy monitoring via resctrl's PERF_PKG_MON + + Add support for Linux kernel 7.0 feature - energy monitoring via resct= rl. + This allows to monitor per-VM energy consumption on supported platform= s. + Implemented via ``energytune`` element in ``cputune`` . + * **Improvements** =20 * **Bug fixes** diff --git a/docs/formatdomain.rst b/docs/formatdomain.rst index 078cd7aa84..db1ca5637a 100644 --- a/docs/formatdomain.rst +++ b/docs/formatdomain.rst @@ -900,6 +900,9 @@ CPU Tuning + + + =20 ... @@ -1084,6 +1087,23 @@ CPU Tuning responsible for making sure the value makes sense on their system= and configuration. =20 +``energytune`` :since:`Since 12.4.0` + Optional ``energytune`` element allows to monitor energy consumption us= ing the + resctrl filesystem on the host. Whether or not is this supported can be + gathered from capabilities where number of monitors and available featu= res are + reported. The required attribute ``vcpus`` specifies to which allocatio= n group + this monitor belongs. A vCPU can only be member of one allocation group= and monitor + group. The ``vcpus`` specified by ``energytune`` can be identical to th= ose + specified by ``cachetune`` or ``memorytune``. However they are not allo= wed to + overlap each other. Supported subelements are: + + ``monitor`` + The optional element ``monitor`` creates the energy monitor for + this allocation group and has the following required attribute: + + ``vcpus`` + vCPU list the monitor applies to. + =20 Memory Allocation ----------------- diff --git a/include/libvirt/libvirt-domain.h b/include/libvirt/libvirt-dom= ain.h index 4a8e3114b3..3ba1b27743 100644 --- a/include/libvirt/libvirt-domain.h +++ b/include/libvirt/libvirt-domain.h @@ -4401,6 +4401,71 @@ struct _virDomainStatsRecord { # define VIR_DOMAIN_STATS_MEMORY_BANDWIDTH_MONITOR_SUFFIX_NODE_SUFFIX_BYTE= S_TOTAL ".bytes.total" =20 =20 +/** + * VIR_DOMAIN_STATS_CPU_ENERGY_MONITOR_COUNT: + * + * The number of energy monitors for this domain, as an unsigned int. + * + * Since: 12.4.0 + */ +# define VIR_DOMAIN_STATS_CPU_ENERGY_MONITOR_COUNT "cpu.energy.monitor.cou= nt" + +/** + * VIR_DOMAIN_STATS_CPU_ENERGY_MONITOR_PREFIX: + * + * Prefix for an individual energy monitor group. Concatenate + * with the monitor index and one of the "cpu.energy.monitor.." suffix + * macros below to form a full parameter name. + * + * Since: 12.4.0 + */ +# define VIR_DOMAIN_STATS_CPU_ENERGY_MONITOR_PREFIX "cpu.energy.monitor." + +/** + * VIR_DOMAIN_STATS_CPU_ENERGY_MONITOR_SUFFIX_NAME: + * + * Name of the monitor group as a string. + * + * Since: 12.4.0 + */ +# define VIR_DOMAIN_STATS_CPU_ENERGY_MONITOR_SUFFIX_NAME ".name" + +/** + * VIR_DOMAIN_STATS_CPU_ENERGY_MONITOR_SUFFIX_VCPUS: + * + * vCPU set covered by the monitor group as a string. + * + * Since: 12.4.0 + */ +# define VIR_DOMAIN_STATS_CPU_ENERGY_MONITOR_SUFFIX_VCPUS ".vcpus" + +/** + * VIR_DOMAIN_STATS_CPU_ENERGY_MONITOR_SUFFIX_PKG_COUNT: + * + * Number of PERF_PKG nodes the monitor group exposes, as an unsigned int. + * + * Since: 12.4.0 + */ +# define VIR_DOMAIN_STATS_CPU_ENERGY_MONITOR_SUFFIX_PKG_COUNT ".pkg.count" + +/** + * VIR_DOMAIN_STATS_CPU_ENERGY_MONITOR_SUFFIX_PKG_PREFIX: + * + * Prefix for a single mon_PERF_PKG node inside a monitor group. + * + * Since: 12.4.0 + */ +# define VIR_DOMAIN_STATS_CPU_ENERGY_MONITOR_SUFFIX_PKG_PREFIX ".pkg." + +/** + * VIR_DOMAIN_STATS_CPU_ENERGY_MONITOR_SUFFIX_PKG_SUFFIX_ID: + * + * Kernel-assigned mon_PERF_PKG node id, as an unsigned int. + * + * Since: 12.4.0 + */ +# define VIR_DOMAIN_STATS_CPU_ENERGY_MONITOR_SUFFIX_PKG_SUFFIX_ID ".id" + /** * VIR_DOMAIN_STATS_DIRTYRATE_CALC_STATUS: * diff --git a/src/conf/capabilities.c b/src/conf/capabilities.c index 1821e36e61..e83de6d1bc 100644 --- a/src/conf/capabilities.c +++ b/src/conf/capabilities.c @@ -263,6 +263,8 @@ virCapsDispose(void *object) virResctrlInfoMonFree(caps->host.memBW.monitor); g_free(caps->host.memBW.nodes); =20 + virResctrlInfoMonFree(caps->host.energy.monitor); + g_free(caps->host.netprefix); g_free(caps->host.pagesSize); virCPUDefFree(caps->host.cpu); @@ -1057,6 +1059,26 @@ virCapabilitiesFormatMemoryBandwidth(virBuffer *buf, } =20 =20 +static int +virCapabilitiesFormatEnergy(virBuffer *buf, + virCapsHostEnergy *energy) +{ + if (!energy->monitor) + return 0; + + virBufferAddLit(buf, "\n"); + virBufferAdjustIndent(buf, 2); + + if (virCapabilitiesFormatResctrlMonitor(buf, energy->monitor) < 0) + return -1; + + virBufferAdjustIndent(buf, -2); + virBufferAddLit(buf, "\n"); + + return 0; +} + + static int virCapabilitiesFormatHostXML(virCapsHost *host, virBuffer *buf) @@ -1156,6 +1178,9 @@ virCapabilitiesFormatHostXML(virCapsHost *host, if (virCapabilitiesFormatMemoryBandwidth(buf, &host->memBW) < 0) return -1; =20 + if (virCapabilitiesFormatEnergy(buf, &host->energy) < 0) + return -1; + for (i =3D 0; i < host->nsecModels; i++) { virBufferAddLit(buf, "\n"); virBufferAdjustIndent(buf, 2); @@ -2143,6 +2168,20 @@ virCapabilitiesInitResctrlMemory(virCaps *caps) } =20 =20 +static int +virCapabilitiesInitEnergy(virCaps *caps) +{ + const char *prefix =3D virResctrlMonitorPrefixTypeToString( + VIR_RESCTRL_MONITOR_TYPE_ENERGY); + + if (virResctrlInfoGetMonitorPrefix(caps->host.resctrl, prefix, + &caps->host.energy.monitor) < 0) + return -1; + + return 0; +} + + int virCapabilitiesInitCaches(virCaps *caps) { @@ -2294,6 +2333,9 @@ virCapabilitiesInitCaches(virCaps *caps) &caps->host.cache.monitor) < 0) return -1; =20 + if (virCapabilitiesInitEnergy(caps) < 0) + return -1; + return 0; } =20 diff --git a/src/conf/capabilities.h b/src/conf/capabilities.h index daea835817..0482e4297a 100644 --- a/src/conf/capabilities.h +++ b/src/conf/capabilities.h @@ -162,6 +162,10 @@ struct _virCapsHostMemBW { virResctrlInfoMon *monitor; }; =20 +struct _virCapsHostEnergy { + virResctrlInfoMon *monitor; +}; + struct _virCapsHost { virArch arch; size_t nfeatures; @@ -184,6 +188,8 @@ struct _virCapsHost { =20 virCapsHostMemBW memBW; =20 + virCapsHostEnergy energy; + size_t nsecModels; virCapsHostSecModel *secModels; =20 diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 3497e84bf5..946b34abf3 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -19467,6 +19467,57 @@ virDomainMemorytuneDefParse(virDomainDef *def, } =20 =20 +static int +virDomainEnergytuneDefParse(virDomainDef *def, + xmlXPathContextPtr ctxt, + xmlNodePtr node, + unsigned int flags) +{ + VIR_XPATH_NODE_AUTORESTORE(ctxt) + virDomainResctrlDef *resctrl =3D NULL; + virDomainResctrlDef *newresctrl =3D NULL; + g_autoptr(virBitmap) vcpus =3D NULL; + g_autoptr(virResctrlAlloc) alloc =3D NULL; + size_t nmons; + int ret =3D -1; + + ctxt->node =3D node; + + if (virDomainResctrlParseVcpus(def, node, &vcpus) < 0) + return -1; + + if (virBitmapIsAllClear(vcpus)) + return 0; + + if (virDomainResctrlVcpuMatch(def, vcpus, &resctrl) < 0) + return -1; + + if (resctrl) { + alloc =3D virObjectRef(resctrl->alloc); + } else { + if (!(alloc =3D virResctrlAllocNew())) + return -1; + if (!(newresctrl =3D virDomainResctrlNew(node, alloc, vcpus, flags= ))) + return -1; + resctrl =3D newresctrl; + } + + nmons =3D resctrl->nmonitors; + if (virDomainResctrlMonDefParse(def, ctxt, node, + VIR_RESCTRL_MONITOR_TYPE_ENERGY, + resctrl) < 0) + goto cleanup; + + if (newresctrl && resctrl->nmonitors > nmons) + VIR_APPEND_ELEMENT(def->resctrls, def->nresctrls, newresctrl); + + ret =3D 0; + cleanup: + virDomainResctrlDefFree(newresctrl); + return ret; +} + + static int virDomainDefTunablesParse(virDomainDef *def, xmlXPathContextPtr ctxt, @@ -19671,6 +19722,15 @@ virDomainDefTunablesParse(virDomainDef *def, } VIR_FREE(nodes); =20 + if ((n =3D virXPathNodeSet("./cputune/energytune", ctxt, &nodes)) < 0) + return -1; + + for (i =3D 0; i < n; i++) { + if (virDomainEnergytuneDefParse(def, ctxt, nodes[i], flags) < 0) + return -1; + } + VIR_FREE(nodes); + return 0; } =20 @@ -28721,6 +28781,42 @@ virDomainMemorytuneDefFormat(virBuffer *buf, return 0; } =20 + +static int +virDomainEnergytuneDefFormat(virBuffer *buf, + virDomainResctrlDef *resctrl, + unsigned int flags) +{ + g_auto(virBuffer) childrenBuf =3D VIR_BUFFER_INIT_CHILD(buf); + g_auto(virBuffer) attrBuf =3D VIR_BUFFER_INITIALIZER; + g_autofree char *vcpus =3D NULL; + size_t i; + + for (i =3D 0; i < resctrl->nmonitors; i++) { + if (virDomainResctrlMonDefFormatHelper(resctrl->monitors[i], + VIR_RESCTRL_MONITOR_TYPE_EN= ERGY, + &childrenBuf) < 0) + return -1; + } + + if (!virBufferUse(&childrenBuf)) + return 0; + + vcpus =3D virBitmapFormat(resctrl->vcpus); + virBufferAsprintf(&attrBuf, " vcpus=3D'%s'", vcpus); + + if (!(flags & VIR_DOMAIN_DEF_FORMAT_INACTIVE)) { + const char *alloc_id =3D virResctrlAllocGetID(resctrl->alloc); + if (!alloc_id) + return -1; + + virBufferAsprintf(&attrBuf, " id=3D'%s'", alloc_id); + } + + virXMLFormatElement(buf, "energytune", &attrBuf, &childrenBuf); + return 0; +} + static int virDomainCputuneDefFormat(virBuffer *buf, virDomainDef *def, @@ -28821,6 +28917,9 @@ virDomainCputuneDefFormat(virBuffer *buf, for (i =3D 0; i < def->nresctrls; i++) virDomainMemorytuneDefFormat(&childrenBuf, def->resctrls[i], flags= ); =20 + for (i =3D 0; i < def->nresctrls; i++) + virDomainEnergytuneDefFormat(&childrenBuf, def->resctrls[i], flags= ); + virXMLFormatElement(buf, "cputune", NULL, &childrenBuf); =20 return 0; diff --git a/src/conf/schemas/capability.rng b/src/conf/schemas/capability.= rng index 8ef6e9a282..2aa711178f 100644 --- a/src/conf/schemas/capability.rng +++ b/src/conf/schemas/capability.rng @@ -45,6 +45,9 @@ + + + @@ -333,6 +336,29 @@ =20 + + + [a-zA-Z0-9\-_]+ + + + + + + + + + + + + + + + + + + + + diff --git a/src/conf/schemas/domaincommon.rng b/src/conf/schemas/domaincom= mon.rng index 8c03e14d37..eb365a83b5 100644 --- a/src/conf/schemas/domaincommon.rng +++ b/src/conf/schemas/domaincommon.rng @@ -1293,6 +1293,25 @@ + + + + + + + + + + + + + + + + + + + diff --git a/src/conf/virconftypes.h b/src/conf/virconftypes.h index 0596791a4d..f1a200bfe2 100644 --- a/src/conf/virconftypes.h +++ b/src/conf/virconftypes.h @@ -52,6 +52,8 @@ typedef struct _virCapsHostMemBW virCapsHostMemBW; =20 typedef struct _virCapsHostMemBWNode virCapsHostMemBWNode; =20 +typedef struct _virCapsHostEnergy virCapsHostEnergy; + typedef struct _virCapsHostNUMA virCapsHostNUMA; =20 typedef struct _virCapsHostNUMACell virCapsHostNUMACell; diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 529e9fe3be..3fafaaea19 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -16917,11 +16917,11 @@ qemuDomainFreeResctrlMonData(virQEMUResctrlMonDat= a *resdata) * returns an error, the caller is also required to call * qemuDomainFreeResctrlMonData to free each element in the * *@resdata array and then the array itself. - * @tag: Could be VIR_RESCTRL_MONITOR_TYPE_CACHE for getting cache statist= ics - * from @dom cache monitors. VIR_RESCTRL_MONITOR_TYPE_MEMBW for - * getting memory bandwidth statistics from memory bandwidth monitor= s. + * @tag: VIR_RESCTRL_MONITOR_TYPE_CACHE for getting cache statistics. + * VIR_RESCTRL_MONITOR_TYPE_MEMBW for getting memory bandwidth stati= stics. + * VIR_RESCTRL_MONITOR_TYPE_ENERGY for getting energy statistics. * - * Get cache or memory bandwidth statistics from @dom monitors. + * Get cache, memory bandwidth or energy statistics from @dom monitors. * * Returns -1 on failure, or 0 on success. */ @@ -16951,6 +16951,10 @@ qemuDomainGetResctrlMonData(virQEMUDriver *driver, if (caps->host.memBW.monitor) features =3D caps->host.memBW.monitor->features; break; + case VIR_RESCTRL_MONITOR_TYPE_ENERGY: + if (caps->host.energy.monitor) + features =3D caps->host.energy.monitor->features; + break; case VIR_RESCTRL_MONITOR_TYPE_UNSUPPORT: case VIR_RESCTRL_MONITOR_TYPE_LAST: virReportError(VIR_ERR_ARGUMENT_UNSUPPORTED, "%s", @@ -17066,6 +17070,62 @@ qemuDomainGetStatsMemoryBandwidth(virQEMUDriver *d= river, } =20 =20 +static void +qemuDomainGetStatsEnergy(virQEMUDriver *driver, + virDomainObj *dom, + virTypedParamList *params) +{ + g_autofree virQEMUResctrlMonData **resdata =3D NULL; + size_t nresdata =3D 0; + size_t i =3D 0; + size_t j =3D 0; + size_t k =3D 0; + + if (!virDomainObjIsActive(dom)) + return; + + if (qemuDomainGetResctrlMonData(driver, dom, &resdata, &nresdata, + VIR_RESCTRL_MONITOR_TYPE_ENERGY) < 0) { + virResetLastError(); + return; + } + + if (nresdata =3D=3D 0) + return; + + virTypedParamListAddUInt(params, nresdata, + VIR_DOMAIN_STATS_CPU_ENERGY_MONITOR_COUNT); + + for (i =3D 0; i < nresdata; i++) { + virTypedParamListAddString(params, resdata[i]->name, + VIR_DOMAIN_STATS_CPU_ENERGY_MONITOR_PRE= FIX "%zu" VIR_DOMAIN_STATS_CPU_ENERGY_MONITOR_SUFFIX_NAME, i); + virTypedParamListAddString(params, resdata[i]->vcpus, + VIR_DOMAIN_STATS_CPU_ENERGY_MONITOR_PRE= FIX "%zu" VIR_DOMAIN_STATS_CPU_ENERGY_MONITOR_SUFFIX_VCPUS, i); + virTypedParamListAddUInt(params, resdata[i]->nstats, + VIR_DOMAIN_STATS_CPU_ENERGY_MONITOR_PREFI= X "%zu" VIR_DOMAIN_STATS_CPU_ENERGY_MONITOR_SUFFIX_PKG_COUNT, i); + + for (j =3D 0; j < resdata[i]->nstats; j++) { + char **features =3D resdata[i]->stats[j]->features; + + virTypedParamListAddUInt(params, resdata[i]->stats[j]->id, + VIR_DOMAIN_STATS_CPU_ENERGY_MONITOR_P= REFIX "%zu" VIR_DOMAIN_STATS_CPU_ENERGY_MONITOR_SUFFIX_PKG_PREFIX "%zu" VIR= _DOMAIN_STATS_CPU_ENERGY_MONITOR_SUFFIX_PKG_SUFFIX_ID, i, j); + + for (k =3D 0; features[k]; k++) { + if (k >=3D resdata[i]->stats[j]->ndvals) + break; + + virTypedParamListAddDouble(params, resdata[i]->stats[j]->d= vals[k], + VIR_DOMAIN_STATS_CPU_ENERGY_MON= ITOR_PREFIX "%zu" VIR_DOMAIN_STATS_CPU_ENERGY_MONITOR_SUFFIX_PKG_PREFIX "%z= u" ".%s", i, j, + features[k]); + } + } + } + + for (i =3D 0; i < nresdata; i++) + qemuDomainFreeResctrlMonData(resdata[i]); +} + + static void qemuDomainGetStatsCpuCache(virQEMUDriver *driver, virDomainObj *dom, @@ -17277,6 +17337,8 @@ qemuDomainGetStatsCpu(virQEMUDriver *driver, =20 qemuDomainGetStatsCpuCache(driver, dom, params); =20 + qemuDomainGetStatsEnergy(driver, dom, params); + qemuDomainGetStatsCpuHaltPollTime(dom, params, privflags); } =20 diff --git a/src/util/virresctrl.c b/src/util/virresctrl.c index 8f33a85a56..32cbfb6c2e 100644 --- a/src/util/virresctrl.c +++ b/src/util/virresctrl.c @@ -79,15 +79,24 @@ VIR_ENUM_IMPL(virResctrl, "DATA", ); =20 -/* Monitor feature name prefix mapping for monitor naming */ +/* Monitor feature prefix/type mapping for monitor naming */ VIR_ENUM_IMPL(virResctrlMonitorPrefix, VIR_RESCTRL_MONITOR_TYPE_LAST, "__unsupported__", "llc_", "mbm_", + "energy", ); =20 =20 +/* PERF_PKG_MON features that report energy data (floating-point). */ +static const char *virResctrlEnergyFeatures[] =3D { + "core_energy", + "activity", + NULL, +}; + + /* All private typedefs so that they exist for all later definitions. Thi= s way * structs can be included in one or another without reorganizing the code= every * time. */ @@ -183,6 +192,8 @@ struct _virResctrlInfo { virResctrlInfoMemBW *membw_info; =20 virResctrlInfoMongrp *monitor_info; + + virResctrlInfoMongrp *perf_monitor_info; }; =20 static void @@ -235,10 +246,14 @@ virResctrlInfoDispose(void *obj) if (resctrl->monitor_info) g_strfreev(resctrl->monitor_info->features); =20 + if (resctrl->perf_monitor_info) + g_strfreev(resctrl->perf_monitor_info->features); + virResctrlInfoMemBWFree(resctrl->membw_info); =20 g_free(resctrl->levels); g_free(resctrl->monitor_info); + g_free(resctrl->perf_monitor_info); } =20 =20 @@ -771,6 +786,52 @@ virResctrlGetMonitorInfo(virResctrlInfo *resctrl) } =20 =20 +static int +virResctrlGetPerfMonitorInfo(virResctrlInfo *resctrl) +{ + int rv =3D -1; + g_autofree char *featurestr =3D NULL; + g_autofree virResctrlInfoMongrp *info_monitor =3D NULL; + + info_monitor =3D g_new0(virResctrlInfoMongrp, 1); + + rv =3D virFileReadValueUint(&info_monitor->max_monitor, + SYSFS_RESCTRL_PATH + "/info/PERF_PKG_MON/num_rmids"); + if (rv =3D=3D -2) { + VIR_INFO("The file '" SYSFS_RESCTRL_PATH "/info/PERF_PKG_MON/num_r= mids' " + "does not exist"); + return 0; + } else if (rv < 0) { + return -1; + } + + rv =3D virFileReadValueString(&featurestr, + SYSFS_RESCTRL_PATH + "/info/PERF_PKG_MON/mon_features"); + if (rv =3D=3D -2) + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Cannot get mon_features from resctrl PERF_PKG_MO= N")); + if (rv < 0) + return -1; + + if (!*featurestr) { + VIR_WARN("Got empty feature list from PERF_PKG_MON; " + "AET energy monitoring will not be available"); + return 0; + } + + info_monitor->features =3D g_strsplit(featurestr, "\n", 0); + info_monitor->nfeatures =3D g_strv_length(info_monitor->features); + VIR_DEBUG("Resctrl supported %zd AET monitoring features", + info_monitor->nfeatures); + + resctrl->perf_monitor_info =3D g_steal_pointer(&info_monitor); + + return 0; +} + + static int virResctrlGetInfo(virResctrlInfo *resctrl) { @@ -790,6 +851,9 @@ virResctrlGetInfo(virResctrlInfo *resctrl) if ((ret =3D virResctrlGetMonitorInfo(resctrl)) < 0) return -1; =20 + if ((ret =3D virResctrlGetPerfMonitorInfo(resctrl)) < 0) + return -1; + return 0; } =20 @@ -830,6 +894,9 @@ virResctrlInfoIsEmpty(virResctrlInfo *resctrl) if (resctrl->monitor_info) return false; =20 + if (resctrl->perf_monitor_info) + return false; + for (i =3D 0; i < resctrl->nlevels; i++) { virResctrlInfoPerLevel *i_level =3D resctrl->levels[i]; =20 @@ -986,13 +1053,6 @@ virResctrlInfoGetMonitorPrefix(virResctrlInfo *resctr= l, if (virResctrlInfoIsEmpty(resctrl)) return 0; =20 - mongrp_info =3D resctrl->monitor_info; - - if (!mongrp_info) { - VIR_INFO("Monitor is not supported in host"); - return 0; - } - for (i =3D 0; i < VIR_RESCTRL_MONITOR_TYPE_LAST; i++) { if (STREQ(prefix, virResctrlMonitorPrefixTypeToString(i))) { mon =3D g_new0(virResctrlInfoMon, 1); @@ -1008,6 +1068,19 @@ virResctrlInfoGetMonitorPrefix(virResctrlInfo *resct= rl, return -1; } =20 + if (mon->type =3D=3D VIR_RESCTRL_MONITOR_TYPE_ENERGY) + mongrp_info =3D resctrl->perf_monitor_info; + else + mongrp_info =3D resctrl->monitor_info; + + if (!mongrp_info) { + VIR_INFO("Monitor prefix '%s' is not supported in host", prefix); + virResctrlInfoMonFree(*monitor); + *monitor =3D NULL; + ret =3D 0; + goto cleanup; + } + mon->max_monitor =3D mongrp_info->max_monitor; =20 if (mon->type =3D=3D VIR_RESCTRL_MONITOR_TYPE_CACHE) { @@ -1018,8 +1091,12 @@ virResctrlInfoGetMonitorPrefix(virResctrlInfo *resct= rl, mon->features =3D g_new0(char *, mongrp_info->nfeatures + 1); =20 for (i =3D 0; i < mongrp_info->nfeatures; i++) { - if (STRPREFIX(mongrp_info->features[i], prefix)) + if (mon->type =3D=3D VIR_RESCTRL_MONITOR_TYPE_ENERGY) { + if (g_strv_contains(virResctrlEnergyFeatures, mongrp_info->fea= tures[i])) + mon->features[mon->nfeatures++] =3D g_strdup(mongrp_info->= features[i]); + } else if (STRPREFIX(mongrp_info->features[i], prefix)) { mon->features[mon->nfeatures++] =3D g_strdup(mongrp_info->feat= ures[i]); + } } =20 mon->features =3D g_renew(char *, mon->features, mon->nfeatures + 1); @@ -2558,7 +2635,7 @@ virResctrlMonitorStatsSorter(const void *a, * memory bandwidth usage data. * @nstats: A size_t pointer to hold the returned array length of @stats * - * Get cache or memory bandwidth utilization information. + * Get cache, memory bandwidth or energy utilization information. * * Returns 0 on success, -1 on error. */ @@ -2593,6 +2670,7 @@ virResctrlMonitorGetStats(virResctrlMonitor *monitor, while (virDirRead(dirp, &ent, datapath) > 0) { g_autofree char *filepath =3D NULL; char *node_id =3D NULL; + bool is_energy =3D false; =20 /* Looking for directory that contains resource utilization * information file. The directory name is arranged in format @@ -2605,18 +2683,17 @@ virResctrlMonitorGetStats(virResctrlMonitor *monito= r, if (!virFileIsDir(filepath)) continue; =20 - /* Looking for directory has a prefix 'mon_L' */ - if (!(node_id =3D STRSKIP(ent->d_name, "mon_L"))) - continue; - - /* Looking for directory has another '_' */ - node_id =3D strchr(node_id, '_'); - if (!node_id) - continue; - - /* Skip the character '_' */ - if (!(node_id =3D STRSKIP(node_id, "_"))) + if ((node_id =3D STRSKIP(ent->d_name, "mon_PERF_PKG_"))) { + is_energy =3D true; + } else if ((node_id =3D STRSKIP(ent->d_name, "mon_L"))) { + node_id =3D strchr(node_id, '_'); + if (!node_id) + continue; + if (!(node_id =3D STRSKIP(node_id, "_"))) + continue; + } else { continue; + } =20 stat =3D g_new0(virResctrlMonitorStats, 1); stat->features =3D g_new0(char *, nresources + 1); @@ -2626,21 +2703,61 @@ virResctrlMonitorGetStats(virResctrlMonitor *monito= r, goto cleanup; =20 for (i =3D 0; resources[i]; i++) { - rv =3D virFileReadValueUllong(&val, "%s/%s/%s", datapath, - ent->d_name, resources[i]); - if (rv =3D=3D -2) { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("File '%1$s/%2$s/%3$s' does not exist."), - datapath, ent->d_name, resources[i]); + if (is_energy) { + g_autofree char *valstr =3D NULL; + double dval =3D 0.0; + char *endp =3D NULL; + + rv =3D virFileReadValueString(&valstr, "%s/%s/%s", datapat= h, + ent->d_name, resources[i]); + if (rv =3D=3D -2) { + if (i =3D=3D 0) + break; + virReportError(VIR_ERR_INTERNAL_ERROR, + _("File '%1$s/%2$s/%3$s' does not exist= ."), + datapath, ent->d_name, resources[i]); + goto cleanup; + } + if (rv < 0) + goto cleanup; + + g_strstrip(valstr); + errno =3D 0; + dval =3D g_ascii_strtod(valstr, &endp); + if (endp =3D=3D valstr || *endp !=3D '\0' || errno !=3D 0)= { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Cannot parse resctrl monitor value '= %1$s' from '%2$s/%3$s/%4$s'"), + valstr, datapath, ent->d_name, resource= s[i]); + goto cleanup; + } + + VIR_APPEND_ELEMENT(stat->dvals, stat->ndvals, dval); + } else { + rv =3D virFileReadValueUllong(&val, "%s/%s/%s", datapath, + ent->d_name, resources[i]); + if (rv =3D=3D -2) { + if (i =3D=3D 0) + break; + virReportError(VIR_ERR_INTERNAL_ERROR, + _("File '%1$s/%2$s/%3$s' does not exist= ."), + datapath, ent->d_name, resources[i]); + goto cleanup; + } + if (rv < 0) + goto cleanup; + + VIR_APPEND_ELEMENT(stat->vals, stat->nvals, val); } - if (rv < 0) - goto cleanup; - - VIR_APPEND_ELEMENT(stat->vals, stat->nvals, val); =20 stat->features[i] =3D g_strdup(resources[i]); } =20 + if (resources[i]) { + virResctrlMonitorStatsFree(stat); + stat =3D NULL; + continue; + } + VIR_APPEND_ELEMENT(*stats, *nstats, stat); } =20 @@ -2665,5 +2782,6 @@ virResctrlMonitorStatsFree(virResctrlMonitorStats *st= at) =20 g_strfreev(stat->features); g_free(stat->vals); + g_free(stat->dvals); g_free(stat); } diff --git a/src/util/virresctrl.h b/src/util/virresctrl.h index c70b112864..0f38db47cf 100644 --- a/src/util/virresctrl.h +++ b/src/util/virresctrl.h @@ -38,6 +38,7 @@ typedef enum { VIR_RESCTRL_MONITOR_TYPE_UNSUPPORT, VIR_RESCTRL_MONITOR_TYPE_CACHE, VIR_RESCTRL_MONITOR_TYPE_MEMBW, + VIR_RESCTRL_MONITOR_TYPE_ENERGY, =20 VIR_RESCTRL_MONITOR_TYPE_LAST } virResctrlMonitorType; @@ -196,11 +197,18 @@ struct _virResctrlMonitorStats { /* @features is a NULL terminal string list tracking the statistical r= ecord * name.*/ char **features; - /* @vals store the statistical record values and @val[0] is the value = for - * @features[0], @val[1] for@features[1] ... respectively */ + /* @vals store the statistical record values for integer-valued resour= ces + * (cache occupancy, memory bandwidth). Entries correspond 1:1 with + * @features; empty when the resource reports floating-point data. */ unsigned long long *vals; /* The length of @vals array */ size_t nvals; + /* @dvals store double-precision values for floating-point resources + * (energy in Joules). Entries correspond 1:1 with + * @features; empty when the resource reports integer data. */ + double *dvals; + /* The length of @dvals array */ + size_t ndvals; }; =20 virResctrlMonitor * diff --git a/tests/genericxml2xmlindata/energytune-basic.xml b/tests/generi= cxml2xmlindata/energytune-basic.xml new file mode 100644 index 0000000000..4ee4bedb68 --- /dev/null +++ b/tests/genericxml2xmlindata/energytune-basic.xml @@ -0,0 +1,32 @@ + + QEMUGuest1 + c7a5fdbd-edaf-9455-926a-d65c16db1809 + 219136 + 219136 + 4 + + + + + + + + + + hvm + + + + destroy + restart + destroy + + /usr/bin/qemu-system-i386 + + + + + + + + diff --git a/tests/genericxml2xmltest.c b/tests/genericxml2xmltest.c index 6be694cac5..bcc9b07a13 100644 --- a/tests/genericxml2xmltest.c +++ b/tests/genericxml2xmltest.c @@ -210,6 +210,7 @@ mymain(void) DO_TEST("cachetune-small"); DO_TEST("cachetune-cdp"); DO_TEST("cachetune"); + DO_TEST("energytune-basic"); DO_TEST_DIFFERENT("cachetune-extra-tunes"); DO_TEST_FAIL_INACTIVE("cachetune-colliding-allocs"); DO_TEST_FAIL_INACTIVE("cachetune-colliding-tunes"); --=20 2.34.1 --------------------------------------------------------------------- Intel Technology Poland sp. z o.o. ul. Slowackiego 173 | 80-298 Gdansk | Sad Rejonowy Gdansk Polnoc | VII Wydz= ial Gospodarczy Krajowego Rejestru Sadowego - KRS 101882 | NIP 957-07-52-31= 6 | Kapital zakladowy 200.000 PLN. Spolka oswiadcza, ze posiada status duzego przedsiebiorcy w rozumieniu usta= wy z dnia 8 marca 2013 r. o przeciwdzialaniu nadmiernym opoznieniom w trans= akcjach handlowych. Ta wiadomosc wraz z zalacznikami jest przeznaczona dla okreslonego adresata= i moze zawierac informacje poufne. W razie przypadkowego otrzymania tej wi= adomosci, prosimy o powiadomienie nadawcy oraz trwale jej usuniecie; jakiek= olwiek przegladanie lub rozpowszechnianie jest zabronione. This e-mail and any attachments may contain confidential material for the s= ole use of the intended recipient(s). If you are not the intended recipient= , please contact the sender and delete all copies; any review or distributi= on by others is strictly prohibited.