[libvirt] [PATCH 12/21] resctrl: Instantiate all resctrl information at once

Martin Kletzander mkletzan at redhat.com
Mon Nov 13 08:50:27 UTC 2017


This allows for looking up the cache control information more sensibly from
conf/capabilities.c and also provides more data to the virresctrl module that
will get more usable later on.

Signed-off-by: Martin Kletzander <mkletzan at redhat.com>
---
 po/POTFILES.in           |   1 +
 src/conf/capabilities.c  |  48 +++----
 src/conf/capabilities.h  |   4 +-
 src/libvirt_private.syms |   4 +-
 src/util/virresctrl.c    | 335 +++++++++++++++++++++++++++++++++--------------
 src/util/virresctrl.h    |  24 ++--
 6 files changed, 274 insertions(+), 142 deletions(-)

diff --git a/po/POTFILES.in b/po/POTFILES.in
index c1fa23427eff..8382ee633621 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -252,6 +252,7 @@ src/util/virportallocator.c
 src/util/virprocess.c
 src/util/virqemu.c
 src/util/virrandom.c
+src/util/virresctrl.c
 src/util/virrotatingfile.c
 src/util/virscsi.c
 src/util/virscsihost.c
diff --git a/src/conf/capabilities.c b/src/conf/capabilities.c
index 5bf8ac2019f9..74d8e7459e6d 100644
--- a/src/conf/capabilities.c
+++ b/src/conf/capabilities.c
@@ -245,6 +245,7 @@ virCapabilitiesDispose(void *object)
     VIR_FREE(caps->host.netprefix);
     VIR_FREE(caps->host.pagesSize);
     virCPUDefFree(caps->host.cpu);
+    virObjectUnref(caps->host.resctrl);
 }
 
 /**
@@ -1588,6 +1589,15 @@ virCapsHostCacheBankSorter(const void *a, const void *b)
     return ca->id - cb->id;
 }
 
+static int
+virCapabilitiesInitResctrl(virCapsPtr caps)
+{
+    if (!caps->host.resctrl)
+        return virResctrlGetInfo(&caps->host.resctrl);
+
+    return 0;
+}
+
 int
 virCapabilitiesInitCaches(virCapsPtr caps)
 {
@@ -1596,7 +1606,6 @@ virCapabilitiesInitCaches(virCapsPtr caps)
     ssize_t pos = -1;
     DIR *dirp = NULL;
     int ret = -1;
-    int typeret;
     char *path = NULL;
     char *type = NULL;
     struct dirent *ent = NULL;
@@ -1607,6 +1616,9 @@ virCapabilitiesInitCaches(virCapsPtr caps)
      * lose information. */
     const int cache_min_level = 3;
 
+    if (virCapabilitiesInitResctrl(caps) < 0)
+        return -1;
+
     /* offline CPUs don't provide cache info */
     if (virFileReadValueBitmap(&cpus, "%s/cpu/online", SYSFS_SYSTEM_PATH) < 0)
         return -1;
@@ -1672,32 +1684,6 @@ virCapabilitiesInitCaches(virCapsPtr caps)
                                        SYSFS_SYSTEM_PATH, pos, ent->d_name) < 0)
                 goto cleanup;
 
