From nobody Thu May 15 09:55:14 2025 Delivered-To: importer@patchew.org Received-SPF: pass (zoho.com: domain of redhat.com designates 209.132.183.28 as permitted sender) client-ip=209.132.183.28; envelope-from=libvir-list-bounces@redhat.com; helo=mx1.redhat.com; Authentication-Results: mx.zohomail.com; spf=pass (zoho.com: domain of redhat.com designates 209.132.183.28 as permitted sender) smtp.mailfrom=libvir-list-bounces@redhat.com Return-Path: Received: from mx1.redhat.com (mx1.redhat.com [209.132.183.28]) by mx.zohomail.com with SMTPS id 1510563068574814.5997659226222; Mon, 13 Nov 2017 00:51:08 -0800 (PST) Received: from smtp.corp.redhat.com (int-mx04.intmail.prod.int.phx2.redhat.com [10.5.11.14]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 38C3768692; Mon, 13 Nov 2017 08:51:07 +0000 (UTC) Received: from colo-mx.corp.redhat.com (colo-mx02.intmail.prod.int.phx2.redhat.com [10.5.11.21]) by smtp.corp.redhat.com (Postfix) with ESMTPS id 0D5935E271; Mon, 13 Nov 2017 08:51:07 +0000 (UTC) Received: from lists01.pubmisc.prod.ext.phx2.redhat.com (lists01.pubmisc.prod.ext.phx2.redhat.com [10.5.19.33]) by colo-mx.corp.redhat.com (Postfix) with ESMTP id CB9FF3D394; Mon, 13 Nov 2017 08:51:06 +0000 (UTC) Received: from smtp.corp.redhat.com (int-mx02.intmail.prod.int.phx2.redhat.com [10.5.11.12]) by lists01.pubmisc.prod.ext.phx2.redhat.com (8.13.8/8.13.8) with ESMTP id vAD8olNg016333 for ; Mon, 13 Nov 2017 03:50:47 -0500 Received: by smtp.corp.redhat.com (Postfix) id ED4D86253D; Mon, 13 Nov 2017 08:50:47 +0000 (UTC) Received: from caroline.localdomain (unknown [10.43.2.67]) by smtp.corp.redhat.com (Postfix) with ESMTPS id 4C5D76253C for ; Mon, 13 Nov 2017 08:50:47 +0000 (UTC) Received: from caroline.brq.redhat.com (caroline.brq.redhat.com [127.0.0.1]) by caroline.localdomain (Postfix) with ESMTP id D40F6123A8C for ; Mon, 13 Nov 2017 09:50:41 +0100 (CET) From: Martin Kletzander To: libvir-list@redhat.com Date: Mon, 13 Nov 2017 09:50:31 +0100 Message-Id: <17bac843a2dd341d0592e2f00ac8393fc5babfb7.1510560300.git.mkletzan@redhat.com> In-Reply-To: References: In-Reply-To: References: X-Scanned-By: MIMEDefang 2.79 on 10.5.11.12 X-loop: libvir-list@redhat.com Subject: [libvirt] [PATCH 16/21] resctrl: Add functions to work with resctrl allocations X-BeenThere: libvir-list@redhat.com X-Mailman-Version: 2.1.12 Precedence: junk List-Id: Development discussions about the libvirt library & tools List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Sender: libvir-list-bounces@redhat.com Errors-To: libvir-list-bounces@redhat.com X-Scanned-By: MIMEDefang 2.79 on 10.5.11.14 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.27]); Mon, 13 Nov 2017 08:51:07 +0000 (UTC) X-ZohoMail: RSF_0 Z_629925259 SPT_0 Content-Type: text/plain; charset="utf-8" With this commit we finally have a way to read and manipulate basic resctrl settings. Locking is done only on exposed functions that read/write from/to resctrlfs. Not in fuctions that are exposed in virresctrlpriv.h as those a= re only supposed to be used from tests. Signed-off-by: Martin Kletzander --- src/Makefile.am | 2 +- src/libvirt_private.syms | 12 + src/util/virresctrl.c | 1012 +++++++++++++++++++++++++++++++++++++++++= +++- src/util/virresctrl.h | 59 +++ src/util/virresctrlpriv.h | 32 ++ 5 files changed, 1115 insertions(+), 2 deletions(-) create mode 100644 src/util/virresctrlpriv.h diff --git a/src/Makefile.am b/src/Makefile.am index 1d24231249de..ad113262fbb0 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -167,7 +167,7 @@ UTIL_SOURCES =3D \ util/virprocess.c util/virprocess.h \ util/virqemu.c util/virqemu.h \ util/virrandom.h util/virrandom.c \ - util/virresctrl.h util/virresctrl.c \ + util/virresctrl.h util/virresctrl.c util/virresctrlpriv.h \ util/virrotatingfile.h util/virrotatingfile.c \ util/virscsi.c util/virscsi.h \ util/virscsihost.c util/virscsihost.h \ diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index b24728ce4a1d..37bac41e618b 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -2532,6 +2532,18 @@ virRandomInt; # util/virresctrl.h virCacheTypeFromString; virCacheTypeToString; +virResctrlAllocAddPID; +virResctrlAllocCreate; +virResctrlAllocForeachSize; +virResctrlAllocFormat; +virResctrlAllocGetFree; +virResctrlAllocMasksAssign; +virResctrlAllocNewFromInfo; +virResctrlAllocRemove; +virResctrlAllocSetID; +virResctrlAllocSetSize; +virResctrlAllocUpdateMask; +virResctrlAllocUpdateSize; virResctrlGetInfo; virResctrlInfoGetCache; =20 diff --git a/src/util/virresctrl.c b/src/util/virresctrl.c index 6c6692e78f42..ac1b38436bb2 100644 --- a/src/util/virresctrl.c +++ b/src/util/virresctrl.c @@ -23,7 +23,7 @@ #include #include =20 -#include "virresctrl.h" +#include "virresctrlpriv.h" =20 #include "c-ctype.h" #include "count-one-bits.h" @@ -151,6 +151,153 @@ virResctrlInfoNew(void) } =20 =20 +/* Alloc-related definitions and AllocClass-related functions */ +typedef struct _virResctrlAllocPerType virResctrlAllocPerType; +typedef virResctrlAllocPerType *virResctrlAllocPerTypePtr; +struct _virResctrlAllocPerType { + /* There could be bool saying whether this is set or not, but since ev= erything + * in virResctrlAlloc (and most of libvirt) goes with pointer arrays w= e would + * have to have one more level of allocation anyway, so this stays fai= thful to + * the concept */ + unsigned long long **sizes; + size_t nsizes; + + /* Mask for each cache */ + virBitmapPtr *masks; + size_t nmasks; +}; + +typedef struct _virResctrlAllocPerLevel virResctrlAllocPerLevel; +typedef virResctrlAllocPerLevel *virResctrlAllocPerLevelPtr; +struct _virResctrlAllocPerLevel { + virResctrlAllocPerTypePtr *types; /* Indexed with enum virCacheType */ +}; + +struct _virResctrlAlloc { + virObject parent; + + virResctrlAllocPerLevelPtr *levels; + size_t nlevels; + + char *id; /* The identifier (any unique string for now) */ + char *path; +}; + +static virClassPtr virResctrlAllocClass; + +static void +virResctrlAllocDispose(void *obj) +{ + size_t i =3D 0; + size_t j =3D 0; + size_t k =3D 0; + + virResctrlAllocPtr resctrl =3D obj; + + for (i =3D 0; i < resctrl->nlevels; i++) { + virResctrlAllocPerLevelPtr level =3D resctrl->levels[--resctrl->nl= evels]; + + if (!level) + continue; + + for (j =3D 0; j < VIR_CACHE_TYPE_LAST; j++) { + virResctrlAllocPerTypePtr type =3D level->types[j]; + + if (!type) + continue; + + for (k =3D 0; k < type->nsizes; k++) + VIR_FREE(type->sizes[k]); + + VIR_FREE(type->sizes); + VIR_FREE(type); + } + VIR_FREE(level->types); + VIR_FREE(level); + } + + VIR_FREE(resctrl->id); + VIR_FREE(resctrl->levels); +} + +static int virResctrlAllocOnceInit(void) +{ + if (!(virResctrlAllocClass =3D virClassNew(virClassForObject(), + "virResctrlAlloc", + sizeof(virResctrlAlloc), + virResctrlAllocDispose))) + return -1; + + return 0; +} + +VIR_ONCE_GLOBAL_INIT(virResctrlAlloc) + +static virResctrlAllocPtr +virResctrlAllocNew(void) +{ + if (virResctrlAllocInitialize() < 0) + return NULL; + + return virObjectNew(virResctrlAllocClass); +} + + +/* Common functions */ +static int +virResctrlLockInternal(int op) +{ + int fd =3D open(SYSFS_RESCTRL_PATH, O_DIRECTORY | O_CLOEXEC); + + if (fd < 0) { + virReportSystemError(errno, "%s", _("Cannot open resctrlfs")); + return -1; + } + + if (flock(fd, op) < 0) { + virReportSystemError(errno, "%s", _("Cannot lock resctrlfs")); + VIR_FORCE_CLOSE(fd); + return -1; + } + + return fd; +} + +static inline int +virResctrlLockRead(void) +{ + return virResctrlLockInternal(LOCK_SH); +} + +static inline int +virResctrlLockWrite(void) +{ + return virResctrlLockInternal(LOCK_EX); +} + +static int +virResctrlUnlock(int fd) +{ + int ret =3D -1; + + if (fd =3D=3D -1) + return 0; + + /* The lock gets unlocked by closing the fd, which we need to do anywa= y in + * order to clean up properly */ + if (VIR_CLOSE(fd) < 0) { + virReportSystemError(errno, "%s", _("Cannot close resctrlfs")); + + /* Trying to save the already broken */ + if (flock(fd, LOCK_UN) < 0) + virReportSystemError(errno, "%s", _("Cannot unlock resctrlfs")= ); + return -1; + } + + return ret; +} + + /* Info-related functions */ int virResctrlGetInfo(virResctrlInfoPtr *resctrl) @@ -318,3 +465,866 @@ virResctrlInfoGetCache(virResctrlInfoPtr resctrl, VIR_FREE(*controls); goto cleanup; } + + +/* Alloc-related functions */ +static virResctrlAllocPerTypePtr +virResctrlAllocFindType(virResctrlAllocPtr *resctrl, + unsigned int level, + virCacheType type, + bool alloc) +{ + virResctrlAllocPerLevelPtr a_level =3D NULL; + virResctrlAllocPtr tmp =3D NULL; + + if (!*resctrl) { + if (!alloc || !(*resctrl =3D virResctrlAllocNew())) + return NULL; + } + + tmp =3D *resctrl; + + /* Per-level array */ + if (tmp->nlevels <=3D level) { + if (!alloc || VIR_EXPAND_N(tmp->levels, tmp->nlevels, + level - tmp->nlevels + 1) < 0) + return NULL; + } + + if (!tmp->levels[level]) { + if (!alloc || + VIR_ALLOC(tmp->levels[level]) < 0 || + VIR_ALLOC_N(tmp->levels[level]->types, VIR_CACHE_TYPE_LAST) < = 0) + return NULL; + } + a_level =3D tmp->levels[level]; + + if (!a_level->types[type]) { + if (!alloc || VIR_ALLOC(a_level->types[type]) < 0) + return NULL; + } + + return a_level->types[type]; +} + +static virBitmapPtr * +virResctrlAllocFindMask(virResctrlAllocPtr *resctrl, + unsigned int level, + virCacheType type, + unsigned int cache, + bool alloc) +{ + virResctrlAllocPerTypePtr a_type =3D virResctrlAllocFindType(resctrl, = level, + type, alloc= ); + + if (!a_type) + return NULL; + + if (!a_type->masks) { + if (!alloc || VIR_ALLOC_N(a_type->masks, cache + 1) < 0) + return NULL; + a_type->nmasks =3D cache + 1; + } else if (a_type->nmasks <=3D cache) { + if (!alloc || VIR_EXPAND_N(a_type->masks, a_type->nmasks, + cache - a_type->nmasks + 1) < 0) + return NULL; + } + + return a_type->masks + cache; +} + +static unsigned long long * +virResctrlAllocFindSize(virResctrlAllocPtr *resctrl, + unsigned int level, + virCacheType type, + unsigned int cache, + bool alloc) +{ + virResctrlAllocPerTypePtr a_type =3D virResctrlAllocFindType(resctrl, = level, + type, alloc= ); + + if (!a_type) + return NULL; + + if (!a_type->sizes) { + if (!alloc || VIR_ALLOC_N(a_type->sizes, cache + 1) < 0) + return NULL; + a_type->nsizes =3D cache + 1; + } else if (a_type->nsizes <=3D cache) { + if (!alloc || VIR_EXPAND_N(a_type->sizes, a_type->nsizes, + cache - a_type->nsizes + 1) < 0) + return NULL; + } + + if (!a_type->sizes[cache]) { + if (!alloc || VIR_ALLOC(a_type->sizes[cache]) < 0) + return NULL; + } + + return a_type->sizes[cache]; +} + +int +virResctrlAllocUpdateMask(virResctrlAllocPtr *resctrl, + unsigned int level, + virCacheType type, + unsigned int cache, + virBitmapPtr mask) +{ + virBitmapPtr *found =3D virResctrlAllocFindMask(resctrl, level, type, = cache, + true); + + if (!found) + return -1; + + virBitmapFree(*found); + + *found =3D virBitmapNew(virBitmapSize(mask)); + if (!*found) + return -1; + + return virBitmapCopy(*found, mask); +} + +int +virResctrlAllocUpdateSize(virResctrlAllocPtr *resctrl, + unsigned int level, + virCacheType type, + unsigned int cache, + unsigned long long size) +{ + unsigned long long *found =3D virResctrlAllocFindSize(resctrl, level, = type, + cache, true); + + if (!found) + return -1; + + *found =3D size; + return 0; +} + +static bool +virResctrlAllocCheckCollision(virResctrlAllocPtr a, + unsigned int level, + virCacheType type, + unsigned int cache) +{ + /* If there is an allocation for type 'both', there can be no other + * allocation for the same cache */ + if (virResctrlAllocFindSize(&a, level, VIR_CACHE_TYPE_BOTH, cache, fal= se)) + return true; + + if (type =3D=3D VIR_CACHE_TYPE_BOTH) { + if (virResctrlAllocFindSize(&a, level, VIR_CACHE_TYPE_CODE, cache,= false)) + return true; + if (virResctrlAllocFindSize(&a, level, VIR_CACHE_TYPE_DATA, cache,= false)) + return true; + } + + /* And never two allocations for the same type */ + if (virResctrlAllocFindSize(&a, level, type, cache, false)) + return true; + + return false; +} + +int +virResctrlAllocSetSize(virResctrlAllocPtr *resctrl, + unsigned int level, + virCacheType type, + unsigned int cache, + unsigned long long size) +{ + if (virResctrlAllocCheckCollision(*resctrl, level, type, cache)) { + virReportError(VIR_ERR_XML_ERROR, + _("Colliding cache allocations for cache " + "level '%u' id '%u', type '%s'"), + level, cache, virCacheTypeToString(type)); + return -1; + } + + return virResctrlAllocUpdateSize(resctrl, level, type, cache, size); +} + +int +virResctrlAllocForeachSize(virResctrlAllocPtr resctrl, + virResctrlAllocForeachSizeCallback cb, + void *opaque) +{ + int ret =3D 0; + unsigned int level =3D 0; + unsigned int type =3D 0; + unsigned int cache =3D 0; + + if (!resctrl) + return 0; + + for (level =3D 0; level < resctrl->nlevels; level++) { + virResctrlAllocPerLevelPtr a_level =3D resctrl->levels[level]; + + if (!a_level) + continue; + + for (type =3D 0; type < VIR_CACHE_TYPE_LAST; type++) { + virResctrlAllocPerTypePtr a_type =3D a_level->types[type]; + + if (!a_type) + continue; + + for (cache =3D 0; cache < a_type->nsizes; cache++) { + unsigned long long *size =3D a_type->sizes[cache]; + + if (!size) + continue; + + ret =3D cb(level, type, cache, *size, opaque); + if (ret < 0) + return ret; + } + } + } + + return 0; +} + +int +virResctrlAllocSetID(virResctrlAllocPtr alloc, + const char *id) +{ + if (!id) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Resctrl allocation id cannot be NULL")); + return -1; + } + + return VIR_STRDUP(alloc->id, id); +} + +char * +virResctrlAllocFormat(virResctrlAllocPtr resctrl) +{ + virBuffer buf =3D VIR_BUFFER_INITIALIZER; + unsigned int level =3D 0; + unsigned int type =3D 0; + unsigned int cache =3D 0; + + if (!resctrl) + return NULL; + + for (level =3D 0; level < resctrl->nlevels; level++) { + virResctrlAllocPerLevelPtr a_level =3D resctrl->levels[level]; + + if (!a_level) + continue; + + for (type =3D 0; type < VIR_CACHE_TYPE_LAST; type++) { + virResctrlAllocPerTypePtr a_type =3D a_level->types[type]; + + if (!a_type) + continue; + + virBufferAsprintf(&buf, "L%u%s:", level, virResctrlTypeToStrin= g(type)); + + for (cache =3D 0; cache < a_type->nmasks; cache++) { + virBitmapPtr mask =3D a_type->masks[cache]; + char *mask_str =3D NULL; + + if (!mask) + continue; + + mask_str =3D virBitmapToString(mask, false, true); + if (!mask_str) { + virBufferFreeAndReset(&buf); + return NULL; + } + + virBufferAsprintf(&buf, "%u=3D%s;", cache, mask_str); + } + + virBufferTrim(&buf, ";", 1); + virBufferAddChar(&buf, '\n'); + } + } + + virBufferCheckError(&buf); + return virBufferContentAndReset(&buf); +} + +static int +virResctrlAllocParseProcessCache(virResctrlAllocPtr *resctrl, + unsigned int level, + virCacheType type, + char *cache) +{ + char *tmp =3D strchr(cache, '=3D'); + unsigned int cache_id =3D 0; + virBitmapPtr mask =3D NULL; + int ret =3D -1; + + if (!tmp) + return 0; + + *tmp =3D '\0'; + tmp++; + + if (virStrToLong_uip(cache, NULL, 10, &cache_id) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Invalid cache id '%s'"), cache); + return -1; + } + + mask =3D virBitmapNewString(tmp); + if (!mask) + return -1; + + if (virResctrlAllocUpdateMask(resctrl, level, type, cache_id, mask) < = 0) + goto cleanup; + + ret =3D 0; + cleanup: + virBitmapFree(mask); + return ret; +} + +static int +virResctrlAllocParseProcessLine(virResctrlAllocPtr *resctrl, + char *line) +{ + char **caches =3D NULL; + char *tmp =3D NULL; + unsigned int level =3D 0; + int type =3D -1; + size_t ncaches =3D 0; + size_t i =3D 0; + int ret =3D -1; + + /* Skip lines that don't concern caches, e.g. MB: etc. */ + if (line[0] !=3D 'L') + return 0; + + /* And lines that we can't parse too */ + tmp =3D strchr(line, ':'); + if (!tmp) + return 0; + + *tmp =3D '\0'; + tmp++; + + if (virStrToLong_uip(line + 1, &line, 10, &level) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Cannot parse resctrl schema level '%s'"), + line + 1); + return -1; + } + + type =3D virResctrlTypeFromString(line); + if (type < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Cannot parse resctrl schema level '%s'"), + line + 1); + return -1; + } + + caches =3D virStringSplitCount(tmp, ";", 0, &ncaches); + if (!caches) + return 0; + + for (i =3D 0; i < ncaches; i++) { + if (virResctrlAllocParseProcessCache(resctrl, level, type, caches[= i]) < 0) + goto cleanup; + } + + ret =3D 0; + cleanup: + virStringListFree(caches); + return ret; +} + +static int +virResctrlAllocParse(virResctrlAllocPtr *alloc, + const char *schemata) +{ + virResctrlAllocPtr tmp =3D NULL; + char **lines =3D NULL; + size_t nlines =3D 0; + size_t i =3D 0; + int ret =3D -1; + + lines =3D virStringSplitCount(schemata, "\n", 0, &nlines); + for (i =3D 0; i < nlines; i++) { + if (virResctrlAllocParseProcessLine(&tmp, lines[i]) < 0) + goto cleanup; + } + + *alloc =3D tmp; + tmp =3D NULL; + ret =3D 0; + cleanup: + virStringListFree(lines); + virObjectUnref(tmp); + return ret; +} + +static void +virResctrlAllocSubtractPerType(virResctrlAllocPerTypePtr a, + virResctrlAllocPerTypePtr b) +{ + size_t i =3D 0; + + if (!a || !b) + return; + + for (i =3D 0; i < a->nmasks && i < b->nmasks; ++i) { + if (a->masks[i] && b->masks[i]) + virBitmapSubtract(a->masks[i], b->masks[i]); + } +} + +static void +virResctrlAllocSubtract(virResctrlAllocPtr a, + virResctrlAllocPtr b) +{ + size_t i =3D 0; + size_t j =3D 0; + + if (!b) + return; + + for (i =3D 0; i < a->nlevels && b->nlevels; ++i) { + if (a->levels[i] && b->levels[i]) { + /* Here we rely on all the system allocations to use the same = types. + * We kind of _hope_ it's the case. If this is left here unti= l the + * review and someone finds it, then suggest only removing thi= s last + * sentence. */ + for (j =3D 0; j < VIR_CACHE_TYPE_LAST; j++) { + virResctrlAllocSubtractPerType(a->levels[i]->types[j], + b->levels[i]->types[j]); + } + } + } +} + +virResctrlAllocPtr +virResctrlAllocNewFromInfo(virResctrlInfoPtr info) +{ + size_t i =3D 0; + size_t j =3D 0; + size_t k =3D 0; + virResctrlAllocPtr ret =3D NULL; + virBitmapPtr mask =3D NULL; + + for (i =3D 0; i < info->nlevels; i++) { + virResctrlInfoPerLevelPtr i_level =3D info->levels[i]; + + if (!i_level) + continue; + + for (j =3D 0; j < VIR_CACHE_TYPE_LAST; j++) { + virResctrlInfoPerTypePtr i_type =3D i_level->types[j]; + + if (!i_type) + continue; + + virBitmapFree(mask); + mask =3D virBitmapNew(i_type->bits); + if (!mask) + goto error; + virBitmapSetAll(mask); + + for (k =3D 0; k <=3D i_type->max_cache_id; k++) { + if (virResctrlAllocUpdateMask(&ret, i, j, k, mask) < 0) + goto error; + } + } + } + + cleanup: + virBitmapFree(mask); + return ret; + error: + virObjectUnref(ret); + ret =3D NULL; + goto cleanup; +} + +virResctrlAllocPtr +virResctrlAllocGetFree(virResctrlInfoPtr resctrl) +{ + virResctrlAllocPtr ret =3D NULL; + virResctrlAllocPtr alloc =3D NULL; + virBitmapPtr mask =3D NULL; + struct dirent *ent =3D NULL; + DIR *dirp =3D NULL; + char *schemata =3D NULL; + int rv =3D -1; + + ret =3D virResctrlAllocNewFromInfo(resctrl); + if (!ret) + return NULL; + + if (virFileReadValueString(&schemata, + SYSFS_RESCTRL_PATH + "/schemata") < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Could not read schemata file for the default gro= up")); + goto error; + } + + if (virResctrlAllocParse(&alloc, schemata) < 0) + goto error; + if (!alloc) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("No schemata for default resctrlfs group")); + goto error; + } + virResctrlAllocSubtract(ret, alloc); + + if (virDirOpen(&dirp, SYSFS_RESCTRL_PATH) < 0) + goto error; + + while ((rv =3D virDirRead(dirp, &ent, SYSFS_RESCTRL_PATH)) > 0) { + if (ent->d_type !=3D DT_DIR) + continue; + + if (STREQ(ent->d_name, "info")) + continue; + + VIR_FREE(schemata); + if (virFileReadValueString(&schemata, + SYSFS_RESCTRL_PATH + "/%s/schemata", + ent->d_name) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Could not read schemata file for group %s"), + ent->d_name); + goto error; + } + + virObjectUnref(alloc); + alloc =3D NULL; + if (virResctrlAllocParse(&alloc, schemata) < 0) + goto error; + + virResctrlAllocSubtract(ret, alloc); + + VIR_FREE(schemata); + } + if (rv < 0) + goto error; + + cleanup: + virObjectUnref(alloc); + VIR_DIR_CLOSE(dirp); + VIR_FREE(schemata); + virBitmapFree(mask); + return ret; + + error: + virObjectUnref(ret); + ret =3D NULL; + goto cleanup; +} + +int +virResctrlAllocMasksAssign(virResctrlInfoPtr r_info, + virResctrlAllocPtr alloc, + void **save_ptr) +{ + int ret =3D -1; + unsigned int level =3D 0; + virResctrlAllocPtr alloc_free =3D NULL; + + if (!r_info) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("Resource control is not supported on this host")= ); + return -1; + } + + if (!save_ptr) { + alloc_free =3D virResctrlAllocGetFree(r_info); + } else { + if (!*save_ptr) + *save_ptr =3D virResctrlAllocGetFree(r_info); + + alloc_free =3D *save_ptr; + } + + if (!alloc_free) + return -1; + + for (level =3D 0; level < alloc->nlevels; level++) { + virResctrlAllocPerLevelPtr a_level =3D alloc->levels[level]; + virResctrlAllocPerLevelPtr f_level =3D NULL; + unsigned int type =3D 0; + + if (!a_level) + continue; + + if (level < alloc_free->nlevels) + f_level =3D alloc_free->levels[level]; + + if (!f_level) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("Cache level %d does not support tuning"), + level); + goto cleanup; + } + + for (type =3D 0; type < VIR_CACHE_TYPE_LAST; type++) { + virResctrlAllocPerTypePtr a_type =3D a_level->types[type]; + virResctrlAllocPerTypePtr f_type =3D f_level->types[type]; + unsigned int cache =3D 0; + + if (!a_type) + continue; + + if (!f_type) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("Cache level %d does not support tuning f= or " + "scope type '%s'"), + level, virCacheTypeToString(type)); + goto cleanup; + } + + for (cache =3D 0; cache < a_type->nsizes; cache++) { + unsigned long long *size =3D a_type->sizes[cache]; + virBitmapPtr a_mask =3D NULL; + virBitmapPtr f_mask =3D f_type->masks[cache]; + virResctrlInfoPerLevelPtr i_level =3D r_info->levels[level= ]; + virResctrlInfoPerTypePtr i_type =3D i_level->types[type]; + unsigned long long granularity; + unsigned long long need_bits; + size_t i =3D 0; + ssize_t pos =3D -1; + ssize_t last_bits =3D 0; + ssize_t last_pos =3D -1; + + if (!size) + continue; + + if (cache >=3D f_type->nmasks) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("Cache with id %u does not exists for= level %d"), + cache, level); + goto cleanup; + } + + f_mask =3D f_type->masks[cache]; + if (!f_mask) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("Cache level %d id %u does not suppor= t tuning for " + "scope type '%s'"), + level, cache, virCacheTypeToString(type= )); + goto cleanup; + } + + granularity =3D i_type->size / i_type->bits; + need_bits =3D *size / granularity; + + if (*size % granularity) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("Cache allocation of size %llu is not= " + "divisible by granularity %llu"), + *size, granularity); + goto cleanup; + } + + if (need_bits < i_type->min_cbm_bits) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("Cache allocation of size %llu is sma= ller " + "than the minimum allowed allocation = %llu"), + *size, granularity * i_type->min_cbm_bi= ts); + goto cleanup; + } + + while ((pos =3D virBitmapNextSetBit(f_mask, pos)) >=3D 0) { + ssize_t pos_clear =3D virBitmapNextClearBit(f_mask, po= s); + ssize_t bits; + + if (pos_clear < 0) + pos_clear =3D virBitmapSize(f_mask); + + bits =3D pos_clear - pos; + + /* Not enough bits, move on and skip all of them */ + if (bits < need_bits) { + pos =3D pos_clear; + continue; + } + + /* This fits perfectly */ + if (bits =3D=3D need_bits) { + last_pos =3D pos; + break; + } + + /* Remember the smaller region if we already found on = before */ + if (last_pos < 0 || (last_bits && bits < last_bits)) { + last_bits =3D bits; + last_pos =3D pos; + } + + pos =3D pos_clear; + } + + if (last_pos < 0) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("Not enough room for allocation of " + "%llu bytes for level %u cache %u " + "scope type '%s'"), + *size, level, cache, + virCacheTypeToString(type)); + goto cleanup; + } + + a_mask =3D virBitmapNew(i_type->bits); + for (i =3D last_pos; i < last_pos + need_bits; i++) { + ignore_value(virBitmapSetBit(a_mask, i)); + ignore_value(virBitmapClearBit(f_mask, i)); + } + + if (a_type->nmasks <=3D cache) { + if (VIR_EXPAND_N(a_type->masks, a_type->nmasks, + cache - a_type->nmasks + 1) < 0) { + virBitmapFree(a_mask); + goto cleanup; + } + } + a_type->masks[cache] =3D a_mask; + } + } + } + + ret =3D 0; + cleanup: + if (!save_ptr) + virObjectUnref(alloc_free); + return ret; +} + +/* This checks if the directory for the alloc exists. If not it tries to = create + * it and apply appropriate alloc settings. */ +int +virResctrlAllocCreate(virResctrlInfoPtr r_info, + virResctrlAllocPtr alloc, + const char *drivername, + const char *machinename) +{ + char *alloc_path =3D NULL; + char *schemata_path =3D NULL; + bool dir_created =3D false; + char *alloc_str =3D NULL; + int ret =3D -1; + int lockfd =3D -1; + + if (!alloc) + return 0; + + if (!alloc->path && + virAsprintf(&alloc->path, "%s/%s-%s-%s", + SYSFS_RESCTRL_PATH, drivername, machinename, alloc->id= ) < 0) + return -1; + + /* Check if this allocation was already created */ + if (virFileIsDir(alloc->path)) { + VIR_FREE(alloc_path); + return 0; + } + + if (virFileExists(alloc->path)) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Path '%s' for resctrl allocation exists and is n= ot a " + "directory"), alloc->path); + goto cleanup; + } + + lockfd =3D virResctrlLockWrite(); + if (lockfd < 0) + goto cleanup; + + if (virResctrlAllocMasksAssign(r_info, alloc, NULL) < 0) + goto cleanup; + + alloc_str =3D virResctrlAllocFormat(alloc); + if (!alloc_str) + return -1; + + if (virAsprintf(&schemata_path, "%s/schemata", alloc->path) < 0) + goto cleanup; + + if (virFileMakePath(alloc->path) < 0) { + virReportSystemError(errno, + _("Cannot create resctrl directory '%s'"), + alloc->path); + goto cleanup; + } + dir_created =3D true; + + if (virFileWriteStr(schemata_path, alloc_str, 0) < 0) { + virReportSystemError(errno, + _("Cannot write into schemata file '%s'"), + schemata_path); + goto cleanup; + } + + ret =3D 0; + cleanup: + if (ret < 0 && dir_created) + rmdir(alloc->path); + virResctrlUnlock(lockfd); + VIR_FREE(alloc_str); + VIR_FREE(alloc_path); + VIR_FREE(schemata_path); + return ret; +} + +int +virResctrlAllocAddPID(virResctrlAllocPtr alloc, + pid_t pid) +{ + char *tasks =3D NULL; + char *pidstr =3D NULL; + int ret =3D 0; + + if (!alloc->path) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Cannot add pid to non-existing resctrl allocatio= n")); + return -1; + } + + if (virAsprintf(&tasks, "%s/tasks", alloc->path) < 0) + return -1; + + if (virAsprintf(&pidstr, "%lld", (long long int) pid) < 0) + goto cleanup; + + if (virFileWriteStr(tasks, pidstr, 0) < 0) { + virReportSystemError(errno, + _("Cannot write pid in tasks file '%s'"), + tasks); + goto cleanup; + } + + ret =3D 0; + cleanup: + VIR_FREE(tasks); + VIR_FREE(pidstr); + return ret; +} + +int +virResctrlAllocRemove(virResctrlAllocPtr alloc) +{ + int ret =3D 0; + + if (!alloc->path) + return 0; + + VIR_DEBUG("Removing resctrl allocation %s", alloc->path); + if (rmdir(alloc->path) !=3D 0 && errno !=3D ENOENT) { + ret =3D -errno; + VIR_ERROR(_("Unable to remove %s (%d)"), alloc->path, errno); + } + + return ret; +} diff --git a/src/util/virresctrl.h b/src/util/virresctrl.h index c4df88f23c3a..b233eca41c03 100644 --- a/src/util/virresctrl.h +++ b/src/util/virresctrl.h @@ -62,4 +62,63 @@ virResctrlInfoGetCache(virResctrlInfoPtr resctrl, size_t *ncontrols, virResctrlInfoPerCachePtr **controls); =20 +/* Alloc-related things */ +typedef struct _virResctrlAlloc virResctrlAlloc; +typedef virResctrlAlloc *virResctrlAllocPtr; + +typedef int virResctrlAllocForeachSizeCallback(unsigned int level, + virCacheType type, + unsigned int cache, + unsigned long long size, + void *opaque); + +virResctrlAllocPtr +virResctrlAllocNewFromInfo(virResctrlInfoPtr info); + +int +virResctrlAllocUpdateSize(virResctrlAllocPtr *resctrl, + unsigned int level, + virCacheType type, + unsigned int cache, + unsigned long long size); + +int +virResctrlAllocUpdateMask(virResctrlAllocPtr *resctrl, + unsigned int level, + virCacheType type, + unsigned int cache, + virBitmapPtr mask); + +int +virResctrlAllocSetSize(virResctrlAllocPtr *resctrl, + unsigned int level, + virCacheType type, + unsigned int cache, + unsigned long long size); + +int +virResctrlAllocForeachSize(virResctrlAllocPtr resctrl, + virResctrlAllocForeachSizeCallback cb, + void *opaque); + +int +virResctrlAllocSetID(virResctrlAllocPtr alloc, + const char *id); + +char * +virResctrlAllocFormat(virResctrlAllocPtr alloc); + +int +virResctrlAllocCreate(virResctrlInfoPtr r_info, + virResctrlAllocPtr alloc, + const char *drivername, + const char *machinename); + +int +virResctrlAllocAddPID(virResctrlAllocPtr alloc, + pid_t pid); + +int +virResctrlAllocRemove(virResctrlAllocPtr alloc); + #endif /* __VIR_RESCTRL_H__ */ diff --git a/src/util/virresctrlpriv.h b/src/util/virresctrlpriv.h new file mode 100644 index 000000000000..4255ad496302 --- /dev/null +++ b/src/util/virresctrlpriv.h @@ -0,0 +1,32 @@ +/* + * virresctrlpriv.h: + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * . + */ + +#ifndef __VIR_RESCTRL_PRIV_H__ +# define __VIR_RESCTRL_PRIV_H__ + +# include "virresctrl.h" + +virResctrlAllocPtr +virResctrlAllocGetFree(virResctrlInfoPtr resctrl); + +int +virResctrlAllocMasksAssign(virResctrlInfoPtr r_info, + virResctrlAllocPtr alloc, + void **save_ptr); + +#endif /* __VIR_RESCTRL_PRIV_H__ */ --=20 2.15.0 -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list