]> git.ipfire.org Git - thirdparty/libvirt.git/commitdiff
util: add PERF_PKG_MON energy monitoring support in virresctrl
authorJedrzej Wasiukiewicz <jedrzej.wasiukiewicz@intel.com>
Thu, 14 May 2026 14:41:02 +0000 (16:41 +0200)
committerMichal Privoznik <mprivozn@redhat.com>
Fri, 15 May 2026 12:02:10 +0000 (14:02 +0200)
Linux 7.0 introduced in resctrl PERF_PKG_MON interface that exposes per-package
energy and performance counters. This patch extends virresctrl implementation
to discover and read energy counters from this new resource type.
(core_energy - Joules, activity - Farads)

Changes:
 - Add Energy features allow-list virResctrlEnergyFeatures
   since PERF_PKG_MON is not prefix-based.
 - Added perf_monitor_info to _virResctrlInfo to contain _virResctrlInfo capabilities
 - New virResctrlGetPerfMonitorInfo following earlier virResctrlGetMonitorInfo
   to check new resource capabilities
 - Added VIR_RESCTRL_MONITOR_TYPE_ENERGY and mapped it to energy allow-list
 - Added dvals/ndvals pair to _virResctrlMonitorStats to support floating-point
   counters and integer counters in single monitor (to support integer perf counters
   in the future).
 - Added floating-point read + parse in virResctrlMonitorGetStats for energy counters
 - Stubbed VIR_RESCTRL_MONITOR_TYPE_ENERGY in qemu_driver

Signed-off-by: Jedrzej Wasiukiewicz <jedrzej.wasiukiewicz@intel.com>
Signed-off-by: Christopher M. Cantalupo <christopher.m.cantalupo@intel.com>
Signed-off-by: Michal Privoznik <mprivozn@redhat.com>
Reviewed-by: Michal Privoznik <mprivozn@redhat.com>
src/qemu/qemu_driver.c
src/util/virresctrl.c
src/util/virresctrl.h

index 264799a8645c76eaa94c0d9089af4b47626e553e..2d509cd2b91689830882798d4789f840f7f47743 100644 (file)
@@ -17085,6 +17085,7 @@ qemuDomainGetResctrlMonData(virQEMUDriver *driver,
         if (caps->host.memBW.monitor)
             features = caps->host.memBW.monitor->features;
         break;
+    case VIR_RESCTRL_MONITOR_TYPE_ENERGY:
     case VIR_RESCTRL_MONITOR_TYPE_UNSUPPORT:
     case VIR_RESCTRL_MONITOR_TYPE_LAST:
         virReportError(VIR_ERR_ARGUMENT_UNSUPPORTED, "%s",
index 8f33a85a5639f351c5550ca523462ab9fa4b5706..66df44fb58f3f1360679ac2ba1a20f43059e4235 100644 (file)
@@ -79,15 +79,24 @@ VIR_ENUM_IMPL(virResctrl,
               "DATA",
 );
 
-/* 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",
 );
 
 
+/* PERF_PKG_MON features that report energy data (floating-point) */
+static const char *virResctrlEnergyFeatures[] = {
+    "core_energy",
+    "activity",
+    NULL,
+};
+
+
 /* All private typedefs so that they exist for all later definitions.  This way
  * structs can be included in one or another without reorganizing the code every
  * time. */
@@ -183,6 +192,8 @@ struct _virResctrlInfo {
     virResctrlInfoMemBW *membw_info;
 
     virResctrlInfoMongrp *monitor_info;
+
+    virResctrlInfoMongrp *perf_monitor_info;
 };
 
 static void
@@ -235,10 +246,14 @@ virResctrlInfoDispose(void *obj)
     if (resctrl->monitor_info)
         g_strfreev(resctrl->monitor_info->features);
 
+    if (resctrl->perf_monitor_info)
+        g_strfreev(resctrl->perf_monitor_info->features);
+
     virResctrlInfoMemBWFree(resctrl->membw_info);
 
     g_free(resctrl->levels);
     g_free(resctrl->monitor_info);
+    g_free(resctrl->perf_monitor_info);
 }
 
 
@@ -771,6 +786,52 @@ virResctrlGetMonitorInfo(virResctrlInfo *resctrl)
 }
 
 
