[libvirt] [PATCHv5 09/19] util: Add more interfaces for resctrl monitor

Wang, Huaqiang huaqiang.wang at intel.com
Mon Oct 15 07:43:00 UTC 2018



On 10/12/2018 10:40 PM, John Ferlan wrote:
> [...]
>
>>>>   virResctrlMonitorCreate(virResctrlAllocPtr alloc,
>>>>                           virResctrlMonitorPtr monitor,
>>>>                           const char *machinename)
>>>> @@ -2534,3 +2565,177 @@ virResctrlMonitorCreate(virResctrlAllocPtr alloc,
>>>>       virResctrlUnlock(lockfd);
>>>>       return ret;
>>>>   }
>>>> +
>>>> +
>>>> +int
>>> The eventual only caller never checks status...
>> That's true, and I noticed that. The back-end logic is copied from
>> virResctrlAllocRemove.
> Understood, but that doesn't check status either. Maybe it needs to
> change to a void since it uses VIR_ERROR.
>
>> Maybe with a change of removing the following
>> virReportSystemError lines.
>>
>>>> +virResctrlMonitorRemove(virResctrlMonitorPtr monitor)
>>>> +{
>>>> +    int ret = 0;
>>>> +
>>> So unlike Alloc, @monitor cannot be NULL...
>> @monitor is not allowed to be NULL. This is guaranteed by the caller.
>>
>>>> +    if (!monitor->path)
>>>> +        return 0;
>>>> +
>>>> +    VIR_DEBUG("Removing resctrl monitor%s", monitor->path);
>>>> +    if (rmdir(monitor->path) != 0 && errno != ENOENT) {
>>>> +        virReportSystemError(errno,
>>>> +                             _("Unable to remove %s (%d)"),
>>>> +                             monitor->path, errno);
>>>> +        ret = -errno;
>>>> +        VIR_ERROR(_("Unable to remove %s (%d)"), monitor->path, errno);
>>> Either virReportSystemError if you're going to handle the returned
>>> status or VIR_ERROR if you're not (and are just ignoring), but not both.
>> I would like to remove the virReportSystemError lines and keep the VIR_ERROR line.
>>
> I can agree to that along with it being void since it's a best effort.
>
>>>
>>>> +    }
>>>> +
>>>> +    return ret;
>>>> +}
>>>> +
>>>> +
>>>> +int
>>>> +virResctrlMonitorSetCacheLevel(virResctrlMonitorPtr monitor,
>>>> +                               unsigned int level)
>>>> +{
>>>> +    /* Only supports cache level 3 CMT */
>>>> +    if (level != 3) {
>>>> +        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
>>>> +                       _("Invalid resctrl monitor cache level"));
>>>> +        return -1;
>>>> +    }
>>>> +
>>>> +    monitor->cache_level = level;
>>>> +
>>>> +    return 0;
>>>> +}
>>>> +
>>>> +unsigned int
>>>> +virResctrlMonitorGetCacheLevel(virResctrlMonitorPtr monitor)
>>>> +{
>>>> +    return monitor->cache_level;
>>>> +}
>>> Based on usage, maybe we should just give up on API's like this. Create
>>> a VIR_RESCTRL_MONITOR_CACHE_LEVEL (or something like it)... Then use it
>>> at least for now when reading/supplying the level. Thus we leave it to
>>> future developer to create the API's and handle the new levels...
>>>
>>> If when we Parse we don't find the constant, then error.
>> Do you mean removing the 'virResctrlMonitorSetCacheLevel'
>> and 'virResctrlMonitorGetCacheLevel' two functions from here, and processing
>> the monitor cache level information directory in domain_conf.c during XML
>> parsing and format process.
>>
> Yeah, I think I've come to that conclusion. In the Parse code you'd
> still parse the value, but then compare against the constant.  In the
> Format code, you can just format what you have. Whomever creates a
> different level in the future can have the fun of managing range and of
> course managing if whatever is being fetched is in the particular cache
> level.
>
>>>> +
>>>> +
>>>> +/*
>>>> + * virResctrlMonitorGetStatistic
> [...]
>
>>>> +        str_id = strchr(++str_id, '_');
>>>> +        if (!str_id)
>>>> +            continue;
>>> I think all of the above could be replaced by:
>>>
>>>      if (!(str_id = STRSKIP(ent->d_name, "mon_L")))
>>>          continue;
>>>
>>> I personally am not a fan of pre-auto-incr. I get particularly
>>> uncomfortable when it involves pointer arithmetic... But I think it's
>>> unnecessary if you use STRSKIP
>> The interesting target directory name is like 'MON_L3_00' or 'MON_L3CODE_00'
>> if CDP is enabled. The last two numbers after second '_' character is the ID
>> number, now we need to locate the second '_' character.
>> One STRSKIP is not enough, still need a call of strchr to locate the second '_' in
>> following way:
>>
>>      if (!(str_id = STRSKIP(ent->d_name, "mon_L")))
>>          continue;
>>
>>       str_id = strchr(str_id , '_');
>>       if (!str_id)
>>            continue;
> So MON_L3DATA_00 would do the same as would MON_L3FUTURE_00?
Yes. Here I am only interested in the last two digital numbers after 
second character '_', which
is the node id.

> Then let's STRSKIP(str_id, "_") - IOW: Skip to the next

Do you mean there steps in total?

     if (!(str_id = STRSKIP(ent->d_name, "mon_L")))
         continue;

      str_id = strchr(str_id , '_');
      if (!str_id)
           continue;

      if (!(str_id = STRSKIP(str_id, "_")))/* instead of STRSKIP(ent->d_name, '_') */
         continue;

      if (virStrToLong_uip(str_id, NULL, 0, &id) < 0)
           goto cleanup;

