Re: [PATCH 1/2] libtracefs: Add tracefs_instance_get_affinity() APIs

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Wed, Jan 19, 2022 at 2:02 PM Steven Rostedt <rostedt@xxxxxxxxxxx> wrote:
>
> Add the APIs:
>
>   tracefs_instance_get_affinity()
>   tracefs_instance_get_affinity_set()
>   tracefs_instance_get_affinity_raw()
>
> These functions can retrieve the CPU affinity that denotes what an
> instance (or toplevel) has for CPUs that are enabled for tracing.
>
> The first API returns a nice human readable list of CPUs:
>
>  "1,4,6-8"
>
> To denote CPUs 1,4,6,7,8
>
> The _set() version uses CPU_SETS and the _raw() version just reads
> directly from the tracing_cpumask file.
>
> Signed-off-by: Steven Rostedt <rostedt@xxxxxxxxxxx>
> ---
>  .../libtracefs-instances-affinity.txt         |  39 +++-
>  include/tracefs.h                             |   4 +
>  src/tracefs-instance.c                        | 192 ++++++++++++++++++
>  3 files changed, 231 insertions(+), 4 deletions(-)
>
> diff --git a/Documentation/libtracefs-instances-affinity.txt b/Documentation/libtracefs-instances-affinity.txt
> index c5fa2d8b820c..22994f877e30 100644
> --- a/Documentation/libtracefs-instances-affinity.txt
> +++ b/Documentation/libtracefs-instances-affinity.txt
> @@ -3,8 +3,9 @@ libtracefs(3)
>
>  NAME
>  ----
> -tracefs_instance_set_affinity, tracefs_instance_set_affinity_set, tracefs_set_affinity_raw -
> -Sets the affinity for an instance or top level for what CPUs enable tracing.
> +tracefs_instance_set_affinity, tracefs_instance_set_affinity_set, tracefs_set_affinity_raw,
> +tracefs_instance_get_affinity, tracefs_instance_get_affinity_set, tracefs_get_affinity_raw,
> +Sets or retrieves the affinity for an instance or top level for what CPUs enable tracing.
>
>  SYNOPSIS
>  --------
> @@ -16,11 +17,14 @@ int *tracefs_instance_set_affinity*(struct tracefs_instance pass:[*]_instance_,
>  int *tracefs_instance_set_affinity_set*(struct tracefs_instance pass:[*]_instance_, cpu_set_t pass:[*]_set_, size_t _set_size_);
>  int *tracefs_instance_set_affinity_raw*(struct tracefs_instance pass:[*]_instance_, const char pass:[*]_mask_);
>
> +char pass:[*]*tracefs_instance_get_affinity*(struct tracefs_instance pass:[*]_instance_);
> +int *tracefs_instance_get_affinity_set*(struct tracefs_instance pass:[*]_instance_, cpu_set_t pass:[*]_set_, size_t _set_size_);
> +char pass:[*]*tracefs_instance_get_affinity_raw*(struct tracefs_instance pass:[*]_instance_);

These should be added also into Documentation/libtracefs.txt, under
the relevant API section. All library APIs should be listed there.

>  --
>
>  DESCRIPTION
>  -----------
> -These functions set the CPU affinity that limits what CPUs will have tracing enabled
> +These functions set or retrieves the CPU affinity that limits what CPUs will have tracing enabled

Maybe "These functions set or retrieve ..." ?

>  for a given instance defined by the _instance_ parameter. If _instance_ is NULL, then
>  the top level instance is affected.
>
> @@ -47,9 +51,32 @@ machine with more that 32 CPUs, to set CPUS 1-10 and CPU 40:
>
>  Where the above is a hex representation of bits 1-10 and bit 40 being set.
>
> +The *tracefs_instance_get_affinity()* will retrieve the affinity in a human readable
> +form.
> +
> +For example: "1,4,6-8"
> +
> +The string returned must be freed with *free*(3).
> +
> +The *tracefs_instance_get_affinity_set()* will set all the bits in the passed in
> +cpu set (from *CPU_SET*(3)). Note it will not clear any bits that are already set
> +in the set but the CPUs are not. If only the bits for the CPUs that are enabled
> +should be set, a CPU_ZERO_S() should be performed on the set before calling this
> +function.
> +
> +The *tracefs_instance_get_affinity_raw()* will simply read the instance tracing_cpumask
> +and return that string. The returned string must be freed with *free*(3).
> +
>  RETURN VALUE
>  ------------
> -All of these functions return 0 on success and -1 on error.
> +All the set functions return 0 on success and -1 on error.
> +
> +The functinos *tracefs_instance_get_affinity()* and *tracefs_instance_get_affinity_raw()*

a typo, "functions"

> +returns an allocated string that must be freed with *free*(3), or NULL on error.
> +
> +The function *tracefs_instance_get_affinity_set()* returns the number of CPUs that
> +were found set, or -1 on error.
> +
>
>  ERRORS
>  ------
> @@ -88,6 +115,10 @@ int main (int argc, char **argv)
>         int cpu2;
>         int i;
>
> +       c = tracefs_instance_get_affinity(NULL);
> +       printf("The affinity was %s\n", c);
> +       free(c);
> +
>         if (argc < 2) {
>                 tracefs_instance_set_affinity(NULL, NULL);
>                 exit(0);
> diff --git a/include/tracefs.h b/include/tracefs.h
> index 9c53b8413795..1848ad0aa668 100644
> --- a/include/tracefs.h
> +++ b/include/tracefs.h
> @@ -49,6 +49,10 @@ int tracefs_instance_set_affinity_raw(struct tracefs_instance *instance,
>                                       const char *mask);
>  int tracefs_instance_set_affinity(struct tracefs_instance *instance,
>                                   const char *cpu_str);
> +char *tracefs_instance_get_affinity(struct tracefs_instance *instance);
> +char *tracefs_instance_get_affinity_raw(struct tracefs_instance *instance);
> +int tracefs_instance_get_affinity_set(struct tracefs_instance *instance,
> +                                     cpu_set_t *set, size_t set_size);
>  char **tracefs_instances(const char *regex);
>
>  bool tracefs_instance_exists(const char *name);
> diff --git a/src/tracefs-instance.c b/src/tracefs-instance.c
> index fab615eb49ca..2d825b0e3bd0 100644
> --- a/src/tracefs-instance.c
> +++ b/src/tracefs-instance.c
> @@ -10,6 +10,7 @@
>  #include <stdio.h>
>  #include <stdlib.h>
>  #include <unistd.h>
> +#include <ctype.h>
>  #include <errno.h>
>  #include <sys/stat.h>
>  #include <fcntl.h>
> @@ -974,3 +975,194 @@ int tracefs_instance_set_affinity(struct tracefs_instance *instance,
>         CPU_FREE(set);
>         return ret;
>  }
> +
> +/**
> + * tracefs_instance_get_affinity_raw - read the affinity instance file
> + * @instance: The instance to get affinity of (NULL for top level)
> + *
> + * Reads the affinity file for @instance (or the top level if @instance
> + * is NULL) and returns it. The returned string must be freed with free().
> + *
> + * Returns the affinity mask on success, and must be freed with free()
> + *   or NULL on error.
> + */
> +char *tracefs_instance_get_affinity_raw(struct tracefs_instance *instance)
> +{
> +       return tracefs_instance_file_read(instance, "tracing_cpumask", NULL);
> +}
> +
> +static inline int update_cpu_set(int cpus, int cpu_set, int cpu,
> +                                cpu_set_t *set, size_t set_size)
> +{
> +       int bit = 1 << cpu;
> +
> +       if (!(cpus & bit))
> +               return 0;
> +
> +       CPU_SET_S(cpu_set + cpu, set_size, set);
> +       return 1;
> +}
> +
> +/**
> + * tracefs_instance_get_affinity_set - Retrieve the cpuset of an instance affinity
> + * @instance: The instance to get affinity of (NULL for top level)
> + * @set: A CPU set to put the affinity into.
> + * @set_size: The size in bytes of @set (use CPU_ALLOC_SIZE() to get this value)
> + *
> + * Reads the affinity of a given instance and updates the CPU set by the
> + * instance.
> + *
> + * Returns the number of CPUS that are set, or -1 on error.
> + */
> +int tracefs_instance_get_affinity_set(struct tracefs_instance *instance,
> +                                     cpu_set_t *set, size_t set_size)
> +{
> +       char *affinity;
> +       int cpu_set;
> +       int cpus;
> +       int cnt = 0;
> +       int ch;
> +       int i;
> +
> +       if (!set || !set_size) {
> +               errno = -EINVAL;
> +               return -1;
> +       }
> +
> +       affinity = tracefs_instance_get_affinity_raw(instance);
> +       if (!affinity)
> +               return -1;
> +
> +       /*
> +        * The returned affinity should be a comma delimited
> +        * hex string. Work backwards setting the values.
> +        */
> +       cpu_set = 0;
> +       i = strlen(affinity);
> +       for (i--; i >= 0; i--) {
> +               ch = affinity[i];
> +               if (isalnum(ch)) {
> +                       ch = tolower(ch);
> +                       if (isdigit(ch))
> +                               cpus = ch - '0';
> +                       else
> +                               cpus = ch - 'a' + 10;
> +
> +                       cnt += update_cpu_set(cpus, cpu_set, 0, set, set_size);
> +                       cnt += update_cpu_set(cpus, cpu_set, 1, set, set_size);
> +                       cnt += update_cpu_set(cpus, cpu_set, 2, set, set_size);
> +                       cnt += update_cpu_set(cpus, cpu_set, 3, set, set_size);
> +                       /* Next nibble */
> +                       cpu_set += 4;

I think there should be a check if set_size is reached. I do not know
how CPU_SET_S is implemented and if it can handle cpu IDs bigger than
the set_size. That should never happen, but a check inside the loop is
useful just to be on the safe side.

> +               }
> +       }
> +
> +       free(affinity);
> +
> +       return cnt;
> +}
> +
> +static inline int update_cpu(int cpus, int cpu_set, int cpu, int s, char **set)
> +{
> +       char *list;
> +       int bit = 1 << cpu;
> +       int ret;
> +
> +       if (*set == (char *)-1)
> +               return s;
> +
> +       if (cpus & bit) {
> +               /* If the previous CPU is set just return s */
> +               if (s >= 0)
> +                       return s;
> +               /* Otherwise, return this cpu */
> +               return cpu_set + cpu;
> +       }
> +
> +       /* If the last CPU wasn't set, just return s */
> +       if (s < 0)
> +               return s;
> +
> +       /* Update the string */
> +       if (s == cpu_set + cpu - 1) {
> +               ret = asprintf(&list, "%s%s%d",
> +                              *set ? *set : "", *set ? "," : "", s);
> +       } else {
> +               ret = asprintf(&list, "%s%s%d-%d",
> +                              *set ? *set : "", *set ? "," : "",
> +                              s, cpu_set + cpu - 1);
> +       }
> +       free(*set);
> +       /* Force *set to be a failure */
> +       if (ret < 0)
> +               *set = (char *)-1;
> +       else
> +               *set = list;
> +       return -1;
> +}
> +
> +/**
> + * tracefs_instance_get_affinity - Retrieve a string of CPUs for instance affinity
> + * @instance: The instance to get affinity of (NULL for top level)
> + *
> + * Reads the affinity of a given instance and returns a CPU count of the
> + * instance. For example, if it reads "eb" it will return:
> + *      "0-1,3,5-7"
> + *
> + * If no CPUs are set, an empty string is returned "\0", and it too needs
> + * to be freed.
> + *
> + * Returns an allocate string containing the CPU affinity in "human readable"

Maybe that should be "Returns an allocated string ..." ?

> + *  format which needs to be freed with free(), or NULL on error.
> + */
> +char *tracefs_instance_get_affinity(struct tracefs_instance *instance)
> +{
> +       char *affinity;
> +       char *set = NULL;
> +       int cpu_set;
> +       int cpus;
> +       int ch;
> +       int s = -1;
> +       int i;
> +
> +       affinity = tracefs_instance_get_affinity_raw(instance);
> +       if (!affinity)
> +               return NULL;
> +
> +       /*
> +        * The returned affinity should be a comma delimited
> +        * hex string. Work backwards setting the values.
> +        */
> +       cpu_set = 0;
> +       i = strlen(affinity);
> +       for (i--; i >= 0; i--) {
> +               ch = affinity[i];
> +               if (isalnum(ch)) {
> +                       ch = tolower(ch);
> +                       if (isdigit(ch))
> +                               cpus = ch - '0';
> +                       else
> +                               cpus = ch - 'a' + 10;
> +                       s = update_cpu(cpus, cpu_set, 0, s, &set);
> +                       s = update_cpu(cpus, cpu_set, 1, s, &set);
> +                       s = update_cpu(cpus, cpu_set, 2, s, &set);
> +                       s = update_cpu(cpus, cpu_set, 3, s, &set);
> +
> +                       if (set == (char *)-1) {
> +                               set = NULL;
> +                               goto out;
> +                       }
> +                       /* Next nibble */
> +                       cpu_set += 4;
> +               }
> +       }
> +       /* Clean up in case the last CPU is set */
> +       s = update_cpu(0, cpu_set, 0, s, &set);
> +
> +       if (!set)
> +               set = strdup("");
> + out:
> +       free(affinity);
> +
> +       return set;
> +}
> --
> 2.33.0
>


-- 
Tzvetomir (Ceco) Stoyanov
VMware Open Source Technology Center



[Index of Archives]     [Linux USB Development]     [Linux USB Development]     [Linux Audio Users]     [Yosemite Hiking]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux