[libvirt] [PATCH 10/10] qemu: Report cache occupancy (CMT) with domstats

Wang Huaqiang huaqiang.wang at intel.com
Mon Aug 27 11:23:13 UTC 2018


Intel x86 RDT CMT is the technology to tell the last level cache
occupancy information.
Adding the interface in qemu to report this information for resource
monitor group through command 'virsh domstats --cpu-total'.
Below is a typical output:

     # virsh domstats 1 --cpu-total
     Domain: 'ubuntu16.04-base'
       ...
       cpu.cache.monitor.count=2
       cpu.cache.0.name=vcpus_1
       cpu.cache.0.vcpus=1
       cpu.cache.0.bank.count=2
       cpu.cache.0.bank.0.id=0
       cpu.cache.0.bank.0.bytes=4505600
       cpu.cache.0.bank.1.id=1
       cpu.cache.0.bank.1.bytes=5586944
       cpu.cache.1.name=vcpus_4-6
       cpu.cache.1.vcpus=4,5,6
       cpu.cache.1.bank.count=2
       cpu.cache.1.bank.0.id=0
       cpu.cache.1.bank.0.bytes=17571840
       cpu.cache.1.bank.1.id=1
       cpu.cache.1.bank.1.bytes=29106176

Signed-off-by: Wang Huaqiang <huaqiang.wang at intel.com>
---
 src/libvirt-domain.c   |   9 ++
 src/qemu/qemu_driver.c | 265 +++++++++++++++++++++++++++++++++++++++++++++----
 2 files changed, 253 insertions(+), 21 deletions(-)