-            typeret = virResctrlGetCacheControlType(bank->level);
-            if (typeret < 0)
-                goto cleanup;
-
-            if (typeret == 1) {
-                if (virResctrlGetCacheInfo(bank->level,
-                                           bank->size,
-                                           VIR_CACHE_TYPE_BOTH,
-                                           &bank->controls,
-                                           &bank->ncontrols) < 0)
-                    goto cleanup;
-            } else if (typeret == 2) {
-                if (virResctrlGetCacheInfo(bank->level,
-                                           bank->size,
-                                           VIR_CACHE_TYPE_CODE,
-                                           &bank->controls,
-                                           &bank->ncontrols) < 0)
-                    goto cleanup;
-                if (virResctrlGetCacheInfo(bank->level,
-                                           bank->size,
-                                           VIR_CACHE_TYPE_DATA,
-                                           &bank->controls,
-                                           &bank->ncontrols) < 0)
-                    goto cleanup;
-            }
-
             kernel_type = virCacheKernelTypeFromString(type);
             if (kernel_type < 0) {
                 virReportError(VIR_ERR_INTERNAL_ERROR,
@@ -1713,6 +1699,14 @@ virCapabilitiesInitCaches(virCapsPtr caps)
                     break;
             }
             if (i == caps->host.ncaches) {
+                /* If it is a new cache, then update its resctrl information. */
+                if (virResctrlInfoGetCache(caps->host.resctrl,
+                                           bank->level,
+                                           bank->size,
+                                           &bank->ncontrols,
+                                           &bank->controls) < 0)
+                    goto cleanup;
+
                 if (VIR_APPEND_ELEMENT(caps->host.caches,
                                        caps->host.ncaches,
                                        bank) < 0) {
diff --git a/src/conf/capabilities.h b/src/conf/capabilities.h
index 5048fa819d95..694a3590bf83 100644
--- a/src/conf/capabilities.h
+++ b/src/conf/capabilities.h
@@ -148,7 +148,7 @@ struct _virCapsHostCacheBank {
     virCacheType type;  /* Data, Instruction or Unified */
     virBitmapPtr cpus;  /* All CPUs that share this bank */
     size_t ncontrols;
-    virResctrlInfoPtr *controls;
+    virResctrlInfoPerCachePtr *controls;
 };
 
 typedef struct _virCapsHost virCapsHost;
@@ -170,6 +170,8 @@ struct _virCapsHost {
     size_t nnumaCell_max;
     virCapsHostNUMACellPtr *numaCell;
 
+    virResctrlInfoPtr resctrl;
+
     size_t ncaches;
     virCapsHostCacheBankPtr *caches;
 
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
index 3986cc523e39..b24728ce4a1d 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -2532,8 +2532,8 @@ virRandomInt;
 # util/virresctrl.h
 virCacheTypeFromString;
 virCacheTypeToString;
-virResctrlGetCacheControlType;
-virResctrlGetCacheInfo;
+virResctrlGetInfo;
+virResctrlInfoGetCache;
 
 
 # util/virrotatingfile.h
diff --git a/src/util/virresctrl.c b/src/util/virresctrl.c
index 2a11825a52dc..6c6692e78f42 100644
--- a/src/util/virresctrl.c
+++ b/src/util/virresctrl.c
@@ -18,6 +18,11 @@
 
 #include <config.h>
 
+#include <sys/file.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
 #include "virresctrl.h"
 
 #include "c-ctype.h"
@@ -25,12 +30,16 @@
 #include "viralloc.h"
 #include "virfile.h"
 #include "virlog.h"
+#include "virobject.h"
 #include "virstring.h"
 
+
 #define VIR_FROM_THIS VIR_FROM_RESCTRL
 
 VIR_LOG_INIT("util.virresctrl")
 
+
+/* Common definitions */
 #define SYSFS_RESCTRL_PATH "/sys/fs/resctrl"
 
 /* Resctrl is short for Resource Control.  It might be implemented for various
@@ -55,133 +64,257 @@ VIR_ENUM_IMPL(virResctrl, VIR_CACHE_TYPE_LAST,
               "CODE",
               "DATA")
 
-int
-virResctrlGetCacheInfo(unsigned int level,
-                       unsigned long long size,
-                       virCacheType scope,
-                       virResctrlInfoPtr **controls,
-                       size_t *ncontrols)
-{
-    int ret = -1;
-    char *tmp = NULL;
-    char *path = NULL;
-    char *cbm_mask = NULL;
-    char *type_upper = NULL;
-    unsigned int bits = 0;
-    unsigned int min_cbm_bits = 0;
-    virResctrlInfoPtr control;
-
-    if (VIR_ALLOC(control) < 0)
-        goto cleanup;
 
-    if (scope != VIR_CACHE_TYPE_BOTH &&
-        virStringToUpper(&type_upper, virCacheTypeToString(scope)) < 0)
-        goto cleanup;
+/* Info-related definitions and InfoClass-related functions */
+typedef struct _virResctrlInfoPerType virResctrlInfoPerType;
+typedef virResctrlInfoPerType *virResctrlInfoPerTypePtr;
+struct _virResctrlInfoPerType {
+    /* Kernel-provided information */
+    char *cbm_mask;
+    unsigned int min_cbm_bits;
 
-    if (virFileReadValueUint(&control->max_allocation,
-                             SYSFS_RESCTRL_PATH "/info/L%u%s/num_closids",
-                             level,
-                             type_upper ? type_upper : "") < 0)
-        goto cleanup;
+    /* Our computed information from the above */
+    unsigned int bits;
+    unsigned int max_cache_id;
 
-    if (virFileReadValueString(&cbm_mask,
-                               SYSFS_RESCTRL_PATH
-                               "/info/L%u%s/cbm_mask",
-                               level,
-                               type_upper ? type_upper: "") < 0)
-        goto cleanup;
+    /* In order to be self-sufficient we need size information per cache.
+     * Funnily enough, one of the outcomes of the resctrlfs design is that it
+     * does not account for different sizes per cache on the same level.  So
+     * for the sake of easiness, let's copy that, for now. */
+    unsigned long long size;
 
-    if (virFileReadValueUint(&min_cbm_bits,
-                             SYSFS_RESCTRL_PATH "/info/L%u%s/min_cbm_bits",
-                             level,
-                             type_upper ? type_upper : "") < 0)
-        goto cleanup;
+    /* Information that we will return upon request (this is public struct) as
+     * until now all the above is internal to this module */
+    virResctrlInfoPerCache control;
+};
 
-    virStringTrimOptionalNewline(cbm_mask);
+typedef struct _virResctrlInfoPerLevel virResctrlInfoPerLevel;
+typedef virResctrlInfoPerLevel *virResctrlInfoPerLevelPtr;
+struct _virResctrlInfoPerLevel {
+    virResctrlInfoPerTypePtr *types;
+};
 
-    for (tmp = cbm_mask; *tmp != '\0'; tmp++) {
-        if (c_isxdigit(*tmp))
-            bits += count_one_bits(virHexToBin(*tmp));
-    }
+struct _virResctrlInfo {
+    virObject parent;
 
-    control->granularity = size / bits;
-    if (min_cbm_bits != 1)
-        control->min = min_cbm_bits * control->granularity;
+    virResctrlInfoPerLevelPtr *levels;
+    size_t nlevels;
+};
 
-    control->scope = scope;
+static virClassPtr virResctrlInfoClass;
 
-    if (VIR_APPEND_ELEMENT(*controls, *ncontrols, control) < 0)
-        goto cleanup;
+static void
+virResctrlInfoDispose(void *obj)
+{
+    size_t i = 0;
+    size_t j = 0;
 
-    ret = 0;
+    virResctrlInfoPtr resctrl = obj;
 
- cleanup:
-    VIR_FREE(path);
-    VIR_FREE(cbm_mask);
-    VIR_FREE(type_upper);
-    VIR_FREE(control);
-    return ret;
-}
+    for (i = 0; i < resctrl->nlevels; i++) {
+        virResctrlInfoPerLevelPtr level = resctrl->levels[--resctrl->nlevels];
+
+        if (!level)
+            continue;
 
+        if (level->types) {
+            for (j = 0; j < VIR_CACHE_TYPE_LAST; j++)
+                VIR_FREE(level->types[j]);
+        }
+        VIR_FREE(level->types);
+        VIR_FREE(level);
+    }
+
+    VIR_FREE(resctrl->levels);
+}
 
-static inline int
-virResctrlGetCacheDir(char **path,
-                      const char *prefix,
-                      unsigned int level,
-                      virCacheType type)
+static int virResctrlInfoOnceInit(void)
 {
-    return virAsprintf(path,
-                       SYSFS_RESCTRL_PATH "%s/L%u%s",
-                       prefix ? prefix : "",
-                       level,
-                       virResctrlTypeToString(type));
+    if (!(virResctrlInfoClass = virClassNew(virClassForObject(),
+                                             "virResctrlInfo",
+                                             sizeof(virResctrlInfo),
+                                             virResctrlInfoDispose)))
+        return -1;
+
+    return 0;
 }
 
+VIR_ONCE_GLOBAL_INIT(virResctrlInfo)
 
-/*
- * This function tests whether TYPE of cache control is supported or not.
- *
- * Returns 0 if not, 1 if yes and negative value on error.
- */
-static int
-virResctrlGetCacheSupport(unsigned int level, virCacheType type)
+static virResctrlInfoPtr
+virResctrlInfoNew(void)
 {
-    int ret = -1;
-    char *path = NULL;
-
-    if (virResctrlGetCacheDir(&path, "/info", level, type) < 0)
-        return -1;
+    if (virResctrlInfoInitialize() < 0)
+        return NULL;
 
-    ret = virFileExists(path);
-    VIR_FREE(path);
-    return ret;
+    return virObjectNew(virResctrlInfoClass);
 }
 
 
-/*
- * This function tests which TYPE of cache control is supported
- * Return values are:
- *  -1: error
- *   0: none
- *   1: CAT
- *   2: CDP
- */
+/* Info-related functions */
 int
-virResctrlGetCacheControlType(unsigned int level)
+virResctrlGetInfo(virResctrlInfoPtr *resctrl)
 {
+    DIR *dirp = NULL;
+    char *info_path = NULL;
+    char *endptr = NULL;
+    char *tmp_str = NULL;
+    int ret = 0;
     int rv = -1;
-
-    rv = virResctrlGetCacheSupport(level, VIR_CACHE_TYPE_BOTH);
-    if (rv < 0)
+    int type = 0;
+    struct dirent *ent = NULL;
+    unsigned int level = 0;
+    virResctrlInfoPerLevelPtr i_level = NULL;
+    virResctrlInfoPerTypePtr i_type = NULL;
+    virResctrlInfoPtr tmp_info = virResctrlInfoNew();
+
+    if (!tmp_info)
         return -1;
-    if (rv)
-        return 1;
 
-    rv = virResctrlGetCacheSupport(level, VIR_CACHE_TYPE_CODE);
-    if (rv < 0)
-        return -1;
-    if (rv)
-        return 2;
+    rv = virDirOpenIfExists(&dirp, SYSFS_RESCTRL_PATH "/info");
+    if (rv <= 0) {
+        ret = rv;
+        goto cleanup;
+    }
 
-    return 0;
+    while ((rv = virDirRead(dirp, &ent, SYSFS_RESCTRL_PATH "/info")) > 0) {
+        if (ent->d_type != DT_DIR)
+            continue;
+
+        if (ent->d_name[0] != 'L')
+            continue;
+
+        if (virStrToLong_uip(ent->d_name + 1, &endptr, 10, &level) < 0)
+            goto cleanup;
+
+        type = virResctrlTypeFromString(endptr);
+        if (type < 0)
+            goto cleanup;
+
+        if (VIR_ALLOC(i_type) < 0)
+            goto cleanup;
+
+        i_type->control.scope = type;
+
+        if (virFileReadValueUint(&i_type->control.max_allocation,
+                                 SYSFS_RESCTRL_PATH "/info/%s/num_closids",
+                                 ent->d_name) < 0)
+            goto cleanup;
+
+        if (virFileReadValueString(&i_type->cbm_mask,
+                                   SYSFS_RESCTRL_PATH
+                                   "/info/%s/cbm_mask",
+                                   ent->d_name) < 0)
+            goto cleanup;
+
+        if (virFileReadValueUint(&i_type->min_cbm_bits,
+                                 SYSFS_RESCTRL_PATH "/info/%s/min_cbm_bits",
+                                 ent->d_name) < 0)
+            goto cleanup;
+
+        virStringTrimOptionalNewline(i_type->cbm_mask);
+
+        if (tmp_info->nlevels <= level &&
+            VIR_EXPAND_N(tmp_info->levels, tmp_info->nlevels,
+                         level - tmp_info->nlevels + 1) < 0)
+                goto cleanup;
+
+        if (!tmp_info->levels[level] &&
+            (VIR_ALLOC(tmp_info->levels[level]) < 0 ||
+             VIR_ALLOC_N(tmp_info->levels[level]->types, VIR_CACHE_TYPE_LAST) < 0))
+            goto cleanup;
+        i_level = tmp_info->levels[level];
+
+        if (i_level->types[type]) {
+            virReportError(VIR_ERR_INTERNAL_ERROR,
+                           _("Duplicate cache type in resctrlfs for level %u"),
+                           level);
+            goto cleanup;
+        }
+
+        if (VIR_ALLOC(i_level->types[type]) < 0)
+            goto cleanup;
+
+        for (tmp_str = i_type->cbm_mask; *tmp_str != '\0'; tmp_str++) {
+            if (!c_isxdigit(*tmp_str))
+                goto cleanup;
+
+            i_type->bits += count_one_bits(virHexToBin(*tmp_str));
+        }
+
+        i_level->types[type] = i_type;
+        i_type = NULL;
+    }
+
+    *resctrl = tmp_info;
+    tmp_info = NULL;
+ cleanup:
+    VIR_DIR_CLOSE(dirp);
+    VIR_FREE(info_path);
+    virObjectUnref(tmp_info);
+    VIR_FREE(i_type);
+    return ret;
+}
+
+int
+virResctrlInfoGetCache(virResctrlInfoPtr resctrl,
+                       unsigned int level,
+                       unsigned long long size,
+                       size_t *ncontrols,
+                       virResctrlInfoPerCachePtr **controls)
+{
+    virResctrlInfoPerLevelPtr i_level = NULL;
+    virResctrlInfoPerTypePtr i_type = NULL;
+    size_t i = 0;
+    int ret = -1;
+
+    if (!resctrl)
+        return 0;
+
+    if (level >= resctrl->nlevels)
+        return 0;
+
+    i_level = resctrl->levels[level];
+    if (!i_level)
+        return 0;
+
+    for (i = 0; i < VIR_CACHE_TYPE_LAST; i++) {
+        i_type = i_level->types[i];
+        if (!i_type)
+            continue;
+
+        /* Let's take the opportunity to update our internal information about
+         * the cache size */
+        if (!i_type->size) {
+            i_type->size = size;
+            i_type->control.granularity = size / i_type->bits;
+            if (i_type->min_cbm_bits != 1)
+                i_type->control.min = i_type->min_cbm_bits * i_type->control.granularity;
+        } else {
+            if (i_type->size != size) {
+                virReportError(VIR_ERR_INTERNAL_ERROR,
+                               _("Forbidden inconsistency for resctrlfs, "
+                                 "level %u caches have different sizes"),
+                               level);
+                goto error;
+            }
+            i_type->max_cache_id++;
+        }
+
+        if (VIR_EXPAND_N(*controls, *ncontrols, 1) < 0)
+            goto error;
+        if (VIR_ALLOC((*controls)[*ncontrols - 1]) < 0)
+            goto error;
+
+        memcpy((*controls)[*ncontrols - 1], &i_type->control, sizeof(i_type->control));
+    }
+
+    ret = 0;
+ cleanup:
+    return ret;
+ error:
+    while (*ncontrols)
+        VIR_FREE((*controls)[--*ncontrols]);
+    VIR_FREE(*controls);
+    goto cleanup;
 }
diff --git a/src/util/virresctrl.h b/src/util/virresctrl.h
index 848b13e98aa3..c4df88f23c3a 100644
--- a/src/util/virresctrl.h
+++ b/src/util/virresctrl.h
@@ -36,28 +36,30 @@ typedef enum {
 VIR_ENUM_DECL(virCache);
 
 
-typedef struct _virResctrlInfo virResctrlInfo;
-typedef virResctrlInfo *virResctrlInfoPtr;
-struct _virResctrlInfo {
+typedef struct _virResctrlInfoPerCache virResctrlInfoPerCache;
+typedef virResctrlInfoPerCache *virResctrlInfoPerCachePtr;
+struct _virResctrlInfoPerCache {
     /* Smallest possible increase of the allocation size in bytes */
     unsigned long long granularity;
     /* Minimal allocatable size in bytes (if different from granularity) */
     unsigned long long min;
-    /* Type of the allocation */
-    virCacheType scope;
     /* Maximum number of simultaneous allocations */
     unsigned int max_allocation;
+    /* Type of the allocation */
+    virCacheType scope;
 };
 
+typedef struct _virResctrlInfo virResctrlInfo;
+typedef virResctrlInfo *virResctrlInfoPtr;
 
 int
-virResctrlGetCacheInfo(unsigned int level,
-                       unsigned long long size,
-                       virCacheType scope,
-                       virResctrlInfoPtr **controls,
-                       size_t *ncontrols);
+virResctrlGetInfo(virResctrlInfoPtr *resctrl);
 
 int
-virResctrlGetCacheControlType(unsigned int level);
+virResctrlInfoGetCache(virResctrlInfoPtr resctrl,
+                       unsigned int level,
+                       unsigned long long size,
+                       size_t *ncontrols,
+                       virResctrlInfoPerCachePtr **controls);
 
 #endif /*  __VIR_RESCTRL_H__ */
-- 
2.15.0




More information about the libvir-list mailing list