[libvirt] [PATCH 16/21] resctrl: Add functions to work with resctrl allocations

John Ferlan jferlan at redhat.com
Wed Nov 15 23:02:37 UTC 2017


[...]

>>  /* 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)
>> +{
> 
> I don't like this implementation, it's too complex and it does two
> different things based on a bool attribute.  I see the benefit that
> it's convenient but IMHO it's ugly.

Well I stared at it a *long* time before coming to the conclusion that
as odd looking as it is - it does what it's supposed to do in a unique
way compared to other libvirt functions. While yes a bit ugly, it didn't
feel too complex once I got the basic premise.

I also kept thinking this whole sequence relies on the "original caller"
(e.g. where &resctrl originally gets passed) to be sure on failure to
Unref - tracing back to that was a challenge. Thinking about these
functions being called in the middle of some other code - I dunno.

Still Like I pointed out - it would help *a lot* to document WTF is
going on!

Returning the "address of" some location based on array entry/offset
does cause some concern - I was able to eventually convince myself it
works... Although I must say I studied virResctrlAllocUpdateMask for
quite a bit trying to determine whether what it claimed to do was
actually being done.

John

> 
> The only call path that doesn't need allocation is from
> virResctrlAllocCheckCollision().  The remaining two calls
> virResctrlAllocUpdateMask() and virResctrlAllocUpdateSize() needs
> to allocate the internal structures of *virResctrlAllocPtr* object.
> 
> Another point is that there is no need to have this function create
> new *virResctrlAllocPtr* object on demand, I would prefer creating
> that object in advance before we even start filling all the data.
> 
>> +    virResctrlAllocPerLevelPtr a_level = NULL;
>> +    virResctrlAllocPtr tmp = NULL;
>> +
>> +    if (!*resctrl) {
>> +        if (!alloc || !(*resctrl = virResctrlAllocNew()))
>> +            return NULL;
>> +    }
>> +
>> +    tmp = *resctrl;
>> +
>> +    /* Per-level array */
>> +    if (tmp->nlevels <= 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 = 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)
>> +{
> 
> The code of this function can be merged into virResctrlAllocUpdateMask()
> and again only allocate the structures and set the mask.  Currently
> there is no need for "Find" function if we will need it in the future
> it should definitely only find the mask, not allocate it.
> 
>> +    virResctrlAllocPerTypePtr a_type = 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 = cache + 1;
>> +    } else if (a_type->nmasks <= 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 = virResctrlAllocFindType(resctrl, level,
>> +                                                               type, alloc);
> 
> Same as for virResctrlAllocFindMask().  With the exception that we
> actually need lookup function so create a one, that will only check
> whether there is some size set or not.
> 
>> +
>> +    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 = cache + 1;
>> +    } else if (a_type->nsizes <= 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 = virResctrlAllocFindMask(resctrl, level, type, cache,
>> +                                                  true);
>> +
>> +    if (!found)
>> +        return -1;
>> +
>> +    virBitmapFree(*found);
>> +
>> +    *found = 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 = virResctrlAllocFindSize(resctrl, level, type,
>> +                                                        cache, true);
>> +
>> +    if (!found)
>> +        return -1;
>> +
>> +    *found = 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, false))
>> +        return true;
>> +
>> +    if (type == 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 = 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);
>> +}
> 
> This is how I would expect all the other public functions to look like.
> Simple, does one thing and there is no magic.
> 

[...]




More information about the libvir-list mailing list