+static int
+virResctrlGetPerfMonitorInfo(virResctrlInfo *resctrl)
+{
+    int rv = -1;
+    g_autofree char *featurestr = NULL;
+    g_autofree virResctrlInfoMongrp *info_monitor = NULL;
+
+    info_monitor = g_new0(virResctrlInfoMongrp, 1);
+
+    rv = virFileReadValueUint(&info_monitor->max_monitor,
+                              SYSFS_RESCTRL_PATH
+                              "/info/PERF_PKG_MON/num_rmids");
+    if (rv == -2) {
+        VIR_DEBUG("The file '" SYSFS_RESCTRL_PATH "/info/PERF_PKG_MON/num_rmids' "
+                  "does not exist");
+        return 0;
+    } else if (rv < 0) {
+        return -1;
+    }
+
+    rv = virFileReadValueString(&featurestr,
+                                SYSFS_RESCTRL_PATH
+                                "/info/PERF_PKG_MON/mon_features");
+    if (rv == -2)
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                       _("Cannot get mon_features from resctrl PERF_PKG_MON"));
+    if (rv < 0)
+        return -1;
+
+    if (!*featurestr) {
+        VIR_DEBUG("Got empty feature list from PERF_PKG_MON; "
+                  "energy monitoring will not be available");
+        return 0;
+    }
+
+    info_monitor->features = g_strsplit(featurestr, "\n", 0);
+    info_monitor->nfeatures = g_strv_length(info_monitor->features);
+    VIR_DEBUG("Resctrl supported %zd PERF_PKG_MON monitoring features",
+              info_monitor->nfeatures);
+
+    resctrl->perf_monitor_info = g_steal_pointer(&info_monitor);
+
+    return 0;
+}
+
+
 static int
 virResctrlGetInfo(virResctrlInfo *resctrl)
 {
@@ -790,6 +851,9 @@ virResctrlGetInfo(virResctrlInfo *resctrl)
     if ((ret = virResctrlGetMonitorInfo(resctrl)) < 0)
         return -1;
 
+    if ((ret = virResctrlGetPerfMonitorInfo(resctrl)) < 0)
+        return -1;
+
     return 0;
 }
 
@@ -830,6 +894,9 @@ virResctrlInfoIsEmpty(virResctrlInfo *resctrl)
     if (resctrl->monitor_info)
         return false;
 
