"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. */
virResctrlInfoMemBW *membw_info;
virResctrlInfoMongrp *monitor_info;
+
+ virResctrlInfoMongrp *perf_monitor_info;
};
static void
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);
}
}
+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)
{
if ((ret = virResctrlGetMonitorInfo(resctrl)) < 0)
return -1;
+ if ((ret = virResctrlGetPerfMonitorInfo(resctrl)) < 0)
+ return -1;
+
return 0;
}
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];
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);
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) {
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);
* 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.
*/
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
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);
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);
}
g_strfreev(stat->features);
g_free(stat->vals);
+ g_free(stat->dvals);
g_free(stat);
}