c1c534
From 6c23daa9238671767dec5c43387be996a066196f Mon Sep 17 00:00:00 2001
c1c534
Message-Id: <6c23daa9238671767dec5c43387be996a066196f@dist-git>
c1c534
From: Martin Kletzander <mkletzan@redhat.com>
c1c534
Date: Wed, 31 Jan 2018 16:32:25 +0100
c1c534
Subject: [PATCH] util: Add virResctrlInfo
c1c534
c1c534
https://bugzilla.redhat.com/show_bug.cgi?id=1289368
c1c534
c1c534
This will make the current functions obsolete and it will provide more
c1c534
information to the virresctrl module so that it can be used later.
c1c534
c1c534
Signed-off-by: Martin Kletzander <mkletzan@redhat.com>
c1c534
(cherry picked from commit cd572df89bc43405a970ad207b32f23c73bdaa75)
c1c534
Signed-off-by: Martin Kletzander <mkletzan@redhat.com>
c1c534
---
c1c534
 po/POTFILES.in           |   1 +
c1c534
 src/libvirt_private.syms |   3 +
c1c534
 src/util/virresctrl.c    | 305 +++++++++++++++++++++++++++++++++++++++++++++++
c1c534
 src/util/virresctrl.h    |  16 +++
c1c534
 4 files changed, 325 insertions(+)
c1c534
c1c534
diff --git a/po/POTFILES.in b/po/POTFILES.in
c1c534
index c1fa23427e..8382ee6336 100644
c1c534
--- a/po/POTFILES.in
c1c534
+++ b/po/POTFILES.in
c1c534
@@ -252,6 +252,7 @@ src/util/virportallocator.c
c1c534
 src/util/virprocess.c
c1c534
 src/util/virqemu.c
c1c534
 src/util/virrandom.c
c1c534
+src/util/virresctrl.c
c1c534
 src/util/virrotatingfile.c
c1c534
 src/util/virscsi.c
c1c534
 src/util/virscsihost.c
c1c534
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
c1c534
index 7dd4621b70..e0e0046522 100644
c1c534
--- a/src/libvirt_private.syms
c1c534
+++ b/src/libvirt_private.syms
c1c534
@@ -2549,6 +2549,9 @@ virCacheTypeFromString;
c1c534
 virCacheTypeToString;
c1c534
 virResctrlGetCacheControlType;
c1c534
 virResctrlGetCacheInfo;
c1c534
+virResctrlGetInfo;
c1c534
+virResctrlInfoGetCache;
c1c534
+virResctrlInfoNew;
c1c534
 
c1c534
 
c1c534
 # util/virrotatingfile.h
c1c534
diff --git a/src/util/virresctrl.c b/src/util/virresctrl.c
c1c534
index 050a08178e..7ab68c6f74 100644
c1c534
--- a/src/util/virresctrl.c
c1c534
+++ b/src/util/virresctrl.c
c1c534
@@ -25,12 +25,15 @@
c1c534
 #include "viralloc.h"
c1c534
 #include "virfile.h"
c1c534
 #include "virlog.h"
c1c534
+#include "virobject.h"
c1c534
 #include "virstring.h"
c1c534
 
c1c534
 #define VIR_FROM_THIS VIR_FROM_RESCTRL
c1c534
 
c1c534
 VIR_LOG_INIT("util.virresctrl")
c1c534
 
c1c534
+
c1c534
+/* Common definitions */
c1c534
 #define SYSFS_RESCTRL_PATH "/sys/fs/resctrl"
c1c534
 
