If we reorder the functions properly, we only need to have few stub functions for non-Linux platforms and it is also nicer to look at and read if they are in one place with only one preprocessor condition. The order after this patch is: - Class allocation and Static functions (used by everything, static dependencies) - Linux-only functions - Non-Linux stubs - Exported functions (as they rely on all previous functions) Signed-off-by: Martin Kletzander <mkletzan@xxxxxxxxxx> --- src/libvirt_linux.syms | 3 + src/libvirt_private.syms | 1 - src/util/virresctrl.c | 939 +++++++++++++++++++++++------------------------ 3 files changed, 459 insertions(+), 484 deletions(-) diff --git a/src/libvirt_linux.syms b/src/libvirt_linux.syms index 5fa2c790efc1..27425e4bbebb 100644 --- a/src/libvirt_linux.syms +++ b/src/libvirt_linux.syms @@ -9,6 +9,9 @@ virHostCPUGetSiblingsList; virHostCPUGetSocket; virHostCPUGetStatsLinux; +# util/virresctrl.h +virResctrlAllocGetUnused; + # Let emacs know we want case-insensitive sorting # Local Variables: # sort-fold-case: t diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 9339c2c3259d..21ec8d8c8c34 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -2564,7 +2564,6 @@ virResctrlAllocDeterminePath; virResctrlAllocForeachSize; virResctrlAllocFormat; virResctrlAllocGetID; -virResctrlAllocGetUnused; virResctrlAllocNew; virResctrlAllocRemove; virResctrlAllocSetID; diff --git a/src/util/virresctrl.c b/src/util/virresctrl.c index fefca32a6710..cab7570f7b8e 100644 --- a/src/util/virresctrl.c +++ b/src/util/virresctrl.c @@ -294,346 +294,6 @@ virResctrlAllocNew(void) } -/* Common functions */ -#ifdef __linux__ -static int -virResctrlLockInternal(int op) -{ - int fd = open(SYSFS_RESCTRL_PATH, O_DIRECTORY | O_CLOEXEC); - - if (fd < 0) { - virReportSystemError(errno, "%s", _("Cannot open resctrl")); - return -1; - } - - if (flock(fd, op) < 0) { - virReportSystemError(errno, "%s", _("Cannot lock resctrl")); - VIR_FORCE_CLOSE(fd); - return -1; - } - - return fd; -} - - -static inline int -virResctrlLockWrite(void) -{ - return virResctrlLockInternal(LOCK_EX); -} - -#else - -static inline int -virResctrlLockWrite(void) -{ - virReportSystemError(ENOSYS, "%s", - _("resctrl not supported on this platform")); - return -1; -} - -#endif - - - - -static int -virResctrlUnlock(int fd) -{ - if (fd == -1) - return 0; - -#ifdef __linux__ - /* The lock gets unlocked by closing the fd, which we need to do anyway in - * order to clean up properly */ - if (VIR_CLOSE(fd) < 0) { - virReportSystemError(errno, "%s", _("Cannot close resctrl")); - - /* Trying to save the already broken */ - if (flock(fd, LOCK_UN) < 0) - virReportSystemError(errno, "%s", _("Cannot unlock resctrl")); - return -1; - } -#endif /* ! __linux__ */ - - return 0; -} - - -/* Info-related functions */ -static bool -virResctrlInfoIsEmpty(virResctrlInfoPtr resctrl) -{ - size_t i = 0; - size_t j = 0; - - if (!resctrl) - return true; - - for (i = 0; i < resctrl->nlevels; i++) { - virResctrlInfoPerLevelPtr i_level = resctrl->levels[i]; - - if (!i_level) - continue; - - for (j = 0; j < VIR_CACHE_TYPE_LAST; j++) { - if (i_level->types[j]) - return false; - } - } - - return true; -} - - -#ifdef __linux__ - -int -virResctrlGetInfo(virResctrlInfoPtr resctrl) -{ - DIR *dirp = NULL; - char *endptr = NULL; - char *tmp_str = NULL; - int ret = -1; - int rv = -1; - int type = 0; - struct dirent *ent = NULL; - unsigned int level = 0; - virResctrlInfoPerLevelPtr i_level = NULL; - virResctrlInfoPerTypePtr i_type = NULL; - - rv = virDirOpenIfExists(&dirp, SYSFS_RESCTRL_PATH "/info"); - if (rv <= 0) { - ret = rv; - goto cleanup; - } - - while ((rv = virDirRead(dirp, &ent, SYSFS_RESCTRL_PATH "/info")) > 0) { - VIR_DEBUG("Parsing info type '%s'", ent->d_name); - if (ent->d_name[0] != 'L') - continue; - - if (virStrToLong_uip(ent->d_name + 1, &endptr, 10, &level) < 0) { - VIR_DEBUG("Cannot parse resctrl cache info level '%s'", ent->d_name + 1); - continue; - } - - type = virResctrlTypeFromString(endptr); - if (type < 0) { - VIR_DEBUG("Cannot parse resctrl cache info type '%s'", endptr); - continue; - } - - if (VIR_ALLOC(i_type) < 0) - goto cleanup; - - i_type->control.scope = type; - - rv = virFileReadValueUint(&i_type->control.max_allocation, - SYSFS_RESCTRL_PATH "/info/%s/num_closids", - ent->d_name); - if (rv == -2) { - /* The file doesn't exist, so it's unusable for us, - * but we can scan further */ - VIR_WARN("The path '" SYSFS_RESCTRL_PATH "/info/%s/num_closids' " - "does not exist", - ent->d_name); - } else if (rv < 0) { - /* Other failures are fatal, so just quit */ - goto cleanup; - } - - rv = virFileReadValueString(&i_type->cbm_mask, - SYSFS_RESCTRL_PATH - "/info/%s/cbm_mask", - ent->d_name); - if (rv == -2) { - /* If the previous file exists, so should this one. Hence -2 is - * fatal in this case as well (errors out in next condition) - the - * kernel interface might've changed too much or something else is - * wrong. */ - virReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("Cannot get cbm_mask from resctrl cache info")); - } - if (rv < 0) - goto cleanup; - - virStringTrimOptionalNewline(i_type->cbm_mask); - - rv = virFileReadValueUint(&i_type->min_cbm_bits, - SYSFS_RESCTRL_PATH "/info/%s/min_cbm_bits", - ent->d_name); - if (rv == -2) - virReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("Cannot get min_cbm_bits from resctrl cache info")); - if (rv < 0) - goto cleanup; - - if (resctrl->nlevels <= level && - VIR_EXPAND_N(resctrl->levels, resctrl->nlevels, - level - resctrl->nlevels + 1) < 0) - goto cleanup; - - if (!resctrl->levels[level]) { - virResctrlInfoPerTypePtr *types = NULL; - - if (VIR_ALLOC_N(types, VIR_CACHE_TYPE_LAST) < 0) - goto cleanup; - - if (VIR_ALLOC(resctrl->levels[level]) < 0) { - VIR_FREE(types); - goto cleanup; - } - resctrl->levels[level]->types = types; - } - - i_level = resctrl->levels[level]; - - if (i_level->types[type]) { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("Duplicate cache type in resctrl for level %u"), - level); - goto cleanup; - } - - for (tmp_str = i_type->cbm_mask; *tmp_str != '\0'; tmp_str++) { - if (!c_isxdigit(*tmp_str)) { - virReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("Cannot parse cbm_mask from resctrl cache info")); - goto cleanup; - } - - i_type->bits += count_one_bits(virHexToBin(*tmp_str)); - } - - VIR_STEAL_PTR(i_level->types[type], i_type); - } - - ret = 0; - cleanup: - VIR_DIR_CLOSE(dirp); - if (i_type) - VIR_FREE(i_type->cbm_mask); - VIR_FREE(i_type); - return ret; -} - -#else /* ! __linux__ */ - -int -virResctrlGetInfo(virResctrlInfoPtr resctrl ATTRIBUTE_UNUSED) -{ - virReportSystemError(ENOSYS, "%s", - _("Cache tune not supported on this platform")); - return -1; -} - -#endif /* ! __linux__ */ - - -int -virResctrlInfoGetCache(virResctrlInfoPtr resctrl, - unsigned int level, - unsigned long long size, - size_t *ncontrols, - virResctrlInfoPerCachePtr **controls) -{ - virResctrlInfoPerLevelPtr i_level = NULL; - virResctrlInfoPerTypePtr i_type = NULL; - size_t i = 0; - int ret = -1; - - if (virResctrlInfoIsEmpty(resctrl)) - return 0; - - if (level >= resctrl->nlevels) - return 0; - - i_level = resctrl->levels[level]; - if (!i_level) - return 0; - - for (i = 0; i < VIR_CACHE_TYPE_LAST; i++) { - i_type = i_level->types[i]; - if (!i_type) - continue; - - /* Let's take the opportunity to update our internal information about - * the cache size */ - if (!i_type->size) { - i_type->size = size; - i_type->control.granularity = size / i_type->bits; - if (i_type->min_cbm_bits != 1) - i_type->control.min = i_type->min_cbm_bits * i_type->control.granularity; - } else { - if (i_type->size != size) { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("level %u cache size %llu does not match " - "expected size %llu"), - level, i_type->size, size); - goto error; - } - i_type->max_cache_id++; - } - - if (VIR_EXPAND_N(*controls, *ncontrols, 1) < 0) - goto error; - if (VIR_ALLOC((*controls)[*ncontrols - 1]) < 0) - goto error; - - memcpy((*controls)[*ncontrols - 1], &i_type->control, sizeof(i_type->control)); - } - - ret = 0; - cleanup: - return ret; - error: - while (*ncontrols) - VIR_FREE((*controls)[--*ncontrols]); - VIR_FREE(*controls); - goto cleanup; -} - - -/* Alloc-related functions */ -bool -virResctrlAllocIsEmpty(virResctrlAllocPtr resctrl) -{ - size_t i = 0; - size_t j = 0; - size_t k = 0; - - if (!resctrl) - return true; - - for (i = 0; i < resctrl->nlevels; i++) { - virResctrlAllocPerLevelPtr a_level = resctrl->levels[i]; - - if (!a_level) - continue; - - for (j = 0; j < VIR_CACHE_TYPE_LAST; j++) { - virResctrlAllocPerTypePtr a_type = a_level->types[j]; - - if (!a_type) - continue; - - for (k = 0; k < a_type->nsizes; k++) { - if (a_type->sizes[k]) - return false; - } - - for (k = 0; k < a_type->nmasks; k++) { - if (a_type->masks[k]) - return false; - } - } - } - - return true; -} - - static virResctrlAllocPerTypePtr virResctrlAllocGetType(virResctrlAllocPtr resctrl, unsigned int level, @@ -667,38 +327,6 @@ virResctrlAllocGetType(virResctrlAllocPtr resctrl, } -#ifdef __linux__ - -static int -virResctrlAllocUpdateMask(virResctrlAllocPtr resctrl, - unsigned int level, - virCacheType type, - unsigned int cache, - virBitmapPtr mask) -{ - virResctrlAllocPerTypePtr a_type = virResctrlAllocGetType(resctrl, level, type); - - if (!a_type) - return -1; - - if (a_type->nmasks <= cache && - VIR_EXPAND_N(a_type->masks, a_type->nmasks, - cache - a_type->nmasks + 1) < 0) - return -1; - - if (!a_type->masks[cache]) { - a_type->masks[cache] = virBitmapNew(virBitmapSize(mask)); - - if (!a_type->masks[cache]) - return -1; - } - - return virBitmapCopy(a_type->masks[cache], mask); -} - -#endif - - static int virResctrlAllocUpdateSize(virResctrlAllocPtr resctrl, unsigned int level, @@ -725,6 +353,31 @@ virResctrlAllocUpdateSize(virResctrlAllocPtr resctrl, } +static bool +virResctrlInfoIsEmpty(virResctrlInfoPtr resctrl) +{ + size_t i = 0; + size_t j = 0; + + if (!resctrl) + return true; + + for (i = 0; i < resctrl->nlevels; i++) { + virResctrlInfoPerLevelPtr i_level = resctrl->levels[i]; + + if (!i_level) + continue; + + for (j = 0; j < VIR_CACHE_TYPE_LAST; j++) { + if (i_level->types[j]) + return false; + } + } + + return true; +} + + /* * Check if there is an allocation for this level/type/cache already. Called * before updating the structure. VIR_CACHE_TYPE_BOTH collides with any type, @@ -775,150 +428,223 @@ virResctrlAllocCheckCollision(virResctrlAllocPtr alloc, } else { a_type = a_level->types[type]; - if (a_type && a_type->nsizes > cache && a_type->sizes[cache]) - return true; + if (a_type && a_type->nsizes > cache && a_type->sizes[cache]) + return true; + } + + return false; +} + + +#ifdef __linux__ + +static int +virResctrlLockInternal(int op) +{ + int fd = open(SYSFS_RESCTRL_PATH, O_DIRECTORY | O_CLOEXEC); + + if (fd < 0) { + virReportSystemError(errno, "%s", _("Cannot open resctrl")); + return -1; + } + + if (flock(fd, op) < 0) { + virReportSystemError(errno, "%s", _("Cannot lock resctrl")); + VIR_FORCE_CLOSE(fd); + return -1; } - return false; + return fd; } -int -virResctrlAllocSetSize(virResctrlAllocPtr resctrl, - unsigned int level, - virCacheType type, - unsigned int cache, - unsigned long long size) +static inline int +virResctrlLockWrite(void) { - 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 virResctrlLockInternal(LOCK_EX); +} + + +static int +virResctrlUnlock(int fd) +{ + if (fd == -1) + return 0; + + /* The lock gets unlocked by closing the fd, which we need to do anyway in + * order to clean up properly */ + if (VIR_CLOSE(fd) < 0) { + virReportSystemError(errno, "%s", _("Cannot close resctrl")); + + /* Trying to save the already broken */ + if (flock(fd, LOCK_UN) < 0) + virReportSystemError(errno, "%s", _("Cannot unlock resctrl")); return -1; } - return virResctrlAllocUpdateSize(resctrl, level, type, cache, size); + return 0; } int -virResctrlAllocForeachSize(virResctrlAllocPtr resctrl, - virResctrlAllocForeachSizeCallback cb, - void *opaque) +virResctrlGetInfo(virResctrlInfoPtr resctrl) { - int ret = 0; + DIR *dirp = NULL; + char *endptr = NULL; + char *tmp_str = NULL; + int ret = -1; + int rv = -1; + int type = 0; + struct dirent *ent = NULL; unsigned int level = 0; - unsigned int type = 0; - unsigned int cache = 0; - - if (!resctrl) - return 0; + virResctrlInfoPerLevelPtr i_level = NULL; + virResctrlInfoPerTypePtr i_type = NULL; - for (level = 0; level < resctrl->nlevels; level++) { - virResctrlAllocPerLevelPtr a_level = resctrl->levels[level]; + rv = virDirOpenIfExists(&dirp, SYSFS_RESCTRL_PATH "/info"); + if (rv <= 0) { + ret = rv; + goto cleanup; + } - if (!a_level) + while ((rv = virDirRead(dirp, &ent, SYSFS_RESCTRL_PATH "/info")) > 0) { + VIR_DEBUG("Parsing info type '%s'", ent->d_name); + if (ent->d_name[0] != 'L') continue; - for (type = 0; type < VIR_CACHE_TYPE_LAST; type++) { - virResctrlAllocPerTypePtr a_type = a_level->types[type]; + if (virStrToLong_uip(ent->d_name + 1, &endptr, 10, &level) < 0) { + VIR_DEBUG("Cannot parse resctrl cache info level '%s'", ent->d_name + 1); + continue; + } - if (!a_type) - continue; + type = virResctrlTypeFromString(endptr); + if (type < 0) { + VIR_DEBUG("Cannot parse resctrl cache info type '%s'", endptr); + continue; + } - for (cache = 0; cache < a_type->nsizes; cache++) { - unsigned long long *size = a_type->sizes[cache]; + if (VIR_ALLOC(i_type) < 0) + goto cleanup; - if (!size) - continue; + i_type->control.scope = type; - ret = cb(level, type, cache, *size, opaque); - if (ret < 0) - return ret; - } + rv = virFileReadValueUint(&i_type->control.max_allocation, + SYSFS_RESCTRL_PATH "/info/%s/num_closids", + ent->d_name); + if (rv == -2) { + /* The file doesn't exist, so it's unusable for us, + * but we can scan further */ + VIR_WARN("The path '" SYSFS_RESCTRL_PATH "/info/%s/num_closids' " + "does not exist", + ent->d_name); + } else if (rv < 0) { + /* Other failures are fatal, so just quit */ + goto cleanup; } - } - return 0; -} + rv = virFileReadValueString(&i_type->cbm_mask, + SYSFS_RESCTRL_PATH + "/info/%s/cbm_mask", + ent->d_name); + if (rv == -2) { + /* If the previous file exists, so should this one. Hence -2 is + * fatal in this case as well (errors out in next condition) - the + * kernel interface might've changed too much or something else is + * wrong. */ + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Cannot get cbm_mask from resctrl cache info")); + } + if (rv < 0) + goto cleanup; + virStringTrimOptionalNewline(i_type->cbm_mask); -int -virResctrlAllocSetID(virResctrlAllocPtr alloc, - const char *id) -{ - if (!id) { - virReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("Resctrl allocation 'id' cannot be NULL")); - return -1; - } + rv = virFileReadValueUint(&i_type->min_cbm_bits, + SYSFS_RESCTRL_PATH "/info/%s/min_cbm_bits", + ent->d_name); + if (rv == -2) + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Cannot get min_cbm_bits from resctrl cache info")); + if (rv < 0) + goto cleanup; - return VIR_STRDUP(alloc->id, id); -} + if (resctrl->nlevels <= level && + VIR_EXPAND_N(resctrl->levels, resctrl->nlevels, + level - resctrl->nlevels + 1) < 0) + goto cleanup; + if (!resctrl->levels[level]) { + virResctrlInfoPerTypePtr *types = NULL; -const char * -virResctrlAllocGetID(virResctrlAllocPtr alloc) -{ - return alloc->id; -} + if (VIR_ALLOC_N(types, VIR_CACHE_TYPE_LAST) < 0) + goto cleanup; + if (VIR_ALLOC(resctrl->levels[level]) < 0) { + VIR_FREE(types); + goto cleanup; + } + resctrl->levels[level]->types = types; + } -char * -virResctrlAllocFormat(virResctrlAllocPtr resctrl) -{ - virBuffer buf = VIR_BUFFER_INITIALIZER; - unsigned int level = 0; - unsigned int type = 0; - unsigned int cache = 0; + i_level = resctrl->levels[level]; - if (!resctrl) - return NULL; + if (i_level->types[type]) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Duplicate cache type in resctrl for level %u"), + level); + goto cleanup; + } - for (level = 0; level < resctrl->nlevels; level++) { - virResctrlAllocPerLevelPtr a_level = resctrl->levels[level]; + for (tmp_str = i_type->cbm_mask; *tmp_str != '\0'; tmp_str++) { + if (!c_isxdigit(*tmp_str)) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Cannot parse cbm_mask from resctrl cache info")); + goto cleanup; + } - if (!a_level) - continue; + i_type->bits += count_one_bits(virHexToBin(*tmp_str)); + } - for (type = 0; type < VIR_CACHE_TYPE_LAST; type++) { - virResctrlAllocPerTypePtr a_type = a_level->types[type]; + VIR_STEAL_PTR(i_level->types[type], i_type); + } - if (!a_type) - continue; + ret = 0; + cleanup: + VIR_DIR_CLOSE(dirp); + if (i_type) + VIR_FREE(i_type->cbm_mask); + VIR_FREE(i_type); + return ret; +} - virBufferAsprintf(&buf, "L%u%s:", level, virResctrlTypeToString(type)); - for (cache = 0; cache < a_type->nmasks; cache++) { - virBitmapPtr mask = a_type->masks[cache]; - char *mask_str = NULL; +static int +virResctrlAllocUpdateMask(virResctrlAllocPtr resctrl, + unsigned int level, + virCacheType type, + unsigned int cache, + virBitmapPtr mask) +{ + virResctrlAllocPerTypePtr a_type = virResctrlAllocGetType(resctrl, level, type); - if (!mask) - continue; + if (!a_type) + return -1; - mask_str = virBitmapToString(mask, false, true); - if (!mask_str) { - virBufferFreeAndReset(&buf); - return NULL; - } + if (a_type->nmasks <= cache && + VIR_EXPAND_N(a_type->masks, a_type->nmasks, + cache - a_type->nmasks + 1) < 0) + return -1; - virBufferAsprintf(&buf, "%u=%s;", cache, mask_str); - VIR_FREE(mask_str); - } + if (!a_type->masks[cache]) { + a_type->masks[cache] = virBitmapNew(virBitmapSize(mask)); - virBufferTrim(&buf, ";", 1); - virBufferAddChar(&buf, '\n'); - } + if (!a_type->masks[cache]) + return -1; } - virBufferCheckError(&buf); - return virBufferContentAndReset(&buf); + return virBitmapCopy(a_type->masks[cache], mask); } -#ifdef __linux__ - static int virResctrlAllocParseProcessCache(virResctrlInfoPtr resctrl, virResctrlAllocPtr alloc, @@ -1171,6 +897,7 @@ virResctrlAllocNewFromInfo(virResctrlInfoPtr info) goto cleanup; } + /* * This function creates an allocation that represents all unused parts of all * caches in the system. It uses virResctrlInfo for creating a new full @@ -1238,23 +965,11 @@ virResctrlAllocGetUnused(virResctrlInfoPtr resctrl) return ret; error: - virObjectUnref(ret); - ret = NULL; - goto cleanup; -} - -#else /* ! __linux__ */ - -virResctrlAllocPtr -virResctrlAllocGetUnused(virResctrlInfoPtr resctrl ATTRIBUTE_UNUSED) -{ - virReportSystemError(ENOSYS, "%s", - _("Cache tune not supported on this platform")); - return NULL; + virObjectUnref(ret); + ret = NULL; + goto cleanup; } -#endif /* ! __linux__ */ - /* * Given the information about requested allocation type `a_type`, the host @@ -1591,6 +1306,264 @@ virResctrlAllocCreate(virResctrlInfoPtr resctrl, return ret; } +#else /* ! __linux__ */ + +int +virResctrlGetInfo(virResctrlInfoPtr resctrl ATTRIBUTE_UNUSED) +{ + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("Resource control is not supported on this host")); + return -1; +} + +int +virResctrlAllocCreate(virResctrlInfoPtr resctrl ATTRIBUTE_UNUSED, + virResctrlAllocPtr alloc ATTRIBUTE_UNUSED, + const char *machinename ATTRIBUTE_UNUSED) +{ + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("Resource control is not supported on this host")); + return -1; +} + +#endif /* ! __linux__ */ + + +int +virResctrlInfoGetCache(virResctrlInfoPtr resctrl, + unsigned int level, + unsigned long long size, + size_t *ncontrols, + virResctrlInfoPerCachePtr **controls) +{ + virResctrlInfoPerLevelPtr i_level = NULL; + virResctrlInfoPerTypePtr i_type = NULL; + size_t i = 0; + int ret = -1; + + if (virResctrlInfoIsEmpty(resctrl)) + return 0; + + if (level >= resctrl->nlevels) + return 0; + + i_level = resctrl->levels[level]; + if (!i_level) + return 0; + + for (i = 0; i < VIR_CACHE_TYPE_LAST; i++) { + i_type = i_level->types[i]; + if (!i_type) + continue; + + /* Let's take the opportunity to update our internal information about + * the cache size */ + if (!i_type->size) { + i_type->size = size; + i_type->control.granularity = size / i_type->bits; + if (i_type->min_cbm_bits != 1) + i_type->control.min = i_type->min_cbm_bits * i_type->control.granularity; + } else { + if (i_type->size != size) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("level %u cache size %llu does not match " + "expected size %llu"), + level, i_type->size, size); + goto error; + } + i_type->max_cache_id++; + } + + if (VIR_EXPAND_N(*controls, *ncontrols, 1) < 0) + goto error; + if (VIR_ALLOC((*controls)[*ncontrols - 1]) < 0) + goto error; + + memcpy((*controls)[*ncontrols - 1], &i_type->control, sizeof(i_type->control)); + } + + ret = 0; + cleanup: + return ret; + error: + while (*ncontrols) + VIR_FREE((*controls)[--*ncontrols]); + VIR_FREE(*controls); + goto cleanup; +} + + +bool +virResctrlAllocIsEmpty(virResctrlAllocPtr resctrl) +{ + size_t i = 0; + size_t j = 0; + size_t k = 0; + + if (!resctrl) + return true; + + for (i = 0; i < resctrl->nlevels; i++) { + virResctrlAllocPerLevelPtr a_level = resctrl->levels[i]; + + if (!a_level) + continue; + + for (j = 0; j < VIR_CACHE_TYPE_LAST; j++) { + virResctrlAllocPerTypePtr a_type = a_level->types[j]; + + if (!a_type) + continue; + + for (k = 0; k < a_type->nsizes; k++) { + if (a_type->sizes[k]) + return false; + } + + for (k = 0; k < a_type->nmasks; k++) { + if (a_type->masks[k]) + return false; + } + } + } + + return true; +} + + +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 = 0; + unsigned int level = 0; + unsigned int type = 0; + unsigned int cache = 0; + + if (!resctrl) + return 0; + + for (level = 0; level < resctrl->nlevels; level++) { + virResctrlAllocPerLevelPtr a_level = resctrl->levels[level]; + + if (!a_level) + continue; + + for (type = 0; type < VIR_CACHE_TYPE_LAST; type++) { + virResctrlAllocPerTypePtr a_type = a_level->types[type]; + + if (!a_type) + continue; + + for (cache = 0; cache < a_type->nsizes; cache++) { + unsigned long long *size = a_type->sizes[cache]; + + if (!size) + continue; + + ret = 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); +} + + +const char * +virResctrlAllocGetID(virResctrlAllocPtr alloc) +{ + return alloc->id; +} + + +char * +virResctrlAllocFormat(virResctrlAllocPtr resctrl) +{ + virBuffer buf = VIR_BUFFER_INITIALIZER; + unsigned int level = 0; + unsigned int type = 0; + unsigned int cache = 0; + + if (!resctrl) + return NULL; + + for (level = 0; level < resctrl->nlevels; level++) { + virResctrlAllocPerLevelPtr a_level = resctrl->levels[level]; + + if (!a_level) + continue; + + for (type = 0; type < VIR_CACHE_TYPE_LAST; type++) { + virResctrlAllocPerTypePtr a_type = a_level->types[type]; + + if (!a_type) + continue; + + virBufferAsprintf(&buf, "L%u%s:", level, virResctrlTypeToString(type)); + + for (cache = 0; cache < a_type->nmasks; cache++) { + virBitmapPtr mask = a_type->masks[cache]; + char *mask_str = NULL; + + if (!mask) + continue; + + mask_str = virBitmapToString(mask, false, true); + if (!mask_str) { + virBufferFreeAndReset(&buf); + return NULL; + } + + virBufferAsprintf(&buf, "%u=%s;", cache, mask_str); + VIR_FREE(mask_str); + } + + virBufferTrim(&buf, ";", 1); + virBufferAddChar(&buf, '\n'); + } + } + + virBufferCheckError(&buf); + return virBufferContentAndReset(&buf); +} + int virResctrlAllocAddPID(virResctrlAllocPtr alloc, -- 2.16.1 -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list