diff --git a/src/libvirt-domain.c b/src/libvirt-domain.c
index ef46027..a88e94a 100644
--- a/src/libvirt-domain.c
+++ b/src/libvirt-domain.c
@@ -11337,6 +11337,15 @@ virConnectGetDomainCapabilities(virConnectPtr conn,
  *     "cpu.user" - user cpu time spent in nanoseconds as unsigned long long.
  *     "cpu.system" - system cpu time spent in nanoseconds as unsigned long
  *                    long.
+ *     "cpu.cache.monitor.count" - tocal cache monitoring groups
+ *     "cpu.cache.M.name" - name for cache monitoring group 'M'
+ *     "cpu.cache.M.vcpus" - vcpus for cache monitoring group 'M'
+ *     "cpu.cache.M.bank.count" - total bank number for cache monitoring
+ *                                group 'M'
+ *     "cpu.cache.M.bank.N.id" - OS assigned cache bank id for cache 'N' in
+ *                               cache monitoring group 'M'
+ *     "cpu.cache.M.bank.N.bytes" - cache occupancy of cache bank 'N' in
+ *                                  cache monitoring group 'M'
  *
  * VIR_DOMAIN_STATS_BALLOON:
  *     Return memory balloon device information.
diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c
index da8c4e8..193f606 100644
--- a/src/qemu/qemu_driver.c
+++ b/src/qemu/qemu_driver.c
@@ -107,6 +107,7 @@
 #include "virnuma.h"
 #include "dirname.h"
 #include "netdev_bandwidth_conf.h"
+#include "c-ctype.h"
 
 #define VIR_FROM_THIS VIR_FROM_QEMU
 
@@ -19660,6 +19661,225 @@ typedef enum {
 #define HAVE_JOB(flags) ((flags) & QEMU_DOMAIN_STATS_HAVE_JOB)
 
 
+/*
+ * qemuDomainVcpuFormatHelper
+ * For vcpu string, both '1-3' and '1,3' are valid format and
+ * representing different vcpu set, but it is not easy to
+ * differentiate them at first galance, to avoid this case
+ * substituting all '-' with ',', e.g. substitute string '1-3'
+ * with '1,2,3'.
+ */
+static int
+qemuDomainVcpuFormatHelper(char **vcpus)
+{
+    const char *cur = NULL;
+    size_t i = 0;
+    char *tmp = NULL;
+    int start, last;
+    virBuffer buf = VIR_BUFFER_INITIALIZER;
+    bool firstnum = 1;
+
+    if (!*vcpus)
+        goto error;
+
+    cur = *vcpus;
+
+    virSkipSpaces(&cur);
+
+    if (*cur == '\0')
+        goto error;
+
+    while (*cur != 0) {
+        if (!c_isdigit(*cur))
+            goto error;
+
+        if (virStrToLong_i(cur, &tmp, 10, &start) < 0)
+            goto error;
+        if (start < 0)
+            goto error;
+
+        cur = tmp;
+
+        virSkipSpaces(&cur);
+
+        if (*cur == ',' || *cur == 0) {
+            if (!firstnum)
+                virBufferAddChar(&buf, ',');
+            virBufferAsprintf(&buf, "%d", start);
+            firstnum = 0;
+        } else if (*cur == '-') {
+            cur++;
+            virSkipSpaces(&cur);
+
+            if (virStrToLong_i(cur, &tmp, 10, &last) < 0)
+
+                goto error;
+            if (last < start)
+                goto error;
+            cur = tmp;
+
+            for (i = start; i <= last; i++) {
+                if (!firstnum)
+
+                    virBufferAddChar(&buf, ',');
+                virBufferAsprintf(&buf, "%ld", i);
+                firstnum = 0;
+            }
+
+            virSkipSpaces(&cur);
+        }
+
+        if (*cur == ',') {
+            cur++;
+            virSkipSpaces(&cur);
+        } else if (*cur == 0) {
+            break;
+        } else {
+            goto error;
+        }
+    }
+    VIR_FREE(*vcpus);
+    *vcpus = virBufferContentAndReset(&buf);
+    return 0;
+ error:
+    virBufferFreeAndReset(&buf);
+    return -1;
+}
+
+static int
+qemuDomainGetStatsCpuResource(virQEMUDriverPtr driver ATTRIBUTE_UNUSED,
+                              virDomainObjPtr dom,
+                              virDomainStatsRecordPtr record,
+                              int *maxparams,
+                              unsigned int privflags ATTRIBUTE_UNUSED)
+{
+    size_t i = 0;
+    size_t j = 0;
+    char param_name[VIR_TYPED_PARAM_FIELD_LENGTH];
+    virDomainResctrlDefPtr resctrl = NULL;
+    virDomainResctrlMonitorPtr monitor = NULL;
+    unsigned int nvals = 0;
+    unsigned int *ids = NULL;
+    unsigned int *vals = NULL;
+    unsigned int nmonitor = NULL;
+    char *vcpustr = NULL;
+    int ret = -1;
+
+    for (i = 0; i < dom->def->nresctrls; i++) {
+        resctrl = dom->def->resctrls[i];
+
+        for (j = 0; j < resctrl->nmonitors; j++) {
+            monitor = resctrl->monitors[j];
+            if (monitor->vcpus)
+                nmonitor++;
+        }
+    }
+
+    snprintf(param_name, VIR_TYPED_PARAM_FIELD_LENGTH,
+             "cpu.cache.monitor.count");
+    if (virTypedParamsAddUInt(&record->params,
+                              &record->nparams,
+                              maxparams,
+                              param_name,
+                              nmonitor) < 0)
+        goto cleanup;
+
+    for (i = 0; i < dom->def->nresctrls; i++) {
+        resctrl = dom->def->resctrls[i];
+
+        for (j = 0; j < resctrl->nmonitors; j++) {
+            size_t l = 0;
+
+            monitor = resctrl->monitors[j];
+
+            if (!(vcpustr = virBitmapFormat(monitor->vcpus)))
+                goto cleanup;
+
+            if (qemuDomainVcpuFormatHelper(&vcpustr) < 0)
+                goto cleanup;
+
+            switch ((virDomainResctrlMonType) monitor->type) {
+            case VIR_DOMAIN_RESCTRL_MONITOR_CACHE:
+            case VIR_DOMAIN_RESCTRL_MONITOR_CACHE_MEMBW:
+                if (!monitor->vcpus)
+                    continue;
+
+                if (virResctrlAllocGetCacheOccupancy(resctrl->alloc,
+                                                     monitor->id, &nvals,
+                                                     &ids, &vals) < 0)
+                    goto cleanup;
+
+                snprintf(param_name, VIR_TYPED_PARAM_FIELD_LENGTH,
+                         "cpu.cache.%ld.name", i);
+                if (virTypedParamsAddString(&record->params,
+                                            &record->nparams,
+                                            maxparams,
+                                            param_name,
+                                            monitor->id) < 0)
+                    goto cleanup;
+
+                snprintf(param_name, VIR_TYPED_PARAM_FIELD_LENGTH,
+                         "cpu.cache.%ld.vcpus", i);
+
+                if (virTypedParamsAddString(&record->params,
+                                            &record->nparams,
+                                            maxparams,
+                                            param_name,
+                                            vcpustr) < 0)
+                    goto cleanup;
+
+                snprintf(param_name, VIR_TYPED_PARAM_FIELD_LENGTH,
+                         "cpu.cache.%ld.bank.count", i);
+                if (virTypedParamsAddUInt(&record->params,
+                                          &record->nparams,
+                                          maxparams,
+                                          param_name,
+                                          nvals) < 0)
+                    goto cleanup;
+
+                for (l = 0; l < nvals; l++) {
+                    snprintf(param_name, VIR_TYPED_PARAM_FIELD_LENGTH,
+                             "cpu.cache.%ld.bank.%ld.id", i, l);
+                    if (virTypedParamsAddUInt(&record->params,
+                                              &record->nparams,
+                                              maxparams,
+                                              param_name,
+                                              ids[l]) < 0)
+                        goto cleanup;
+
+
+                    snprintf(param_name, VIR_TYPED_PARAM_FIELD_LENGTH,
+                             "cpu.cache.%ld.bank.%ld.bytes", i, l);
+                    if (virTypedParamsAddUInt(&record->params,
+                                              &record->nparams,
+                                              maxparams,
+                                              param_name,
+                                              vals[l]) < 0)
+                        goto cleanup;
+                }
+                break;
+
+            case VIR_DOMAIN_RESCTRL_MONITOR_MEMBW:
+            case VIR_DOMAIN_RESCTRL_MONITOR_LAST:
+            default:
+                break;
+            }
+
+            VIR_FREE(ids);
+            VIR_FREE(vals);
+            VIR_FREE(vcpustr);
+            nvals = 0;
+        }
+    }
+
+    ret = 0;
+ cleanup:
+    VIR_FREE(ids);
+    VIR_FREE(vals);
+    VIR_FREE(vcpustr);
+    return ret;
+}
+
 static int
 qemuDomainGetStatsCpu(virQEMUDriverPtr driver ATTRIBUTE_UNUSED,
                       virDomainObjPtr dom,
@@ -19673,29 +19893,32 @@ qemuDomainGetStatsCpu(virQEMUDriverPtr driver ATTRIBUTE_UNUSED,
     unsigned long long sys_time = 0;
     int err = 0;
 
-    if (!priv->cgroup)
-        return 0;
+    if (priv->cgroup) {
+        err = virCgroupGetCpuacctUsage(priv->cgroup, &cpu_time);
+        if (!err && virTypedParamsAddULLong(&record->params,
+                    &record->nparams,
+                    maxparams,
+                    "cpu.time",
+                    cpu_time) < 0)
+            return -1;
 
-    err = virCgroupGetCpuacctUsage(priv->cgroup, &cpu_time);
-    if (!err && virTypedParamsAddULLong(&record->params,
-                                        &record->nparams,
-                                        maxparams,
-                                        "cpu.time",
-                                        cpu_time) < 0)
-        return -1;
+        err = virCgroupGetCpuacctStat(priv->cgroup, &user_time, &sys_time);
+        if (!err && virTypedParamsAddULLong(&record->params,
+                    &record->nparams,
+                    maxparams,
+                    "cpu.user",
+                    user_time) < 0)
+            return -1;
+        if (!err && virTypedParamsAddULLong(&record->params,
+                    &record->nparams,
+                    maxparams,
+                    "cpu.system",
+                    sys_time) < 0)
+            return -1;
+    }
 
-    err = virCgroupGetCpuacctStat(priv->cgroup, &user_time, &sys_time);
-    if (!err && virTypedParamsAddULLong(&record->params,
-                                        &record->nparams,
-                                        maxparams,
-                                        "cpu.user",
-                                        user_time) < 0)
-        return -1;
-    if (!err && virTypedParamsAddULLong(&record->params,
-                                        &record->nparams,
-                                        maxparams,
-                                        "cpu.system",
-                                        sys_time) < 0)
+    if (qemuDomainGetStatsCpuResource(driver, dom,
+                                      record, maxparams, privflags) < 0)
         return -1;
 
     return 0;
-- 
2.7.4




More information about the libvir-list mailing list