> The first STRSKIP is to get to the "level"#, right?

Yes.  But I skipped the verify for cache level, we only support L3 cache 
monitoring.

>   The second to the
> "id"#. Maybe the variables should be named that way to make it clear
> along with some comments.

Good suggestion.
I'll directly use 'node_id' for the name, and code would look like these:

           /* Looking for directory that contains the resource utilization
            * information file. The directory name is arranged in format
            * "mon_<node_name>_<node_id>". For example, "mon_L3_00" and
            * "mon_L3_01" are two target directories for a two nodes system
            * with resource utilization data file for each node 
respectively.
            */
           if (ent->d_type != DT_DIR)
continue;

           /* Looking for directory has a prefix 'mon_L' */
           if (!(node_id = STRSKIP(end->d_name, "mon_L")))
               continue;

           /* Looking for directory has another '_' */
           node_id = strchr(node_id, '_');
           if (!node_id)
               continue;

           /* Skip the character '_' */
           if (!(node_id = STRSKIP(node_id, "_")))
               continue;

           /* The node ID number should be here, parsing it. */
           if (virStrToLong_uip(node_id, NULL, 0, &id) < 0)
               goto cleanup;


>>>
>>>> +
>>>> +        if (virStrToLong_uip(++str_id, NULL, 0, &id) < 0)
>>>> +            goto cleanup;
>>>> +
>>>> +        rv = virFileReadValueUint(&val, "%s/%s/%s", datapath,
>>>> +                                  ent->d_name, resource);
>>>> +        if (rv == -2) {
>>>> +            virReportError(VIR_ERR_INTERNAL_ERROR,
>>>> +                           _("File '%s/%s/%s' does not exist."),
>>>> +                           datapath, ent->d_name, resource);
>>>> +        }
>>>> +        if (rv < 0)
>>>> +            goto cleanup;
>>>> +
>>>> +        if (VIR_APPEND_ELEMENT(*ids, nids, id) < 0)
>>>> +            goto cleanup;
>>>> +
>>>> +        if (VIR_APPEND_ELEMENT(*vals, nvals, val) < 0)
>>>> +            goto cleanup;
>>> If you had a single structure for id and val, then...
>> How about:
>>
>> struct _virResctrlMonitorStats {
>>      unsigned int id;
>>      unsigned int val;
>> };
>>
> Your call how you do it, but that self created sort (below) is more what
> is objectionable.  I remember peeking quickly - there are plenty of
> qsort examples to choose from in libvirt sources.

I'll using the _virResctrlMonitorStats structure, and sorting the output 
with qsort as you suggested.

>
> John

Thanks for review.
Huaqiang
>>>
>>>> +
>>>> +        /* Sort @ids and @vals arrays in the ascending order of id */
>>>> +        cur_id_pos = nids - 1;
>>>> +        for (i = 0; i < cur_id_pos; i++) {
>>>> +            if ((*ids)[cur_id_pos] < (*ids)[i]) {
>>>> +                tmp_id = (*ids)[cur_id_pos];
>>>> +                tmp_val = (*vals)[cur_id_pos];
>>>> +                (*ids)[cur_id_pos] = (*ids)[i];
>>>> +                (*vals)[cur_id_pos] = (*vals)[i];
>>>> +                (*ids)[i] = tmp_id;
>>>> +                (*vals)[i] = tmp_val;
>>>> +            }
> [...]




More information about the libvir-list mailing list