+    if (resctrl->perf_monitor_info)
+        return false;
+
     for (i = 0; i < resctrl->nlevels; i++) {
         virResctrlInfoPerLevel *i_level = resctrl->levels[i];
 
@@ -986,13 +1053,6 @@ virResctrlInfoGetMonitorPrefix(virResctrlInfo *resctrl,
     if (virResctrlInfoIsEmpty(resctrl))
         return 0;
 
-    mongrp_info = resctrl->monitor_info;
-
-    if (!mongrp_info) {
-        VIR_INFO("Monitor is not supported in host");
-        return 0;
-    }
-
     for (i = 0; i < VIR_RESCTRL_MONITOR_TYPE_LAST; i++) {
         if (STREQ(prefix, virResctrlMonitorPrefixTypeToString(i))) {
             mon = g_new0(virResctrlInfoMon, 1);
@@ -1008,6 +1068,19 @@ virResctrlInfoGetMonitorPrefix(virResctrlInfo *resctrl,
         return -1;
     }
 
+    if (mon->type == VIR_RESCTRL_MONITOR_TYPE_ENERGY)
+        mongrp_info = resctrl->perf_monitor_info;
+    else
+        mongrp_info = resctrl->monitor_info;
+
+    if (!mongrp_info) {
+        VIR_DEBUG("Monitor prefix '%s' is not supported in host", prefix);
+        virResctrlInfoMonFree(*monitor);
+        *monitor = NULL;
+        ret = 0;
+        goto cleanup;
+    }
+
     mon->max_monitor = mongrp_info->max_monitor;
 
     if (mon->type == VIR_RESCTRL_MONITOR_TYPE_CACHE) {
@@ -1018,8 +1091,12 @@ virResctrlInfoGetMonitorPrefix(virResctrlInfo *resctrl,
     mon->features = g_new0(char *, mongrp_info->nfeatures + 1);
 
     for (i = 0; i < mongrp_info->nfeatures; i++) {
-        if (STRPREFIX(mongrp_info->features[i], prefix))
+        if (mon->type == VIR_RESCTRL_MONITOR_TYPE_ENERGY) {
+            if (g_strv_contains(virResctrlEnergyFeatures, mongrp_info->features[i]))
+                mon->features[mon->nfeatures++] = g_strdup(mongrp_info->features[i]);
+        } else if (STRPREFIX(mongrp_info->features[i], prefix)) {
             mon->features[mon->nfeatures++] = g_strdup(mongrp_info->features[i]);
+        }
     }
 
     mon->features = 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 = NULL;
         char *node_id = NULL;
+        bool is_energy = false;
 
         /* Looking for directory that contains resource utilization
          * information file. The directory name is arranged in format
@@ -2605,18 +2683,17 @@ virResctrlMonitorGetStats(virResctrlMonitor *monitor,
         if (!virFileIsDir(filepath))
             continue;
 
-        /* Looking for directory has a prefix 'mon_L' */
-        if (!(node_id = STRSKIP(ent->d_name, "mon_L")))
-            continue;
-
-        /* Looking for directory has another '_' */
-        node_id = strchr(node_id, '_');
-        if (!node_id)
-            continue;
-
-        /* Skip the character '_' */
-        if (!(node_id = STRSKIP(node_id, "_")))
+        if ((node_id = STRSKIP(ent->d_name, "mon_PERF_PKG_"))) {
+            is_energy = true;
+        } else if ((node_id = STRSKIP(ent->d_name, "mon_L"))) {
+            node_id = strchr(node_id, '_');
+            if (!node_id)
+                continue;
+            if (!(node_id = STRSKIP(node_id, "_")))
+                continue;
+        } else {
             continue;
+        }
 
         stat = g_new0(virResctrlMonitorStats, 1);
         stat->features = g_new0(char *, nresources + 1);
@@ -2626,21 +2703,61 @@ virResctrlMonitorGetStats(virResctrlMonitor *monitor,
             goto cleanup;
 
         for (i = 0; resources[i]; i++) {
-            rv = virFileReadValueUllong(&val, "%s/%s/%s", datapath,
-                                        ent->d_name, resources[i]);
-            if (rv == -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 = NULL;
+                double dval = 0.0;
+                char *endp = NULL;
+
+                rv = virFileReadValueString(&valstr, "%s/%s/%s", datapath,
+                                            ent->d_name, resources[i]);
+                if (rv == -2) {
+                    if (i == 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 = 0;
+                dval = g_ascii_strtod(valstr, &endp);
+                if (endp == valstr || *endp != '\0' || errno != 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, resources[i]);
+                    goto cleanup;
+                }
+
+                VIR_APPEND_ELEMENT(stat->dvals, stat->ndvals, dval);
+            } else {
+                rv = virFileReadValueUllong(&val, "%s/%s/%s", datapath,
+                                            ent->d_name, resources[i]);
+                if (rv == -2) {
+                    if (i == 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);
 
             stat->features[i] = g_strdup(resources[i]);
         }
 
+        if (resources[i]) {
+            virResctrlMonitorStatsFree(stat);
+            stat = NULL;
+            continue;
+        }
+
         VIR_APPEND_ELEMENT(*stats, *nstats, stat);
     }
 
@@ -2665,5 +2782,6 @@ virResctrlMonitorStatsFree(virResctrlMonitorStats *stat)
 
     g_strfreev(stat->features);
     g_free(stat->vals);
+    g_free(stat->dvals);
     g_free(stat);
 }
index c70b112864ebad8e9cb7211e5a2323c968061558..2818b6e8cc278fd031806e7110cbcf0951b9fcc3 100644 (file)
@@ -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,
 
     VIR_RESCTRL_MONITOR_TYPE_LAST
 } virResctrlMonitorType;
@@ -196,11 +197,18 @@ struct _virResctrlMonitorStats {
     /* @features is a NULL terminal string list tracking the statistical record
      * 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 resources.
+     * 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.
+     * Entries correspond 1:1 with @features; empty when the resource reports
+     * integer data. */
+    double *dvals;
+    /* The length of @dvals array */
+    size_t ndvals;
 };
 
 virResctrlMonitor *