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_); -- 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 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()* +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; + } + } + + 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" + * 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