c1c534
 /* Resctrl is short for Resource Control.  It might be implemented for various
c1c534
@@ -55,6 +58,308 @@ VIR_ENUM_IMPL(virResctrl, VIR_CACHE_TYPE_LAST,
c1c534
               "CODE",
c1c534
               "DATA")
c1c534
 
c1c534
+
c1c534
+/* Info-related definitions and InfoClass-related functions */
c1c534
+typedef struct _virResctrlInfoPerType virResctrlInfoPerType;
c1c534
+typedef virResctrlInfoPerType *virResctrlInfoPerTypePtr;
c1c534
+struct _virResctrlInfoPerType {
c1c534
+    /* Kernel-provided information */
c1c534
+    char *cbm_mask;
c1c534
+    unsigned int min_cbm_bits;
c1c534
+
c1c534
+    /* Our computed information from the above */
c1c534
+    unsigned int bits;
c1c534
+    unsigned int max_cache_id;
c1c534
+
c1c534
+    /* In order to be self-sufficient we need size information per cache.
c1c534
+     * Funnily enough, one of the outcomes of the resctrlfs design is that it
c1c534
+     * does not account for different sizes per cache on the same level.  So
c1c534
+     * for the sake of easiness, let's copy that, for now. */
c1c534
+    unsigned long long size;
c1c534
+
c1c534
+    /* Information that we will return upon request (this is public struct) as
c1c534
+     * until now all the above is internal to this module */
c1c534
+    virResctrlInfoPerCache control;
c1c534
+};
c1c534
+
c1c534
+typedef struct _virResctrlInfoPerLevel virResctrlInfoPerLevel;
c1c534
+typedef virResctrlInfoPerLevel *virResctrlInfoPerLevelPtr;
c1c534
+struct _virResctrlInfoPerLevel {
c1c534
+    virResctrlInfoPerTypePtr *types;
c1c534
+};
c1c534
+
c1c534
+struct _virResctrlInfo {
c1c534
+    virObject parent;
c1c534
+
c1c534
+    virResctrlInfoPerLevelPtr *levels;
c1c534
+    size_t nlevels;
c1c534
+};
c1c534
+
c1c534
+static virClassPtr virResctrlInfoClass;
c1c534
+
c1c534
+static void
c1c534
+virResctrlInfoDispose(void *obj)
c1c534
+{
c1c534
+    size_t i = 0;
c1c534
+    size_t j = 0;
c1c534
+
c1c534
+    virResctrlInfoPtr resctrl = obj;
c1c534
+
c1c534
+    for (i = 0; i < resctrl->nlevels; i++) {
c1c534
+        virResctrlInfoPerLevelPtr level = resctrl->levels[i];
c1c534
+
c1c534
+        if (!level)
c1c534
+            continue;
c1c534
+
c1c534
+        if (level->types) {
c1c534
+            for (j = 0; j < VIR_CACHE_TYPE_LAST; j++) {
c1c534
+                if (level->types[j])
c1c534
+                    VIR_FREE(level->types[j]->cbm_mask);
c1c534
+                VIR_FREE(level->types[j]);
c1c534
+            }
c1c534
+        }
c1c534
+        VIR_FREE(level->types);
c1c534
+        VIR_FREE(level);
c1c534
+    }
c1c534
+
c1c534
+    VIR_FREE(resctrl->levels);
c1c534
+}
c1c534
+
c1c534
+
c1c534
+static int
c1c534
+virResctrlInfoOnceInit(void)
c1c534
+{
c1c534
+    if (!(virResctrlInfoClass = virClassNew(virClassForObject(),
c1c534
+                                            "virResctrlInfo",
c1c534
+                                            sizeof(virResctrlInfo),
c1c534
+                                            virResctrlInfoDispose)))
c1c534
+        return -1;
c1c534
+
c1c534
+    return 0;
c1c534
+}
c1c534
+
c1c534
+
c1c534
+VIR_ONCE_GLOBAL_INIT(virResctrlInfo)
c1c534
+
c1c534
+
c1c534
+virResctrlInfoPtr
c1c534
+virResctrlInfoNew(void)
c1c534
+{
c1c534
+    if (virResctrlInfoInitialize() < 0)
c1c534
+        return NULL;
c1c534
+
c1c534
+    return virObjectNew(virResctrlInfoClass);
c1c534
+}
c1c534
+
c1c534
+
c1c534
+/* Info-related functions */
c1c534
+static bool
c1c534
+virResctrlInfoIsEmpty(virResctrlInfoPtr resctrl)
c1c534
+{
c1c534
+    size_t i = 0;
c1c534
+    size_t j = 0;
c1c534
+
c1c534
+    if (!resctrl)
c1c534
+        return true;
c1c534
+
c1c534
+    for (i = 0; i < resctrl->nlevels; i++) {
c1c534
+        virResctrlInfoPerLevelPtr i_level = resctrl->levels[i];
c1c534
+
c1c534
+        if (!i_level)
c1c534
+            continue;
c1c534
+
c1c534
+        for (j = 0; j < VIR_CACHE_TYPE_LAST; j++) {
c1c534
+            if (i_level->types[j])
c1c534
+                return false;
c1c534
+        }
c1c534
+    }
c1c534
+
c1c534
+    return true;
c1c534
+}
c1c534
+
c1c534
+
c1c534
+int
c1c534
+virResctrlGetInfo(virResctrlInfoPtr resctrl)
c1c534
+{
c1c534
+    DIR *dirp = NULL;
c1c534
+    char *info_path = NULL;
c1c534
+    char *endptr = NULL;
c1c534
+    char *tmp_str = NULL;
c1c534
+    int ret = -1;
c1c534
+    int rv = -1;
c1c534
+    int type = 0;
c1c534
+    struct dirent *ent = NULL;
c1c534
+    unsigned int level = 0;
c1c534
+    virResctrlInfoPerLevelPtr i_level = NULL;
c1c534
+    virResctrlInfoPerTypePtr i_type = NULL;
c1c534
+
c1c534
+    rv = virDirOpenIfExists(&dirp, SYSFS_RESCTRL_PATH "/info");
c1c534
+    if (rv <= 0) {
c1c534
+        ret = rv;
c1c534
+        goto cleanup;
c1c534
+    }
c1c534
+
c1c534
+    while ((rv = virDirRead(dirp, &ent, SYSFS_RESCTRL_PATH "/info")) > 0) {
c1c534
+        if (ent->d_type != DT_DIR)
c1c534
+            continue;
c1c534
+
c1c534
+        if (ent->d_name[0] != 'L')
c1c534
+            continue;
c1c534
+
c1c534
+        if (virStrToLong_uip(ent->d_name + 1, &endptr, 10, &level) < 0) {
c1c534
+            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
c1c534
+                           _("Cannot parse resctrl cache info level"));
c1c534
+            goto cleanup;
c1c534
+        }
c1c534
+
c1c534
+        type = virResctrlTypeFromString(endptr);
c1c534
+        if (type < 0) {
c1c534
+            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
c1c534
+                           _("Cannot parse resctrl cache info type"));
c1c534
+            goto cleanup;
c1c534
+        }
c1c534
+
c1c534
+        if (VIR_ALLOC(i_type) < 0)
c1c534
+            goto cleanup;
c1c534
+
c1c534
+        i_type->control.scope = type;
c1c534
+
c1c534
+        rv = virFileReadValueUint(&i_type->control.max_allocation,
c1c534
+                                  SYSFS_RESCTRL_PATH "/info/%s/num_closids",
c1c534
+                                  ent->d_name);
c1c534
+        if (rv == -2)
c1c534
+            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
c1c534
+                           _("Cannot get num_closids from resctrl cache info"));
c1c534
+        if (rv < 0)
c1c534
+            goto cleanup;
c1c534
+
c1c534
+        rv = virFileReadValueString(&i_type->cbm_mask,
c1c534
+                                    SYSFS_RESCTRL_PATH
c1c534
+                                    "/info/%s/cbm_mask",
c1c534
+                                    ent->d_name);
c1c534
+        if (rv == -2)
c1c534
+            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
c1c534
+                           _("Cannot get cbm_mask from resctrl cache info"));
c1c534
+        if (rv < 0)
c1c534
+            goto cleanup;
c1c534
+
c1c534
+        virStringTrimOptionalNewline(i_type->cbm_mask);
c1c534
+
c1c534
+        rv = virFileReadValueUint(&i_type->min_cbm_bits,
c1c534
+                                  SYSFS_RESCTRL_PATH "/info/%s/min_cbm_bits",
c1c534
+                                  ent->d_name);
c1c534
+        if (rv == -2)
c1c534
+            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
c1c534
+                           _("Cannot get min_cbm_bits from resctrl cache info"));
c1c534
+        if (rv < 0)
c1c534
+            goto cleanup;
c1c534
+
c1c534
+        if (resctrl->nlevels <= level &&
c1c534
+            VIR_EXPAND_N(resctrl->levels, resctrl->nlevels,
c1c534
+                         level - resctrl->nlevels + 1) < 0)
c1c534
+            goto cleanup;
c1c534
+
c1c534
+        if (!resctrl->levels[level] &&
c1c534
+            (VIR_ALLOC(resctrl->levels[level]) < 0 ||
c1c534
+             VIR_ALLOC_N(resctrl->levels[level]->types, VIR_CACHE_TYPE_LAST) < 0))
c1c534
+            goto cleanup;
c1c534
+        i_level = resctrl->levels[level];
c1c534
+
c1c534
+        if (i_level->types[type]) {
c1c534
+            virReportError(VIR_ERR_INTERNAL_ERROR,
c1c534
+                           _("Duplicate cache type in resctrlfs for level %u"),
c1c534
+                           level);
c1c534
+            goto cleanup;
c1c534
+        }
c1c534
+
c1c534
+        for (tmp_str = i_type->cbm_mask; *tmp_str != '\0'; tmp_str++) {
c1c534
+            if (!c_isxdigit(*tmp_str)) {
c1c534
+                virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
c1c534
+                               _("Cannot parse cbm_mask from resctrl cache info"));
c1c534
+                goto cleanup;
c1c534
+            }
c1c534
+
c1c534
+            i_type->bits += count_one_bits(virHexToBin(*tmp_str));
c1c534
+        }
c1c534
+
c1c534
+        VIR_STEAL_PTR(i_level->types[type], i_type);
c1c534
+    }
c1c534
+
c1c534
+    ret = 0;
c1c534
+ cleanup:
c1c534
+    VIR_DIR_CLOSE(dirp);
c1c534
+    VIR_FREE(info_path);
c1c534
+    if (i_type)
c1c534
+        VIR_FREE(i_type->cbm_mask);
c1c534
+    VIR_FREE(i_type);
c1c534
+    return ret;
c1c534
+}
c1c534
+
c1c534
+
c1c534
+int
c1c534
+virResctrlInfoGetCache(virResctrlInfoPtr resctrl,
c1c534
+                       unsigned int level,
c1c534
+                       unsigned long long size,
c1c534
+                       size_t *ncontrols,
c1c534
+                       virResctrlInfoPerCachePtr **controls)
c1c534
+{
c1c534
+    virResctrlInfoPerLevelPtr i_level = NULL;
c1c534
+    virResctrlInfoPerTypePtr i_type = NULL;
c1c534
+    size_t i = 0;
c1c534
+    int ret = -1;
c1c534
+
c1c534
+    if (virResctrlInfoIsEmpty(resctrl))
c1c534
+        return 0;
c1c534
+
c1c534
+    if (level >= resctrl->nlevels)
c1c534
+        return 0;
c1c534
+
c1c534
+    i_level = resctrl->levels[level];
c1c534
+    if (!i_level)
c1c534
+        return 0;
c1c534
+
c1c534
+    for (i = 0; i < VIR_CACHE_TYPE_LAST; i++) {
c1c534
+        i_type = i_level->types[i];
c1c534
+        if (!i_type)
c1c534
+            continue;
c1c534
+
c1c534
+        /* Let's take the opportunity to update our internal information about
c1c534
+         * the cache size */
c1c534
+        if (!i_type->size) {
c1c534
+            i_type->size = size;
c1c534
+            i_type->control.granularity = size / i_type->bits;
c1c534
+            if (i_type->min_cbm_bits != 1)
c1c534
+                i_type->control.min = i_type->min_cbm_bits * i_type->control.granularity;
c1c534
+        } else {
c1c534
+            if (i_type->size != size) {
c1c534
+                virReportError(VIR_ERR_INTERNAL_ERROR,
c1c534
+                               _("level %u cache size %llu does not match "
c1c534
+                                 "expected size %llu"),
c1c534
+                                 level, i_type->size, size);
c1c534
+                goto error;
c1c534
+            }
c1c534
+            i_type->max_cache_id++;
c1c534
+        }
c1c534
+
c1c534
+        if (VIR_EXPAND_N(*controls, *ncontrols, 1) < 0)
c1c534
+            goto error;
c1c534
+        if (VIR_ALLOC((*controls)[*ncontrols - 1]) < 0)
c1c534
+            goto error;
c1c534
+
c1c534
+        memcpy((*controls)[*ncontrols - 1], &i_type->control, sizeof(i_type->control));
c1c534
+    }
c1c534
+
c1c534
+    ret = 0;
c1c534
+ cleanup:
c1c534
+    return ret;
c1c534
+ error:
c1c534
+    while (*ncontrols)
c1c534
+        VIR_FREE((*controls)[--*ncontrols]);
c1c534
+    VIR_FREE(*controls);
c1c534
+    goto cleanup;
c1c534
+}
c1c534
+
c1c534
+
c1c534
 int
c1c534
 virResctrlGetCacheInfo(unsigned int level,
c1c534
                        unsigned long long size,
c1c534
diff --git a/src/util/virresctrl.h b/src/util/virresctrl.h
c1c534
index 42e8527803..c7f25c76e1 100644
c1c534
--- a/src/util/virresctrl.h
c1c534
+++ b/src/util/virresctrl.h
c1c534
@@ -49,7 +49,23 @@ struct _virResctrlInfoPerCache {
c1c534
     unsigned int max_allocation;
c1c534
 };
c1c534
 
c1c534
+typedef struct _virResctrlInfo virResctrlInfo;
c1c534
+typedef virResctrlInfo *virResctrlInfoPtr;
c1c534
 
c1c534
+virResctrlInfoPtr
c1c534
+virResctrlInfoNew(void);
c1c534
+
c1c534
+int
c1c534
+virResctrlGetInfo(virResctrlInfoPtr resctrl);
c1c534
+
c1c534
+int
c1c534
+virResctrlInfoGetCache(virResctrlInfoPtr resctrl,
c1c534
+                       unsigned int level,
c1c534
+                       unsigned long long size,
c1c534
+                       size_t *ncontrols,
c1c534
+                       virResctrlInfoPerCachePtr **controls);
c1c534
+
c1c534
+/* To be removed */
c1c534
 int
c1c534
 virResctrlGetCacheInfo(unsigned int level,
c1c534
                        unsigned long long size,
c1c534
-- 
c1c534
2.16.1
c1c534