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