|
|
c1c534 |
From 5fb3ec71e0e67f1f1c0157c9c9038c5136820799 Mon Sep 17 00:00:00 2001
|
|
|
c1c534 |
Message-Id: <5fb3ec71e0e67f1f1c0157c9c9038c5136820799@dist-git>
|
|
|
c1c534 |
From: Martin Kletzander <mkletzan@redhat.com>
|
|
|
c1c534 |
Date: Wed, 31 Jan 2018 16:32:29 +0100
|
|
|
c1c534 |
Subject: [PATCH] resctrl: Add functions to work with resctrl allocations
|
|
|
c1c534 |
|
|
|
c1c534 |
With this commit we finally have a way to read and manipulate basic resctrl
|
|
|
c1c534 |
settings. Locking is done only on exposed functions that read/write from/to
|
|
|
c1c534 |
resctrlfs. Not in functions that are exposed in virresctrlpriv.h as those are
|
|
|
c1c534 |
only supposed to be used from tests.
|
|
|
c1c534 |
|
|
|
c1c534 |
More information about how resctrl works:
|
|
|
c1c534 |
|
|
|
c1c534 |
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/Documentation/x86/intel_rdt_ui.txt
|
|
|
c1c534 |
|
|
|
c1c534 |
Signed-off-by: Martin Kletzander <mkletzan@redhat.com>
|
|
|
c1c534 |
(cherry picked from commit a64c761c275c61517360cf1f5af5461fc5b93138)
|
|
|
c1c534 |
Signed-off-by: Martin Kletzander <mkletzan@redhat.com>
|
|
|
c1c534 |
|
|
|
c1c534 |
https://bugzilla.redhat.com/show_bug.cgi?id=1289368
|
|
|
c1c534 |
|
|
|
c1c534 |
Conflicts:
|
|
|
c1c534 |
|
|
|
c1c534 |
- whitespace due to 3e7db8d3e8538bcd5deb1b111fb1233fc18831aa
|
|
|
c1c534 |
|
|
|
c1c534 |
Signed-off-by: Martin Kletzander <mkletzan@redhat.com>
|
|
|
c1c534 |
---
|
|
|
c1c534 |
src/Makefile.am | 2 +-
|
|
|
c1c534 |
src/libvirt_private.syms | 10 +
|
|
|
c1c534 |
src/util/virresctrl.c | 1174 ++++++++++++++++++++++++++++++++++++++++++++-
|
|
|
c1c534 |
src/util/virresctrl.h | 49 ++
|
|
|
c1c534 |
src/util/virresctrlpriv.h | 27 ++
|
|
|
c1c534 |
5 files changed, 1260 insertions(+), 2 deletions(-)
|
|
|
c1c534 |
create mode 100644 src/util/virresctrlpriv.h
|
|
|
c1c534 |
|
|
|
c1c534 |
diff --git a/src/Makefile.am b/src/Makefile.am
|
|
|
c1c534 |
index b74856ba78..6b24a47afe 100644
|
|
|
c1c534 |
--- a/src/Makefile.am
|
|
|
c1c534 |
+++ b/src/Makefile.am
|
|
|
c1c534 |
@@ -167,7 +167,7 @@ UTIL_SOURCES = \
|
|
|
c1c534 |
util/virprocess.c util/virprocess.h \
|
|
|
c1c534 |
util/virqemu.c util/virqemu.h \
|
|
|
c1c534 |
util/virrandom.h util/virrandom.c \
|
|
|
c1c534 |
- util/virresctrl.h util/virresctrl.c \
|
|
|
c1c534 |
+ util/virresctrl.h util/virresctrl.c util/virresctrlpriv.h \
|
|
|
c1c534 |
util/virrotatingfile.h util/virrotatingfile.c \
|
|
|
c1c534 |
util/virscsi.c util/virscsi.h \
|
|
|
c1c534 |
util/virscsihost.c util/virscsihost.h \
|
|
|
c1c534 |
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
|
|
|
c1c534 |
index 02bad6c0ce..ee75e09767 100644
|
|
|
c1c534 |
--- a/src/libvirt_private.syms
|
|
|
c1c534 |
+++ b/src/libvirt_private.syms
|
|
|
c1c534 |
@@ -2547,6 +2547,16 @@ virRandomInt;
|
|
|
c1c534 |
# util/virresctrl.h
|
|
|
c1c534 |
virCacheTypeFromString;
|
|
|
c1c534 |
virCacheTypeToString;
|
|
|
c1c534 |
+virResctrlAllocAddPID;
|
|
|
c1c534 |
+virResctrlAllocCreate;
|
|
|
c1c534 |
+virResctrlAllocForeachSize;
|
|
|
c1c534 |
+virResctrlAllocFormat;
|
|
|
c1c534 |
+virResctrlAllocGetID;
|
|
|
c1c534 |
+virResctrlAllocGetUnused;
|
|
|
c1c534 |
+virResctrlAllocNew;
|
|
|
c1c534 |
+virResctrlAllocRemove;
|
|
|
c1c534 |
+virResctrlAllocSetID;
|
|
|
c1c534 |
+virResctrlAllocSetSize;
|
|
|
c1c534 |
virResctrlGetInfo;
|
|
|
c1c534 |
virResctrlInfoGetCache;
|
|
|
c1c534 |
virResctrlInfoNew;
|
|
|
c1c534 |
diff --git a/src/util/virresctrl.c b/src/util/virresctrl.c
|
|
|
c1c534 |
index d439e7792c..e252aefe31 100644
|
|
|
c1c534 |
--- a/src/util/virresctrl.c
|
|
|
c1c534 |
+++ b/src/util/virresctrl.c
|
|
|
c1c534 |
@@ -18,8 +18,12 @@
|
|
|
c1c534 |
|
|
|
c1c534 |
#include <config.h>
|
|
|
c1c534 |
|
|
|
c1c534 |
-#include "virresctrl.h"
|
|
|
c1c534 |
+#include <sys/file.h>
|
|
|
c1c534 |
+#include <sys/types.h>
|
|
|
c1c534 |
+#include <sys/stat.h>
|
|
|
c1c534 |
+#include <fcntl.h>
|
|
|
c1c534 |
|
|
|
c1c534 |
+#include "virresctrlpriv.h"
|
|
|
c1c534 |
#include "c-ctype.h"
|
|
|
c1c534 |
#include "count-one-bits.h"
|
|
|
c1c534 |
#include "viralloc.h"
|
|
|
c1c534 |
@@ -152,6 +156,193 @@ virResctrlInfoNew(void)
|
|
|
c1c534 |
}
|
|
|
c1c534 |
|
|
|
c1c534 |
|
|
|
c1c534 |
+/* Alloc-related definitions and AllocClass-related functions */
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+/*
|
|
|
c1c534 |
+ * virResctrlAlloc represents one allocation (in XML under cputune/cachetune and
|
|
|
c1c534 |
+ * consequently a directory under /sys/fs/resctrl). Since it can have multiple
|
|
|
c1c534 |
+ * parts of multiple caches allocated it is represented as bunch of nested
|
|
|
c1c534 |
+ * sparse arrays (by sparse I mean array of pointers so that each might be NULL
|
|
|
c1c534 |
+ * in case there is no allocation for that particular one (level, cache, ...)).
|
|
|
c1c534 |
+ *
|
|
|
c1c534 |
+ * Since one allocation can be made for caches on different levels, the first
|
|
|
c1c534 |
+ * nested sparse array is of types virResctrlAllocPerLevel. For example if you
|
|
|
c1c534 |
+ * have allocation for level 3 cache, there will be three NULL pointers and then
|
|
|
c1c534 |
+ * allocated pointer to virResctrlAllocPerLevel. That way you can access it by
|
|
|
c1c534 |
+ * `alloc[level]` as O(1) is desired instead of crawling through normal arrays
|
|
|
c1c534 |
+ * or lists in three nested loops. The code uses a lot of direct accesses.
|
|
|
c1c534 |
+ *
|
|
|
c1c534 |
+ * Each virResctrlAllocPerLevel can have allocations for different cache
|
|
|
c1c534 |
+ * allocation types. You can allocate instruction cache (VIR_CACHE_TYPE_CODE),
|
|
|
c1c534 |
+ * data cache (VIR_CACHE_TYPE_DATA) or unified cache (VIR_CACHE_TYPE_BOTH).
|
|
|
c1c534 |
+ * Those allocations are kept in sparse array of virResctrlAllocPerType pointers.
|
|
|
c1c534 |
+ *
|
|
|
c1c534 |
+ * For each virResctrlAllocPerType users can request some size of the cache to
|
|
|
c1c534 |
+ * be allocated. That's what the sparse array `sizes` is for. Non-NULL
|
|
|
c1c534 |
+ * pointers represent requested size allocations. The array is indexed by host
|
|
|
c1c534 |
+ * cache id (gotten from `/sys/devices/system/cpu/cpuX/cache/indexY/id`). Users
|
|
|
c1c534 |
+ * can see this information e.g. in the output of `virsh capabilities` (for that
|
|
|
c1c534 |
+ * information there's the other struct, namely `virResctrlInfo`).
|
|
|
c1c534 |
+ *
|
|
|
c1c534 |
+ * When allocation is being created we need to find unused part of the cache for
|
|
|
c1c534 |
+ * all of them. While doing that we store the bitmask in a sparse array of
|
|
|
c1c534 |
+ * virBitmaps named `masks` indexed the same way as `sizes`. The upper bounds
|
|
|
c1c534 |
+ * of the sparse arrays are stored in nmasks or nsizes, respectively.
|
|
|
c1c534 |
+ */
|
|
|
c1c534 |
+typedef struct _virResctrlAllocPerType virResctrlAllocPerType;
|
|
|
c1c534 |
+typedef virResctrlAllocPerType *virResctrlAllocPerTypePtr;
|
|
|
c1c534 |
+struct _virResctrlAllocPerType {
|
|
|
c1c534 |
+ /* There could be bool saying whether this is set or not, but since everything
|
|
|
c1c534 |
+ * in virResctrlAlloc (and most of libvirt) goes with pointer arrays we would
|
|
|
c1c534 |
+ * have to have one more level of allocation anyway, so this stays faithful to
|
|
|
c1c534 |
+ * the concept */
|
|
|
c1c534 |
+ unsigned long long **sizes;
|
|
|
c1c534 |
+ size_t nsizes;
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ /* Mask for each cache */
|
|
|
c1c534 |
+ virBitmapPtr *masks;
|
|
|
c1c534 |
+ size_t nmasks;
|
|
|
c1c534 |
+};
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+typedef struct _virResctrlAllocPerLevel virResctrlAllocPerLevel;
|
|
|
c1c534 |
+typedef virResctrlAllocPerLevel *virResctrlAllocPerLevelPtr;
|
|
|
c1c534 |
+struct _virResctrlAllocPerLevel {
|
|
|
c1c534 |
+ virResctrlAllocPerTypePtr *types; /* Indexed with enum virCacheType */
|
|
|
c1c534 |
+ /* There is no `ntypes` member variable as it is always allocated for
|
|
|
c1c534 |
+ * VIR_CACHE_TYPE_LAST number of items */
|
|
|
c1c534 |
+};
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+struct _virResctrlAlloc {
|
|
|
c1c534 |
+ virObject parent;
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ virResctrlAllocPerLevelPtr *levels;
|
|
|
c1c534 |
+ size_t nlevels;
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ /* The identifier (any unique string for now) */
|
|
|
c1c534 |
+ char *id;
|
|
|
c1c534 |
+ /* libvirt-generated path in /sys/fs/resctrl for this particular
|
|
|
c1c534 |
+ * allocation */
|
|
|
c1c534 |
+ char *path;
|
|
|
c1c534 |
+};
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+static virClassPtr virResctrlAllocClass;
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+static void
|
|
|
c1c534 |
+virResctrlAllocDispose(void *obj)
|
|
|
c1c534 |
+{
|
|
|
c1c534 |
+ size_t i = 0;
|
|
|
c1c534 |
+ size_t j = 0;
|
|
|
c1c534 |
+ size_t k = 0;
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ virResctrlAllocPtr resctrl = obj;
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ for (i = 0; i < resctrl->nlevels; i++) {
|
|
|
c1c534 |
+ virResctrlAllocPerLevelPtr level = resctrl->levels[i];
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ if (!level)
|
|
|
c1c534 |
+ continue;
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ for (j = 0; j < VIR_CACHE_TYPE_LAST; j++) {
|
|
|
c1c534 |
+ virResctrlAllocPerTypePtr type = level->types[j];
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ if (!type)
|
|
|
c1c534 |
+ continue;
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ for (k = 0; k < type->nsizes; k++)
|
|
|
c1c534 |
+ VIR_FREE(type->sizes[k]);
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ for (k = 0; k < type->nmasks; k++)
|
|
|
c1c534 |
+ virBitmapFree(type->masks[k]);
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ VIR_FREE(type->sizes);
|
|
|
c1c534 |
+ VIR_FREE(type->masks);
|
|
|
c1c534 |
+ VIR_FREE(type);
|
|
|
c1c534 |
+ }
|
|
|
c1c534 |
+ VIR_FREE(level->types);
|
|
|
c1c534 |
+ VIR_FREE(level);
|
|
|
c1c534 |
+ }
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ VIR_FREE(resctrl->id);
|
|
|
c1c534 |
+ VIR_FREE(resctrl->path);
|
|
|
c1c534 |
+ VIR_FREE(resctrl->levels);
|
|
|
c1c534 |
+}
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+static int
|
|
|
c1c534 |
+virResctrlAllocOnceInit(void)
|
|
|
c1c534 |
+{
|
|
|
c1c534 |
+ if (!(virResctrlAllocClass = virClassNew(virClassForObject(),
|
|
|
c1c534 |
+ "virResctrlAlloc",
|
|
|
c1c534 |
+ sizeof(virResctrlAlloc),
|
|
|
c1c534 |
+ virResctrlAllocDispose)))
|
|
|
c1c534 |
+ return -1;
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ return 0;
|
|
|
c1c534 |
+}
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+VIR_ONCE_GLOBAL_INIT(virResctrlAlloc)
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+virResctrlAllocPtr
|
|
|
c1c534 |
+virResctrlAllocNew(void)
|
|
|
c1c534 |
+{
|
|
|
c1c534 |
+ if (virResctrlAllocInitialize() < 0)
|
|
|
c1c534 |
+ return NULL;
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ return virObjectNew(virResctrlAllocClass);
|
|
|
c1c534 |
+}
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+/* Common functions */
|
|
|
c1c534 |
+static int
|
|
|
c1c534 |
+virResctrlLockInternal(int op)
|
|
|
c1c534 |
+{
|
|
|
c1c534 |
+ int fd = open(SYSFS_RESCTRL_PATH, O_DIRECTORY | O_CLOEXEC);
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ if (fd < 0) {
|
|
|
c1c534 |
+ virReportSystemError(errno, "%s", _("Cannot open resctrlfs"));
|
|
|
c1c534 |
+ return -1;
|
|
|
c1c534 |
+ }
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ if (flock(fd, op) < 0) {
|
|
|
c1c534 |
+ virReportSystemError(errno, "%s", _("Cannot lock resctrlfs"));
|
|
|
c1c534 |
+ VIR_FORCE_CLOSE(fd);
|
|
|
c1c534 |
+ return -1;
|
|
|
c1c534 |
+ }
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ return fd;
|
|
|
c1c534 |
+}
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+static inline int
|
|
|
c1c534 |
+virResctrlLockWrite(void)
|
|
|
c1c534 |
+{
|
|
|
c1c534 |
+ return virResctrlLockInternal(LOCK_EX);
|
|
|
c1c534 |
+}
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+static int
|
|
|
c1c534 |
+virResctrlUnlock(int fd)
|
|
|
c1c534 |
+{
|
|
|
c1c534 |
+ if (fd == -1)
|
|
|
c1c534 |
+ return 0;
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ /* The lock gets unlocked by closing the fd, which we need to do anyway in
|
|
|
c1c534 |
+ * order to clean up properly */
|
|
|
c1c534 |
+ if (VIR_CLOSE(fd) < 0) {
|
|
|
c1c534 |
+ virReportSystemError(errno, "%s", _("Cannot close resctrlfs"));
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ /* Trying to save the already broken */
|
|
|
c1c534 |
+ if (flock(fd, LOCK_UN) < 0)
|
|
|
c1c534 |
+ virReportSystemError(errno, "%s", _("Cannot unlock resctrlfs"));
|
|
|
c1c534 |
+ return -1;
|
|
|
c1c534 |
+ }
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ return 0;
|
|
|
c1c534 |
+}
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+
|
|
|
c1c534 |
/* Info-related functions */
|
|
|
c1c534 |
static bool
|
|
|
c1c534 |
virResctrlInfoIsEmpty(virResctrlInfoPtr resctrl)
|
|
|
c1c534 |
@@ -367,3 +558,984 @@ virResctrlInfoGetCache(virResctrlInfoPtr resctrl,
|
|
|
c1c534 |
VIR_FREE(*controls);
|
|
|
c1c534 |
goto cleanup;
|
|
|
c1c534 |
}
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+/* Alloc-related functions */
|
|
|
c1c534 |
+bool
|
|
|
c1c534 |
+virResctrlAllocIsEmpty(virResctrlAllocPtr resctrl)
|
|
|
c1c534 |
+{
|
|
|
c1c534 |
+ size_t i = 0;
|
|
|
c1c534 |
+ size_t j = 0;
|
|
|
c1c534 |
+ size_t k = 0;
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ if (!resctrl)
|
|
|
c1c534 |
+ return true;
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ for (i = 0; i < resctrl->nlevels; i++) {
|
|
|
c1c534 |
+ virResctrlAllocPerLevelPtr a_level = resctrl->levels[i];
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ if (!a_level)
|
|
|
c1c534 |
+ continue;
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ for (j = 0; j < VIR_CACHE_TYPE_LAST; j++) {
|
|
|
c1c534 |
+ virResctrlAllocPerTypePtr a_type = a_level->types[j];
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ if (!a_type)
|
|
|
c1c534 |
+ continue;
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ for (k = 0; k < a_type->nsizes; k++) {
|
|
|
c1c534 |
+ if (a_type->sizes[k])
|
|
|
c1c534 |
+ return false;
|
|
|
c1c534 |
+ }
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ for (k = 0; k < a_type->nmasks; k++) {
|
|
|
c1c534 |
+ if (a_type->masks[k])
|
|
|
c1c534 |
+ return false;
|
|
|
c1c534 |
+ }
|
|
|
c1c534 |
+ }
|
|
|
c1c534 |
+ }
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ return true;
|
|
|
c1c534 |
+}
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+static virResctrlAllocPerTypePtr
|
|
|
c1c534 |
+virResctrlAllocGetType(virResctrlAllocPtr resctrl,
|
|
|
c1c534 |
+ unsigned int level,
|
|
|
c1c534 |
+ virCacheType type)
|
|
|
c1c534 |
+{
|
|
|
c1c534 |
+ virResctrlAllocPerLevelPtr a_level = NULL;
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ if (resctrl->nlevels <= level &&
|
|
|
c1c534 |
+ VIR_EXPAND_N(resctrl->levels, resctrl->nlevels, level - resctrl->nlevels + 1) < 0)
|
|
|
c1c534 |
+ return NULL;
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ if (!resctrl->levels[level]) {
|
|
|
c1c534 |
+ virResctrlAllocPerTypePtr *types = NULL;
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ if (VIR_ALLOC_N(types, VIR_CACHE_TYPE_LAST) < 0)
|
|
|
c1c534 |
+ return NULL;
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ if (VIR_ALLOC(resctrl->levels[level]) < 0) {
|
|
|
c1c534 |
+ VIR_FREE(types);
|
|
|
c1c534 |
+ return NULL;
|
|
|
c1c534 |
+ }
|
|
|
c1c534 |
+ resctrl->levels[level]->types = types;
|
|
|
c1c534 |
+ }
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ a_level = resctrl->levels[level];
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ if (!a_level->types[type] && VIR_ALLOC(a_level->types[type]) < 0)
|
|
|
c1c534 |
+ return NULL;
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ return a_level->types[type];
|
|
|
c1c534 |
+}
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+static int
|
|
|
c1c534 |
+virResctrlAllocUpdateMask(virResctrlAllocPtr resctrl,
|
|
|
c1c534 |
+ unsigned int level,
|
|
|
c1c534 |
+ virCacheType type,
|
|
|
c1c534 |
+ unsigned int cache,
|
|
|
c1c534 |
+ virBitmapPtr mask)
|
|
|
c1c534 |
+{
|
|
|
c1c534 |
+ virResctrlAllocPerTypePtr a_type = virResctrlAllocGetType(resctrl, level, type);
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ if (!a_type)
|
|
|
c1c534 |
+ return -1;
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ if (a_type->nmasks <= cache &&
|
|
|
c1c534 |
+ VIR_EXPAND_N(a_type->masks, a_type->nmasks,
|
|
|
c1c534 |
+ cache - a_type->nmasks + 1) < 0)
|
|
|
c1c534 |
+ return -1;
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ if (!a_type->masks[cache]) {
|
|
|
c1c534 |
+ a_type->masks[cache] = virBitmapNew(virBitmapSize(mask));
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ if (!a_type->masks[cache])
|
|
|
c1c534 |
+ return -1;
|
|
|
c1c534 |
+ }
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ return virBitmapCopy(a_type->masks[cache], mask);
|
|
|
c1c534 |
+}
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+static int
|
|
|
c1c534 |
+virResctrlAllocUpdateSize(virResctrlAllocPtr resctrl,
|
|
|
c1c534 |
+ unsigned int level,
|
|
|
c1c534 |
+ virCacheType type,
|
|
|
c1c534 |
+ unsigned int cache,
|
|
|
c1c534 |
+ unsigned long long size)
|
|
|
c1c534 |
+{
|
|
|
c1c534 |
+ virResctrlAllocPerTypePtr a_type = virResctrlAllocGetType(resctrl, level, type);
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ if (!a_type)
|
|
|
c1c534 |
+ return -1;
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ if (a_type->nsizes <= cache &&
|
|
|
c1c534 |
+ VIR_EXPAND_N(a_type->sizes, a_type->nsizes,
|
|
|
c1c534 |
+ cache - a_type->nsizes + 1) < 0)
|
|
|
c1c534 |
+ return -1;
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ if (!a_type->sizes[cache] && VIR_ALLOC(a_type->sizes[cache]) < 0)
|
|
|
c1c534 |
+ return -1;
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ *(a_type->sizes[cache]) = size;
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ return 0;
|
|
|
c1c534 |
+}
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+/*
|
|
|
c1c534 |
+ * Check if there is an allocation for this level/type/cache already. Called
|
|
|
c1c534 |
+ * before updating the structure. VIR_CACHE_TYPE_BOTH collides with any type,
|
|
|
c1c534 |
+ * the other types collide with itself. This code basically checks if either:
|
|
|
c1c534 |
+ * `alloc[level]->types[type]->sizes[cache]`
|
|
|
c1c534 |
+ * or
|
|
|
c1c534 |
+ * `alloc[level]->types[VIR_CACHE_TYPE_BOTH]->sizes[cache]`
|
|
|
c1c534 |
+ * is non-NULL. All the fuzz around it is checking for NULL pointers along
|
|
|
c1c534 |
+ * the way.
|
|
|
c1c534 |
+ */
|
|
|
c1c534 |
+static bool
|
|
|
c1c534 |
+virResctrlAllocCheckCollision(virResctrlAllocPtr alloc,
|
|
|
c1c534 |
+ unsigned int level,
|
|
|
c1c534 |
+ virCacheType type,
|
|
|
c1c534 |
+ unsigned int cache)
|
|
|
c1c534 |
+{
|
|
|
c1c534 |
+ virResctrlAllocPerLevelPtr a_level = NULL;
|
|
|
c1c534 |
+ virResctrlAllocPerTypePtr a_type = NULL;
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ if (!alloc)
|
|
|
c1c534 |
+ return false;
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ if (alloc->nlevels <= level)
|
|
|
c1c534 |
+ return false;
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ a_level = alloc->levels[level];
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ if (!a_level)
|
|
|
c1c534 |
+ return false;
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ a_type = a_level->types[VIR_CACHE_TYPE_BOTH];
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ /* If there is an allocation for type 'both', there can be no other
|
|
|
c1c534 |
+ * allocation for the same cache */
|
|
|
c1c534 |
+ if (a_type && a_type->nsizes > cache && a_type->sizes[cache])
|
|
|
c1c534 |
+ return true;
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ if (type == VIR_CACHE_TYPE_BOTH) {
|
|
|
c1c534 |
+ a_type = a_level->types[VIR_CACHE_TYPE_CODE];
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ if (a_type && a_type->nsizes > cache && a_type->sizes[cache])
|
|
|
c1c534 |
+ return true;
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ a_type = a_level->types[VIR_CACHE_TYPE_DATA];
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ if (a_type && a_type->nsizes > cache && a_type->sizes[cache])
|
|
|
c1c534 |
+ return true;
|
|
|
c1c534 |
+ } else {
|
|
|
c1c534 |
+ a_type = a_level->types[type];
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ if (a_type && a_type->nsizes > cache && a_type->sizes[cache])
|
|
|
c1c534 |
+ return true;
|
|
|
c1c534 |
+ }
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ return false;
|
|
|
c1c534 |
+}
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+int
|
|
|
c1c534 |
+virResctrlAllocSetSize(virResctrlAllocPtr resctrl,
|
|
|
c1c534 |
+ unsigned int level,
|
|
|
c1c534 |
+ virCacheType type,
|
|
|
c1c534 |
+ unsigned int cache,
|
|
|
c1c534 |
+ unsigned long long size)
|
|
|
c1c534 |
+{
|
|
|
c1c534 |
+ if (virResctrlAllocCheckCollision(resctrl, level, type, cache)) {
|
|
|
c1c534 |
+ virReportError(VIR_ERR_XML_ERROR,
|
|
|
c1c534 |
+ _("Colliding cache allocations for cache "
|
|
|
c1c534 |
+ "level '%u' id '%u', type '%s'"),
|
|
|
c1c534 |
+ level, cache, virCacheTypeToString(type));
|
|
|
c1c534 |
+ return -1;
|
|
|
c1c534 |
+ }
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ return virResctrlAllocUpdateSize(resctrl, level, type, cache, size);
|
|
|
c1c534 |
+}
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+int
|
|
|
c1c534 |
+virResctrlAllocForeachSize(virResctrlAllocPtr resctrl,
|
|
|
c1c534 |
+ virResctrlAllocForeachSizeCallback cb,
|
|
|
c1c534 |
+ void *opaque)
|
|
|
c1c534 |
+{
|
|
|
c1c534 |
+ int ret = 0;
|
|
|
c1c534 |
+ unsigned int level = 0;
|
|
|
c1c534 |
+ unsigned int type = 0;
|
|
|
c1c534 |
+ unsigned int cache = 0;
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ if (!resctrl)
|
|
|
c1c534 |
+ return 0;
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ for (level = 0; level < resctrl->nlevels; level++) {
|
|
|
c1c534 |
+ virResctrlAllocPerLevelPtr a_level = resctrl->levels[level];
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ if (!a_level)
|
|
|
c1c534 |
+ continue;
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ for (type = 0; type < VIR_CACHE_TYPE_LAST; type++) {
|
|
|
c1c534 |
+ virResctrlAllocPerTypePtr a_type = a_level->types[type];
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ if (!a_type)
|
|
|
c1c534 |
+ continue;
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ for (cache = 0; cache < a_type->nsizes; cache++) {
|
|
|
c1c534 |
+ unsigned long long *size = a_type->sizes[cache];
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ if (!size)
|
|
|
c1c534 |
+ continue;
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ ret = cb(level, type, cache, *size, opaque);
|
|
|
c1c534 |
+ if (ret < 0)
|
|
|
c1c534 |
+ return ret;
|
|
|
c1c534 |
+ }
|
|
|
c1c534 |
+ }
|
|
|
c1c534 |
+ }
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ return 0;
|
|
|
c1c534 |
+}
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+int
|
|
|
c1c534 |
+virResctrlAllocSetID(virResctrlAllocPtr alloc,
|
|
|
c1c534 |
+ const char *id)
|
|
|
c1c534 |
+{
|
|
|
c1c534 |
+ if (!id) {
|
|
|
c1c534 |
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
c1c534 |
+ _("Resctrl allocation 'id' cannot be NULL"));
|
|
|
c1c534 |
+ return -1;
|
|
|
c1c534 |
+ }
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ return VIR_STRDUP(alloc->id, id);
|
|
|
c1c534 |
+}
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+const char *
|
|
|
c1c534 |
+virResctrlAllocGetID(virResctrlAllocPtr alloc)
|
|
|
c1c534 |
+{
|
|
|
c1c534 |
+ return alloc->id;
|
|
|
c1c534 |
+}
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+char *
|
|
|
c1c534 |
+virResctrlAllocFormat(virResctrlAllocPtr resctrl)
|
|
|
c1c534 |
+{
|
|
|
c1c534 |
+ virBuffer buf = VIR_BUFFER_INITIALIZER;
|
|
|
c1c534 |
+ unsigned int level = 0;
|
|
|
c1c534 |
+ unsigned int type = 0;
|
|
|
c1c534 |
+ unsigned int cache = 0;
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ if (!resctrl)
|
|
|
c1c534 |
+ return NULL;
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ for (level = 0; level < resctrl->nlevels; level++) {
|
|
|
c1c534 |
+ virResctrlAllocPerLevelPtr a_level = resctrl->levels[level];
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ if (!a_level)
|
|
|
c1c534 |
+ continue;
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ for (type = 0; type < VIR_CACHE_TYPE_LAST; type++) {
|
|
|
c1c534 |
+ virResctrlAllocPerTypePtr a_type = a_level->types[type];
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ if (!a_type)
|
|
|
c1c534 |
+ continue;
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ virBufferAsprintf(&buf, "L%u%s:", level, virResctrlTypeToString(type));
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ for (cache = 0; cache < a_type->nmasks; cache++) {
|
|
|
c1c534 |
+ virBitmapPtr mask = a_type->masks[cache];
|
|
|
c1c534 |
+ char *mask_str = NULL;
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ if (!mask)
|
|
|
c1c534 |
+ continue;
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ mask_str = virBitmapToString(mask, false, true);
|
|
|
c1c534 |
+ if (!mask_str) {
|
|
|
c1c534 |
+ virBufferFreeAndReset(&buf;;
|
|
|
c1c534 |
+ return NULL;
|
|
|
c1c534 |
+ }
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ virBufferAsprintf(&buf, "%u=%s;", cache, mask_str);
|
|
|
c1c534 |
+ VIR_FREE(mask_str);
|
|
|
c1c534 |
+ }
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ virBufferTrim(&buf, ";", 1);
|
|
|
c1c534 |
+ virBufferAddChar(&buf, '\n');
|
|
|
c1c534 |
+ }
|
|
|
c1c534 |
+ }
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ virBufferCheckError(&buf;;
|
|
|
c1c534 |
+ return virBufferContentAndReset(&buf;;
|
|
|
c1c534 |
+}
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+static int
|
|
|
c1c534 |
+virResctrlAllocParseProcessCache(virResctrlInfoPtr resctrl,
|
|
|
c1c534 |
+ virResctrlAllocPtr alloc,
|
|
|
c1c534 |
+ unsigned int level,
|
|
|
c1c534 |
+ virCacheType type,
|
|
|
c1c534 |
+ char *cache)
|
|
|
c1c534 |
+{
|
|
|
c1c534 |
+ char *tmp = strchr(cache, '=');
|
|
|
c1c534 |
+ unsigned int cache_id = 0;
|
|
|
c1c534 |
+ virBitmapPtr mask = NULL;
|
|
|
c1c534 |
+ int ret = -1;
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ if (!tmp)
|
|
|
c1c534 |
+ return 0;
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ *tmp = '\0';
|
|
|
c1c534 |
+ tmp++;
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ if (virStrToLong_uip(cache, NULL, 10, &cache_id) < 0) {
|
|
|
c1c534 |
+ virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
c1c534 |
+ _("Invalid cache id '%s'"), cache);
|
|
|
c1c534 |
+ return -1;
|
|
|
c1c534 |
+ }
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ mask = virBitmapNewString(tmp);
|
|
|
c1c534 |
+ if (!mask)
|
|
|
c1c534 |
+ return -1;
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ virBitmapShrink(mask, resctrl->levels[level]->types[type]->bits);
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ if (virResctrlAllocUpdateMask(alloc, level, type, cache_id, mask) < 0)
|
|
|
c1c534 |
+ goto cleanup;
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ ret = 0;
|
|
|
c1c534 |
+ cleanup:
|
|
|
c1c534 |
+ virBitmapFree(mask);
|
|
|
c1c534 |
+ return ret;
|
|
|
c1c534 |
+}
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+static int
|
|
|
c1c534 |
+virResctrlAllocParseProcessLine(virResctrlInfoPtr resctrl,
|
|
|
c1c534 |
+ virResctrlAllocPtr alloc,
|
|
|
c1c534 |
+ char *line)
|
|
|
c1c534 |
+{
|
|
|
c1c534 |
+ char **caches = NULL;
|
|
|
c1c534 |
+ char *tmp = NULL;
|
|
|
c1c534 |
+ unsigned int level = 0;
|
|
|
c1c534 |
+ int type = -1;
|
|
|
c1c534 |
+ size_t ncaches = 0;
|
|
|
c1c534 |
+ size_t i = 0;
|
|
|
c1c534 |
+ int ret = -1;
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ /* For no reason there can be spaces */
|
|
|
c1c534 |
+ virSkipSpaces((const char **) &line);
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ /* Skip lines that don't concern caches, e.g. MB: etc. */
|
|
|
c1c534 |
+ if (line[0] != 'L')
|
|
|
c1c534 |
+ return 0;
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ /* And lines that we can't parse too */
|
|
|
c1c534 |
+ tmp = strchr(line, ':');
|
|
|
c1c534 |
+ if (!tmp)
|
|
|
c1c534 |
+ return 0;
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ *tmp = '\0';
|
|
|
c1c534 |
+ tmp++;
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ if (virStrToLong_uip(line + 1, &line, 10, &level) < 0) {
|
|
|
c1c534 |
+ virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
c1c534 |
+ _("Cannot parse resctrl schema level '%s'"),
|
|
|
c1c534 |
+ line + 1);
|
|
|
c1c534 |
+ return -1;
|
|
|
c1c534 |
+ }
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ type = virResctrlTypeFromString(line);
|
|
|
c1c534 |
+ if (type < 0) {
|
|
|
c1c534 |
+ virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
c1c534 |
+ _("Cannot parse resctrl schema level '%s'"),
|
|
|
c1c534 |
+ line + 1);
|
|
|
c1c534 |
+ return -1;
|
|
|
c1c534 |
+ }
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ caches = virStringSplitCount(tmp, ";", 0, &ncaches);
|
|
|
c1c534 |
+ if (!caches)
|
|
|
c1c534 |
+ return 0;
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ for (i = 0; i < ncaches; i++) {
|
|
|
c1c534 |
+ if (virResctrlAllocParseProcessCache(resctrl, alloc, level, type, caches[i]) < 0)
|
|
|
c1c534 |
+ goto cleanup;
|
|
|
c1c534 |
+ }
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ ret = 0;
|
|
|
c1c534 |
+ cleanup:
|
|
|
c1c534 |
+ virStringListFree(caches);
|
|
|
c1c534 |
+ return ret;
|
|
|
c1c534 |
+}
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+static int
|
|
|
c1c534 |
+virResctrlAllocParse(virResctrlInfoPtr resctrl,
|
|
|
c1c534 |
+ virResctrlAllocPtr alloc,
|
|
|
c1c534 |
+ const char *schemata)
|
|
|
c1c534 |
+{
|
|
|
c1c534 |
+ char **lines = NULL;
|
|
|
c1c534 |
+ size_t nlines = 0;
|
|
|
c1c534 |
+ size_t i = 0;
|
|
|
c1c534 |
+ int ret = -1;
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ lines = virStringSplitCount(schemata, "\n", 0, &nlines);
|
|
|
c1c534 |
+ for (i = 0; i < nlines; i++) {
|
|
|
c1c534 |
+ if (virResctrlAllocParseProcessLine(resctrl, alloc, lines[i]) < 0)
|
|
|
c1c534 |
+ goto cleanup;
|
|
|
c1c534 |
+ }
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ ret = 0;
|
|
|
c1c534 |
+ cleanup:
|
|
|
c1c534 |
+ virStringListFree(lines);
|
|
|
c1c534 |
+ return ret;
|
|
|
c1c534 |
+}
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+static void
|
|
|
c1c534 |
+virResctrlAllocSubtractPerType(virResctrlAllocPerTypePtr dst,
|
|
|
c1c534 |
+ virResctrlAllocPerTypePtr src)
|
|
|
c1c534 |
+{
|
|
|
c1c534 |
+ size_t i = 0;
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ if (!dst || !src)
|
|
|
c1c534 |
+ return;
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ for (i = 0; i < dst->nmasks && i < src->nmasks; i++) {
|
|
|
c1c534 |
+ if (dst->masks[i] && src->masks[i])
|
|
|
c1c534 |
+ virBitmapSubtract(dst->masks[i], src->masks[i]);
|
|
|
c1c534 |
+ }
|
|
|
c1c534 |
+}
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+static void
|
|
|
c1c534 |
+virResctrlAllocSubtract(virResctrlAllocPtr dst,
|
|
|
c1c534 |
+ virResctrlAllocPtr src)
|
|
|
c1c534 |
+{
|
|
|
c1c534 |
+ size_t i = 0;
|
|
|
c1c534 |
+ size_t j = 0;
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ if (!src)
|
|
|
c1c534 |
+ return;
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ for (i = 0; i < dst->nlevels && i < src->nlevels; i++) {
|
|
|
c1c534 |
+ if (dst->levels[i] && src->levels[i]) {
|
|
|
c1c534 |
+ for (j = 0; j < VIR_CACHE_TYPE_LAST; j++) {
|
|
|
c1c534 |
+ virResctrlAllocSubtractPerType(dst->levels[i]->types[j],
|
|
|
c1c534 |
+ src->levels[i]->types[j]);
|
|
|
c1c534 |
+ }
|
|
|
c1c534 |
+ }
|
|
|
c1c534 |
+ }
|
|
|
c1c534 |
+}
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+static virResctrlAllocPtr
|
|
|
c1c534 |
+virResctrlAllocNewFromInfo(virResctrlInfoPtr info)
|
|
|
c1c534 |
+{
|
|
|
c1c534 |
+ size_t i = 0;
|
|
|
c1c534 |
+ size_t j = 0;
|
|
|
c1c534 |
+ size_t k = 0;
|
|
|
c1c534 |
+ virResctrlAllocPtr ret = virResctrlAllocNew();
|
|
|
c1c534 |
+ virBitmapPtr mask = NULL;
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ if (!ret)
|
|
|
c1c534 |
+ return NULL;
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ for (i = 0; i < info->nlevels; i++) {
|
|
|
c1c534 |
+ virResctrlInfoPerLevelPtr i_level = info->levels[i];
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ if (!i_level)
|
|
|
c1c534 |
+ continue;
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ for (j = 0; j < VIR_CACHE_TYPE_LAST; j++) {
|
|
|
c1c534 |
+ virResctrlInfoPerTypePtr i_type = i_level->types[j];
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ if (!i_type)
|
|
|
c1c534 |
+ continue;
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ virBitmapFree(mask);
|
|
|
c1c534 |
+ mask = virBitmapNew(i_type->bits);
|
|
|
c1c534 |
+ if (!mask)
|
|
|
c1c534 |
+ goto error;
|
|
|
c1c534 |
+ virBitmapSetAll(mask);
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ for (k = 0; k <= i_type->max_cache_id; k++) {
|
|
|
c1c534 |
+ if (virResctrlAllocUpdateMask(ret, i, j, k, mask) < 0)
|
|
|
c1c534 |
+ goto error;
|
|
|
c1c534 |
+ }
|
|
|
c1c534 |
+ }
|
|
|
c1c534 |
+ }
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ cleanup:
|
|
|
c1c534 |
+ virBitmapFree(mask);
|
|
|
c1c534 |
+ return ret;
|
|
|
c1c534 |
+ error:
|
|
|
c1c534 |
+ virObjectUnref(ret);
|
|
|
c1c534 |
+ ret = NULL;
|
|
|
c1c534 |
+ goto cleanup;
|
|
|
c1c534 |
+}
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+/*
|
|
|
c1c534 |
+ * This function creates an allocation that represents all unused parts of all
|
|
|
c1c534 |
+ * caches in the system. It uses virResctrlInfo for creating a new full
|
|
|
c1c534 |
+ * allocation with all bits set (using virResctrlAllocNewFromInfo()) and then
|
|
|
c1c534 |
+ * scans for all allocations under /sys/fs/resctrl and subtracts each one of
|
|
|
c1c534 |
+ * them from it. That way it can then return an allocation with only bit set
|
|
|
c1c534 |
+ * being those that are not mentioned in any other allocation. It is used for
|
|
|
c1c534 |
+ * two things, a) calculating the masks when creating allocations and b) from
|
|
|
c1c534 |
+ * tests.
|
|
|
c1c534 |
+ */
|
|
|
c1c534 |
+virResctrlAllocPtr
|
|
|
c1c534 |
+virResctrlAllocGetUnused(virResctrlInfoPtr resctrl)
|
|
|
c1c534 |
+{
|
|
|
c1c534 |
+ virResctrlAllocPtr ret = NULL;
|
|
|
c1c534 |
+ virResctrlAllocPtr alloc = NULL;
|
|
|
c1c534 |
+ struct dirent *ent = NULL;
|
|
|
c1c534 |
+ DIR *dirp = NULL;
|
|
|
c1c534 |
+ char *schemata = NULL;
|
|
|
c1c534 |
+ int rv = -1;
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ if (virResctrlInfoIsEmpty(resctrl)) {
|
|
|
c1c534 |
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
|
c1c534 |
+ _("Resource control is not supported on this host"));
|
|
|
c1c534 |
+ return NULL;
|
|
|
c1c534 |
+ }
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ ret = virResctrlAllocNewFromInfo(resctrl);
|
|
|
c1c534 |
+ if (!ret)
|
|
|
c1c534 |
+ return NULL;
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ if (virFileReadValueString(&schemata,
|
|
|
c1c534 |
+ SYSFS_RESCTRL_PATH
|
|
|
c1c534 |
+ "/schemata") < 0) {
|
|
|
c1c534 |
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
c1c534 |
+ _("Could not read schemata file for the default group"));
|
|
|
c1c534 |
+ goto error;
|
|
|
c1c534 |
+ }
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ alloc = virResctrlAllocNew();
|
|
|
c1c534 |
+ if (!alloc)
|
|
|
c1c534 |
+ goto error;
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ if (virResctrlAllocParse(resctrl, alloc, schemata) < 0)
|
|
|
c1c534 |
+ goto error;
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ virResctrlAllocSubtract(ret, alloc);
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ if (virDirOpen(&dirp, SYSFS_RESCTRL_PATH) < 0)
|
|
|
c1c534 |
+ goto error;
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ while ((rv = virDirRead(dirp, &ent, SYSFS_RESCTRL_PATH)) > 0) {
|
|
|
c1c534 |
+ if (ent->d_type != DT_DIR)
|
|
|
c1c534 |
+ continue;
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ if (STREQ(ent->d_name, "info"))
|
|
|
c1c534 |
+ continue;
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ VIR_FREE(schemata);
|
|
|
c1c534 |
+ rv = virFileReadValueString(&schemata,
|
|
|
c1c534 |
+ SYSFS_RESCTRL_PATH
|
|
|
c1c534 |
+ "/%s/schemata",
|
|
|
c1c534 |
+ ent->d_name);
|
|
|
c1c534 |
+ if (rv == -2)
|
|
|
c1c534 |
+ continue;
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ if (rv < 0) {
|
|
|
c1c534 |
+ virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
c1c534 |
+ _("Could not read schemata file for group %s"),
|
|
|
c1c534 |
+ ent->d_name);
|
|
|
c1c534 |
+ goto error;
|
|
|
c1c534 |
+ }
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ virObjectUnref(alloc);
|
|
|
c1c534 |
+ alloc = virResctrlAllocNew();
|
|
|
c1c534 |
+ if (!alloc)
|
|
|
c1c534 |
+ goto error;
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ if (virResctrlAllocParse(resctrl, alloc, schemata) < 0)
|
|
|
c1c534 |
+ goto error;
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ virResctrlAllocSubtract(ret, alloc);
|
|
|
c1c534 |
+ }
|
|
|
c1c534 |
+ if (rv < 0)
|
|
|
c1c534 |
+ goto error;
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ cleanup:
|
|
|
c1c534 |
+ virObjectUnref(alloc);
|
|
|
c1c534 |
+ VIR_DIR_CLOSE(dirp);
|
|
|
c1c534 |
+ VIR_FREE(schemata);
|
|
|
c1c534 |
+ return ret;
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ error:
|
|
|
c1c534 |
+ virObjectUnref(ret);
|
|
|
c1c534 |
+ ret = NULL;
|
|
|
c1c534 |
+ goto cleanup;
|
|
|
c1c534 |
+}
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+static int
|
|
|
c1c534 |
+virResctrlAllocSetMask(virResctrlAllocPerTypePtr a_type,
|
|
|
c1c534 |
+ unsigned int cache,
|
|
|
c1c534 |
+ virBitmapPtr mask)
|
|
|
c1c534 |
+{
|
|
|
c1c534 |
+ if (a_type->nmasks <= cache &&
|
|
|
c1c534 |
+ VIR_EXPAND_N(a_type->masks, a_type->nmasks,
|
|
|
c1c534 |
+ cache - a_type->nmasks + 1) < 0)
|
|
|
c1c534 |
+ return -1;
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ a_type->masks[cache] = mask;
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ return 0;
|
|
|
c1c534 |
+}
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+/*
|
|
|
c1c534 |
+ * Given the information about requested allocation type `a_type`, the host
|
|
|
c1c534 |
+ * cache for a particular type `i_type` and unused bits in the system `f_type`
|
|
|
c1c534 |
+ * this function tries to find the smallest free space in which the allocation
|
|
|
c1c534 |
+ * for cache id `cache` would fit. We're looking for the smallest place in
|
|
|
c1c534 |
+ * order to minimize fragmentation and maximize the possibility of succeeding.
|
|
|
c1c534 |
+ */
|
|
|
c1c534 |
+static int
|
|
|
c1c534 |
+virResctrlAllocFindUnused(virResctrlAllocPerTypePtr a_type,
|
|
|
c1c534 |
+ virResctrlInfoPerTypePtr i_type,
|
|
|
c1c534 |
+ virResctrlAllocPerTypePtr f_type,
|
|
|
c1c534 |
+ unsigned int level,
|
|
|
c1c534 |
+ unsigned int type,
|
|
|
c1c534 |
+ unsigned int cache)
|
|
|
c1c534 |
+{
|
|
|
c1c534 |
+ unsigned long long *size = a_type->sizes[cache];
|
|
|
c1c534 |
+ virBitmapPtr a_mask = NULL;
|
|
|
c1c534 |
+ virBitmapPtr f_mask = NULL;
|
|
|
c1c534 |
+ unsigned long long need_bits;
|
|
|
c1c534 |
+ size_t i = 0;
|
|
|
c1c534 |
+ ssize_t pos = -1;
|
|
|
c1c534 |
+ ssize_t last_bits = 0;
|
|
|
c1c534 |
+ ssize_t last_pos = -1;
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ /* If there is no reservation requested we need to set all bits. That's due
|
|
|
c1c534 |
+ * to weird interface of the resctrl sysfs. It's also the reason why we
|
|
|
c1c534 |
+ * cannot reserve the whole cache in one allocation. */
|
|
|
c1c534 |
+ if (!size) {
|
|
|
c1c534 |
+ a_mask = virBitmapNew(i_type->bits);
|
|
|
c1c534 |
+ if (!a_mask)
|
|
|
c1c534 |
+ return -1;
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ virBitmapSetAll(a_mask);
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ if (virResctrlAllocSetMask(a_type, cache, a_mask) < 0) {
|
|
|
c1c534 |
+ virBitmapFree(a_mask);
|
|
|
c1c534 |
+ return -1;
|
|
|
c1c534 |
+ }
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ return 0;
|
|
|
c1c534 |
+ }
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ if (cache >= f_type->nmasks) {
|
|
|
c1c534 |
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
|
|
c1c534 |
+ _("Cache with id %u does not exists for level %d"),
|
|
|
c1c534 |
+ cache, level);
|
|
|
c1c534 |
+ return -1;
|
|
|
c1c534 |
+ }
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ f_mask = f_type->masks[cache];
|
|
|
c1c534 |
+ if (!f_mask) {
|
|
|
c1c534 |
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
|
|
c1c534 |
+ _("Cache level %d id %u does not support tuning for "
|
|
|
c1c534 |
+ "scope type '%s'"),
|
|
|
c1c534 |
+ level, cache, virCacheTypeToString(type));
|
|
|
c1c534 |
+ return -1;
|
|
|
c1c534 |
+ }
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ if (*size == i_type->size) {
|
|
|
c1c534 |
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
|
|
c1c534 |
+ _("Cache allocation for the whole cache is not "
|
|
|
c1c534 |
+ "possible, specify size smaller than %llu"),
|
|
|
c1c534 |
+ i_type->size);
|
|
|
c1c534 |
+ return -1;
|
|
|
c1c534 |
+ }
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ need_bits = *size / i_type->control.granularity;
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ if (*size % i_type->control.granularity) {
|
|
|
c1c534 |
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
|
|
c1c534 |
+ _("Cache allocation of size %llu is not "
|
|
|
c1c534 |
+ "divisible by granularity %llu"),
|
|
|
c1c534 |
+ *size, i_type->control.granularity);
|
|
|
c1c534 |
+ return -1;
|
|
|
c1c534 |
+ }
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ if (need_bits < i_type->min_cbm_bits) {
|
|
|
c1c534 |
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
|
|
c1c534 |
+ _("Cache allocation of size %llu is smaller "
|
|
|
c1c534 |
+ "than the minimum allowed allocation %llu"),
|
|
|
c1c534 |
+ *size,
|
|
|
c1c534 |
+ i_type->control.granularity * i_type->min_cbm_bits);
|
|
|
c1c534 |
+ return -1;
|
|
|
c1c534 |
+ }
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ while ((pos = virBitmapNextSetBit(f_mask, pos)) >= 0) {
|
|
|
c1c534 |
+ ssize_t pos_clear = virBitmapNextClearBit(f_mask, pos);
|
|
|
c1c534 |
+ ssize_t bits;
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ if (pos_clear < 0)
|
|
|
c1c534 |
+ pos_clear = virBitmapSize(f_mask);
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ bits = pos_clear - pos;
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ /* Not enough bits, move on and skip all of them */
|
|
|
c1c534 |
+ if (bits < need_bits) {
|
|
|
c1c534 |
+ pos = pos_clear;
|
|
|
c1c534 |
+ continue;
|
|
|
c1c534 |
+ }
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ /* This fits perfectly */
|
|
|
c1c534 |
+ if (bits == need_bits) {
|
|
|
c1c534 |
+ last_pos = pos;
|
|
|
c1c534 |
+ break;
|
|
|
c1c534 |
+ }
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ /* Remember the smaller region if we already found on before */
|
|
|
c1c534 |
+ if (last_pos < 0 || (last_bits && bits < last_bits)) {
|
|
|
c1c534 |
+ last_bits = bits;
|
|
|
c1c534 |
+ last_pos = pos;
|
|
|
c1c534 |
+ }
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ pos = pos_clear;
|
|
|
c1c534 |
+ }
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ if (last_pos < 0) {
|
|
|
c1c534 |
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
|
|
c1c534 |
+ _("Not enough room for allocation of "
|
|
|
c1c534 |
+ "%llu bytes for level %u cache %u "
|
|
|
c1c534 |
+ "scope type '%s'"),
|
|
|
c1c534 |
+ *size, level, cache,
|
|
|
c1c534 |
+ virCacheTypeToString(type));
|
|
|
c1c534 |
+ return -1;
|
|
|
c1c534 |
+ }
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ a_mask = virBitmapNew(i_type->bits);
|
|
|
c1c534 |
+ if (!a_mask)
|
|
|
c1c534 |
+ return -1;
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ for (i = last_pos; i < last_pos + need_bits; i++) {
|
|
|
c1c534 |
+ ignore_value(virBitmapSetBit(a_mask, i));
|
|
|
c1c534 |
+ ignore_value(virBitmapClearBit(f_mask, i));
|
|
|
c1c534 |
+ }
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ if (virResctrlAllocSetMask(a_type, cache, a_mask) < 0) {
|
|
|
c1c534 |
+ virBitmapFree(a_mask);
|
|
|
c1c534 |
+ return -1;
|
|
|
c1c534 |
+ }
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ return 0;
|
|
|
c1c534 |
+}
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+/*
|
|
|
c1c534 |
+ * This function is called when creating an allocation in the system. What it
|
|
|
c1c534 |
+ * does is that it gets all the unused bits using virResctrlAllocGetUnused() and
|
|
|
c1c534 |
+ * then tries to find a proper space for every requested allocation effectively
|
|
|
c1c534 |
+ * transforming `sizes` into `masks`.
|
|
|
c1c534 |
+ */
|
|
|
c1c534 |
+static int
|
|
|
c1c534 |
+virResctrlAllocMasksAssign(virResctrlInfoPtr resctrl,
|
|
|
c1c534 |
+ virResctrlAllocPtr alloc)
|
|
|
c1c534 |
+{
|
|
|
c1c534 |
+ int ret = -1;
|
|
|
c1c534 |
+ unsigned int level = 0;
|
|
|
c1c534 |
+ virResctrlAllocPtr alloc_free = NULL;
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ alloc_free = virResctrlAllocGetUnused(resctrl);
|
|
|
c1c534 |
+ if (!alloc_free)
|
|
|
c1c534 |
+ return -1;
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ for (level = 0; level < alloc->nlevels; level++) {
|
|
|
c1c534 |
+ virResctrlAllocPerLevelPtr a_level = alloc->levels[level];
|
|
|
c1c534 |
+ virResctrlAllocPerLevelPtr f_level = NULL;
|
|
|
c1c534 |
+ unsigned int type = 0;
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ if (!a_level)
|
|
|
c1c534 |
+ continue;
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ if (level < alloc_free->nlevels)
|
|
|
c1c534 |
+ f_level = alloc_free->levels[level];
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ if (!f_level) {
|
|
|
c1c534 |
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
|
|
c1c534 |
+ _("Cache level %d does not support tuning"),
|
|
|
c1c534 |
+ level);
|
|
|
c1c534 |
+ goto cleanup;
|
|
|
c1c534 |
+ }
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ for (type = 0; type < VIR_CACHE_TYPE_LAST; type++) {
|
|
|
c1c534 |
+ virResctrlAllocPerTypePtr a_type = a_level->types[type];
|
|
|
c1c534 |
+ virResctrlAllocPerTypePtr f_type = f_level->types[type];
|
|
|
c1c534 |
+ unsigned int cache = 0;
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ if (!a_type)
|
|
|
c1c534 |
+ continue;
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ if (!f_type) {
|
|
|
c1c534 |
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
|
|
c1c534 |
+ _("Cache level %d does not support tuning for "
|
|
|
c1c534 |
+ "scope type '%s'"),
|
|
|
c1c534 |
+ level, virCacheTypeToString(type));
|
|
|
c1c534 |
+ goto cleanup;
|
|
|
c1c534 |
+ }
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ for (cache = 0; cache < a_type->nsizes; cache++) {
|
|
|
c1c534 |
+ virResctrlInfoPerLevelPtr i_level = resctrl->levels[level];
|
|
|
c1c534 |
+ virResctrlInfoPerTypePtr i_type = i_level->types[type];
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ if (virResctrlAllocFindUnused(a_type, i_type, f_type, level, type, cache) < 0)
|
|
|
c1c534 |
+ goto cleanup;
|
|
|
c1c534 |
+ }
|
|
|
c1c534 |
+ }
|
|
|
c1c534 |
+ }
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ ret = 0;
|
|
|
c1c534 |
+ cleanup:
|
|
|
c1c534 |
+ virObjectUnref(alloc_free);
|
|
|
c1c534 |
+ return ret;
|
|
|
c1c534 |
+}
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+/* This checks if the directory for the alloc exists. If not it tries to create
|
|
|
c1c534 |
+ * it and apply appropriate alloc settings. */
|
|
|
c1c534 |
+int
|
|
|
c1c534 |
+virResctrlAllocCreate(virResctrlInfoPtr resctrl,
|
|
|
c1c534 |
+ virResctrlAllocPtr alloc,
|
|
|
c1c534 |
+ const char *machinename)
|
|
|
c1c534 |
+{
|
|
|
c1c534 |
+ char *schemata_path = NULL;
|
|
|
c1c534 |
+ char *alloc_str = NULL;
|
|
|
c1c534 |
+ int ret = -1;
|
|
|
c1c534 |
+ int lockfd = -1;
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ if (!alloc)
|
|
|
c1c534 |
+ return 0;
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ if (!resctrl) {
|
|
|
c1c534 |
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
|
c1c534 |
+ _("Resource control is not supported on this host"));
|
|
|
c1c534 |
+ return -1;
|
|
|
c1c534 |
+ }
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ if (!alloc->id) {
|
|
|
c1c534 |
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
c1c534 |
+ _("Resctrl Allocation ID must be set before creation"));
|
|
|
c1c534 |
+ return -1;
|
|
|
c1c534 |
+ }
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ if (!alloc->path &&
|
|
|
c1c534 |
+ virAsprintf(&alloc->path, "%s/%s-%s",
|
|
|
c1c534 |
+ SYSFS_RESCTRL_PATH, machinename, alloc->id) < 0)
|
|
|
c1c534 |
+ return -1;
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ if (virFileExists(alloc->path)) {
|
|
|
c1c534 |
+ virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
c1c534 |
+ _("Path '%s' for resctrl allocation exists"),
|
|
|
c1c534 |
+ alloc->path);
|
|
|
c1c534 |
+ goto cleanup;
|
|
|
c1c534 |
+ }
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ lockfd = virResctrlLockWrite();
|
|
|
c1c534 |
+ if (lockfd < 0)
|
|
|
c1c534 |
+ goto cleanup;
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ if (virResctrlAllocMasksAssign(resctrl, alloc) < 0)
|
|
|
c1c534 |
+ goto cleanup;
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ alloc_str = virResctrlAllocFormat(alloc);
|
|
|
c1c534 |
+ if (!alloc_str)
|
|
|
c1c534 |
+ goto cleanup;
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ if (virAsprintf(&schemata_path, "%s/schemata", alloc->path) < 0)
|
|
|
c1c534 |
+ goto cleanup;
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ if (virFileMakePath(alloc->path) < 0) {
|
|
|
c1c534 |
+ virReportSystemError(errno,
|
|
|
c1c534 |
+ _("Cannot create resctrl directory '%s'"),
|
|
|
c1c534 |
+ alloc->path);
|
|
|
c1c534 |
+ goto cleanup;
|
|
|
c1c534 |
+ }
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ VIR_DEBUG("Writing resctrl schemata '%s' into '%s'", alloc_str, schemata_path);
|
|
|
c1c534 |
+ if (virFileWriteStr(schemata_path, alloc_str, 0) < 0) {
|
|
|
c1c534 |
+ rmdir(alloc->path);
|
|
|
c1c534 |
+ virReportSystemError(errno,
|
|
|
c1c534 |
+ _("Cannot write into schemata file '%s'"),
|
|
|
c1c534 |
+ schemata_path);
|
|
|
c1c534 |
+ goto cleanup;
|
|
|
c1c534 |
+ }
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ ret = 0;
|
|
|
c1c534 |
+ cleanup:
|
|
|
c1c534 |
+ virResctrlUnlock(lockfd);
|
|
|
c1c534 |
+ VIR_FREE(alloc_str);
|
|
|
c1c534 |
+ VIR_FREE(schemata_path);
|
|
|
c1c534 |
+ return ret;
|
|
|
c1c534 |
+}
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+int
|
|
|
c1c534 |
+virResctrlAllocAddPID(virResctrlAllocPtr alloc,
|
|
|
c1c534 |
+ pid_t pid)
|
|
|
c1c534 |
+{
|
|
|
c1c534 |
+ char *tasks = NULL;
|
|
|
c1c534 |
+ char *pidstr = NULL;
|
|
|
c1c534 |
+ int ret = 0;
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ if (!alloc->path) {
|
|
|
c1c534 |
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
c1c534 |
+ _("Cannot add pid to non-existing resctrl allocation"));
|
|
|
c1c534 |
+ return -1;
|
|
|
c1c534 |
+ }
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ if (virAsprintf(&tasks, "%s/tasks", alloc->path) < 0)
|
|
|
c1c534 |
+ return -1;
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ if (virAsprintf(&pidstr, "%lld", (long long int) pid) < 0)
|
|
|
c1c534 |
+ goto cleanup;
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ if (virFileWriteStr(tasks, pidstr, 0) < 0) {
|
|
|
c1c534 |
+ virReportSystemError(errno,
|
|
|
c1c534 |
+ _("Cannot write pid in tasks file '%s'"),
|
|
|
c1c534 |
+ tasks);
|
|
|
c1c534 |
+ goto cleanup;
|
|
|
c1c534 |
+ }
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ ret = 0;
|
|
|
c1c534 |
+ cleanup:
|
|
|
c1c534 |
+ VIR_FREE(tasks);
|
|
|
c1c534 |
+ VIR_FREE(pidstr);
|
|
|
c1c534 |
+ return ret;
|
|
|
c1c534 |
+}
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+int
|
|
|
c1c534 |
+virResctrlAllocRemove(virResctrlAllocPtr alloc)
|
|
|
c1c534 |
+{
|
|
|
c1c534 |
+ int ret = 0;
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ if (!alloc->path)
|
|
|
c1c534 |
+ return 0;
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ VIR_DEBUG("Removing resctrl allocation %s", alloc->path);
|
|
|
c1c534 |
+ if (rmdir(alloc->path) != 0 && errno != ENOENT) {
|
|
|
c1c534 |
+ ret = -errno;
|
|
|
c1c534 |
+ VIR_ERROR(_("Unable to remove %s (%d)"), alloc->path, errno);
|
|
|
c1c534 |
+ }
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+ return ret;
|
|
|
c1c534 |
+}
|
|
|
c1c534 |
diff --git a/src/util/virresctrl.h b/src/util/virresctrl.h
|
|
|
c1c534 |
index a81ff687de..63a954feaa 100644
|
|
|
c1c534 |
--- a/src/util/virresctrl.h
|
|
|
c1c534 |
+++ b/src/util/virresctrl.h
|
|
|
c1c534 |
@@ -65,4 +65,53 @@ virResctrlInfoGetCache(virResctrlInfoPtr resctrl,
|
|
|
c1c534 |
size_t *ncontrols,
|
|
|
c1c534 |
virResctrlInfoPerCachePtr **controls);
|
|
|
c1c534 |
|
|
|
c1c534 |
+/* Alloc-related things */
|
|
|
c1c534 |
+typedef struct _virResctrlAlloc virResctrlAlloc;
|
|
|
c1c534 |
+typedef virResctrlAlloc *virResctrlAllocPtr;
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+typedef int virResctrlAllocForeachSizeCallback(unsigned int level,
|
|
|
c1c534 |
+ virCacheType type,
|
|
|
c1c534 |
+ unsigned int cache,
|
|
|
c1c534 |
+ unsigned long long size,
|
|
|
c1c534 |
+ void *opaque);
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+virResctrlAllocPtr
|
|
|
c1c534 |
+virResctrlAllocNew(void);
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+bool
|
|
|
c1c534 |
+virResctrlAllocIsEmpty(virResctrlAllocPtr resctrl);
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+int
|
|
|
c1c534 |
+virResctrlAllocSetSize(virResctrlAllocPtr resctrl,
|
|
|
c1c534 |
+ unsigned int level,
|
|
|
c1c534 |
+ virCacheType type,
|
|
|
c1c534 |
+ unsigned int cache,
|
|
|
c1c534 |
+ unsigned long long size);
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+int
|
|
|
c1c534 |
+virResctrlAllocForeachSize(virResctrlAllocPtr resctrl,
|
|
|
c1c534 |
+ virResctrlAllocForeachSizeCallback cb,
|
|
|
c1c534 |
+ void *opaque);
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+int
|
|
|
c1c534 |
+virResctrlAllocSetID(virResctrlAllocPtr alloc,
|
|
|
c1c534 |
+ const char *id);
|
|
|
c1c534 |
+const char *
|
|
|
c1c534 |
+virResctrlAllocGetID(virResctrlAllocPtr alloc);
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+char *
|
|
|
c1c534 |
+virResctrlAllocFormat(virResctrlAllocPtr alloc);
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+int
|
|
|
c1c534 |
+virResctrlAllocCreate(virResctrlInfoPtr r_info,
|
|
|
c1c534 |
+ virResctrlAllocPtr alloc,
|
|
|
c1c534 |
+ const char *machinename);
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+int
|
|
|
c1c534 |
+virResctrlAllocAddPID(virResctrlAllocPtr alloc,
|
|
|
c1c534 |
+ pid_t pid);
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+int
|
|
|
c1c534 |
+virResctrlAllocRemove(virResctrlAllocPtr alloc);
|
|
|
c1c534 |
+
|
|
|
c1c534 |
#endif /* __VIR_RESCTRL_H__ */
|
|
|
c1c534 |
diff --git a/src/util/virresctrlpriv.h b/src/util/virresctrlpriv.h
|
|
|
c1c534 |
new file mode 100644
|
|
|
c1c534 |
index 0000000000..72b115afd1
|
|
|
c1c534 |
--- /dev/null
|
|
|
c1c534 |
+++ b/src/util/virresctrlpriv.h
|
|
|
c1c534 |
@@ -0,0 +1,27 @@
|
|
|
c1c534 |
+/*
|
|
|
c1c534 |
+ * virresctrlpriv.h:
|
|
|
c1c534 |
+ *
|
|
|
c1c534 |
+ * This library is free software; you can redistribute it and/or
|
|
|
c1c534 |
+ * modify it under the terms of the GNU Lesser General Public
|
|
|
c1c534 |
+ * License as published by the Free Software Foundation; either
|
|
|
c1c534 |
+ * version 2.1 of the License, or (at your option) any later version.
|
|
|
c1c534 |
+ *
|
|
|
c1c534 |
+ * This library is distributed in the hope that it will be useful,
|
|
|
c1c534 |
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
c1c534 |
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
c1c534 |
+ * Lesser General Public License for more details.
|
|
|
c1c534 |
+ *
|
|
|
c1c534 |
+ * You should have received a copy of the GNU Lesser General Public
|
|
|
c1c534 |
+ * License along with this library. If not, see
|
|
|
c1c534 |
+ * <http://www.gnu.org/licenses/>.
|
|
|
c1c534 |
+ */
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+#ifndef __VIR_RESCTRL_PRIV_H__
|
|
|
c1c534 |
+# define __VIR_RESCTRL_PRIV_H__
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+# include "virresctrl.h"
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+virResctrlAllocPtr
|
|
|
c1c534 |
+virResctrlAllocGetUnused(virResctrlInfoPtr resctrl);
|
|
|
c1c534 |
+
|
|
|
c1c534 |
+#endif /* __VIR_RESCTRL_PRIV_H__ */
|
|
|
c1c534 |
--
|
|
|
c1c534 |
2.16.1
|
|
|
c1c534 |
|