// SPDX-License-Identifier: LGPL-2.1 /* * Copyright (C) 2008, 2009, 2010 Red Hat Inc, Steven Rostedt * * Updates: * Copyright (C) 2019, VMware, Tzvetomir Stoyanov * */ #include #include #include #include #include #include #include #include #include #include #include #include "tracefs.h" #include "tracefs-local.h" enum { FLAG_INSTANCE_NEWLY_CREATED = (1 << 0), FLAG_INSTANCE_DELETED = (1 << 1), }; struct tracefs_options_mask toplevel_supported_opts; struct tracefs_options_mask toplevel_enabled_opts; __hidden inline struct tracefs_options_mask * supported_opts_mask(struct tracefs_instance *instance) { return instance ? &instance->supported_opts : &toplevel_supported_opts; } __hidden inline struct tracefs_options_mask * enabled_opts_mask(struct tracefs_instance *instance) { return instance ? &instance->enabled_opts : &toplevel_enabled_opts; } /** * instance_alloc - allocate a new ftrace instance * @trace_dir - Full path to the tracing directory, where the instance is * @name: The name of the instance (instance will point to this) * * Returns a newly allocated instance, or NULL in case of an error. */ static struct tracefs_instance *instance_alloc(const char *trace_dir, const char *name) { struct tracefs_instance *instance; instance = calloc(1, sizeof(*instance)); if (!instance) goto error; instance->trace_dir = strdup(trace_dir); if (!instance->trace_dir) goto error; if (name) { instance->name = strdup(name); if (!instance->name) goto error; } if (pthread_mutex_init(&instance->lock, NULL) < 0) goto error; instance->ftrace_filter_fd = -1; instance->ftrace_notrace_fd = -1; instance->ftrace_marker_fd = -1; instance->ftrace_marker_raw_fd = -1; return instance; error: if (instance) { free(instance->name); free(instance->trace_dir); free(instance); } return NULL; } __hidden int trace_get_instance(struct tracefs_instance *instance) { int ret; pthread_mutex_lock(&instance->lock); if (instance->flags & FLAG_INSTANCE_DELETED) { ret = -1; } else { instance->ref++; ret = 0; } pthread_mutex_unlock(&instance->lock); return ret; } __hidden void trace_put_instance(struct tracefs_instance *instance) { pthread_mutex_lock(&instance->lock); if (--instance->ref < 0) instance->flags |= FLAG_INSTANCE_DELETED; pthread_mutex_unlock(&instance->lock); if (!(instance->flags & FLAG_INSTANCE_DELETED)) return; if (instance->ftrace_filter_fd >= 0) close(instance->ftrace_filter_fd); if (instance->ftrace_notrace_fd >= 0) close(instance->ftrace_notrace_fd); if (instance->ftrace_marker_fd >= 0) close(instance->ftrace_marker_fd); if (instance->ftrace_marker_raw_fd >= 0) close(instance->ftrace_marker_raw_fd); free(instance->trace_dir); free(instance->name); pthread_mutex_destroy(&instance->lock); free(instance); } /** * tracefs_instance_free - Free an instance, previously allocated by tracefs_instance_create() * @instance: Pointer to the instance to be freed * */ void tracefs_instance_free(struct tracefs_instance *instance) { if (!instance) return; trace_put_instance(instance); } static mode_t get_trace_file_permissions(char *name) { mode_t rmode = 0; struct stat st; char *path; int ret; path = tracefs_get_tracing_file(name); if (!path) return 0; ret = stat(path, &st); if (ret) goto out; rmode = st.st_mode & ACCESSPERMS; out: tracefs_put_tracing_file(path); return rmode; } /** * tracefs_instance_is_new - Check if the instance is newly created by the library * @instance: Pointer to an ftrace instance * * Returns true, if the ftrace instance is newly created by the library or * false otherwise. */ bool tracefs_instance_is_new(struct tracefs_instance *instance) { if (instance && (instance->flags & FLAG_INSTANCE_NEWLY_CREATED)) return true; return false; } /** * tracefs_instance_create - Create a new ftrace instance * @name: Name of the instance to be created * * Allocates and initializes a new instance structure. If the instance does not * exist in the system, create it. * Returns a pointer to a newly allocated instance, or NULL in case of an error. * The returned instance must be freed by tracefs_instance_free(). */ struct tracefs_instance *tracefs_instance_create(const char *name) { struct tracefs_instance *inst = NULL; char *path = NULL; const char *tdir; struct stat st; mode_t mode; int ret; tdir = tracefs_tracing_dir(); if (!tdir) return NULL; inst = instance_alloc(tdir, name); if (!inst) return NULL; path = tracefs_instance_get_dir(inst); ret = stat(path, &st); if (ret < 0) { /* Cannot create the top instance, if it does not exist! */ if (!name) goto error; mode = get_trace_file_permissions("instances"); if (mkdir(path, mode)) goto error; inst->flags |= FLAG_INSTANCE_NEWLY_CREATED; } tracefs_put_tracing_file(path); return inst; error: tracefs_instance_free(inst); return NULL; } /** * tracefs_instance_alloc - Allocate an instance structure for existing trace instance * @tracing_dir: full path to the system trace directory, where the new instance is * if NULL, the default top tracing directory is used. * @name: Name of the instance. * * Allocates and initializes a new instance structure. If the instance does not * exist, do not create it and exit with error. * Returns a pointer to a newly allocated instance, or NULL in case of an error * or the requested instance does not exists. * The returned instance must be freed by tracefs_instance_free(). */ struct tracefs_instance *tracefs_instance_alloc(const char *tracing_dir, const char *name) { struct tracefs_instance *inst = NULL; char file[PATH_MAX]; const char *tdir; struct stat st; int ret; if (tracing_dir) { ret = stat(tracing_dir, &st); if (ret < 0 || !S_ISDIR(st.st_mode)) return NULL; tdir = tracing_dir; } else tdir = tracefs_tracing_dir(); if (!tdir) return NULL; if (name) { sprintf(file, "%s/instances/%s", tdir, name); ret = stat(file, &st); if (ret < 0 || !S_ISDIR(st.st_mode)) return NULL; } inst = instance_alloc(tdir, name); return inst; } /** * tracefs_instance_destroy - Remove a ftrace instance * @instance: Pointer to the instance to be removed * * Returns -1 in case of an error, or 0 otherwise. */ int tracefs_instance_destroy(struct tracefs_instance *instance) { char *path; int ret = -1; if (!instance || !instance->name) { tracefs_warning("Cannot remove top instance"); return -1; } path = tracefs_instance_get_dir(instance); if (path) ret = rmdir(path); tracefs_put_tracing_file(path); if (ret) { pthread_mutex_lock(&instance->lock); instance->flags |= FLAG_INSTANCE_DELETED; pthread_mutex_unlock(&instance->lock); } return ret; } /** * tracefs_instance_get_file - return the path to an instance file. * @instance: ftrace instance, can be NULL for the top instance * @file: name of file to return * * Returns the path of the @file for the given @instance, or NULL in * case of an error. * * Must use tracefs_put_tracing_file() to free the returned string. */ char * tracefs_instance_get_file(struct tracefs_instance *instance, const char *file) { char *path = NULL; int ret; if (!instance) return tracefs_get_tracing_file(file); if (!instance->name) ret = asprintf(&path, "%s/%s", instance->trace_dir, file); else ret = asprintf(&path, "%s/instances/%s/%s", instance->trace_dir, instance->name, file); if (ret < 0) return NULL; return path; } /** * tracefs_instance_get_dir - return the path to the instance directory. * @instance: ftrace instance, can be NULL for the top instance * * Returns the full path to the instance directory * * Must use tracefs_put_tracing_file() to free the returned string. */ char *tracefs_instance_get_dir(struct tracefs_instance *instance) { char *path = NULL; int ret; if (!instance) /* Top instance of default system trace directory */ return trace_find_tracing_dir(false); if (!instance->name) return strdup(instance->trace_dir); ret = asprintf(&path, "%s/instances/%s", instance->trace_dir, instance->name); if (ret < 0) { tracefs_warning("Failed to allocate path for instance %s", instance->name); return NULL; } return path; } /** * tracefs_instance_get_name - return the name of an instance * @instance: ftrace instance * * Returns the name of the given @instance. * The returned string must *not* be freed. */ const char *tracefs_instance_get_name(struct tracefs_instance *instance) { if (instance) return instance->name; return NULL; } /** * tracefs_instance_get_buffer_size - return the buffer size of the ring buffer * @instance: The instance to get the buffer size from * @cpu: if less that zero, will return the total size, otherwise the cpu size * * Returns the buffer size. If @cpu is less than zero, it returns the total size * of the ring buffer otherwise it returs the size of the buffer for the given * CPU. * * Returns -1 on error. */ ssize_t tracefs_instance_get_buffer_size(struct tracefs_instance *instance, int cpu) { unsigned long long size; char *path; char *val; int ret; if (cpu < 0) { val = tracefs_instance_file_read(instance, "buffer_total_size_kb", NULL); } else { ret = asprintf(&path, "per_cpu/cpu%d/buffer_size_kb", cpu); if (ret < 0) return ret; val = tracefs_instance_file_read(instance, path, NULL); free(path); } if (!val) return -1; size = strtoull(val, NULL, 0); free(val); return size; } int tracefs_instance_set_buffer_size(struct tracefs_instance *instance, size_t size, int cpu) { char *path; char *val; int ret; ret = asprintf(&val, "%zd", size); if (ret < 0) return ret; if (cpu < 0) { ret = tracefs_instance_file_write(instance, "buffer_size_kb", val); } else { ret = asprintf(&path, "per_cpu/cpu%d/buffer_size_kb", cpu); if (ret < 0) { free(val); return ret; } ret = tracefs_instance_file_write(instance, path, val); free(path); } free(val); return ret < 0 ? -1 : 0; } /** * tracefs_instance_get_trace_dir - return the top trace directory, where the instance is confuigred * @instance: ftrace instance * * Returns the top trace directory where the given @instance is configured. * The returned string must *not* be freed. */ const char *tracefs_instance_get_trace_dir(struct tracefs_instance *instance) { if (instance) return instance->trace_dir; return NULL; } static int write_file(const char *file, const char *str, int flags) { int ret = 0; int fd; fd = open(file, flags); if (fd < 0) { tracefs_warning("Failed to open '%s'", file); return -1; } if (str) ret = write(fd, str, strlen(str)); close(fd); return ret; } static int instance_file_write(struct tracefs_instance *instance, const char *file, const char *str, int flags) { struct stat st; char *path; int ret; path = tracefs_instance_get_file(instance, file); if (!path) return -1; ret = stat(path, &st); if (ret == 0) ret = write_file(path, str, flags); tracefs_put_tracing_file(path); return ret; } /** * tracefs_instance_file_write - Write in trace file of specific instance. * @instance: ftrace instance, can be NULL for the top instance * @file: name of the file * @str: nul terminated string, that will be written in the file. * * Returns the number of written bytes, or -1 in case of an error */ int tracefs_instance_file_write(struct tracefs_instance *instance, const char *file, const char *str) { return instance_file_write(instance, file, str, O_WRONLY | O_TRUNC); } /** * tracefs_instance_file_append - Append to a trace file of specific instance. * @instance: ftrace instance, can be NULL for the top instance. * @file: name of the file. * @str: nul terminated string, that will be appended to the file. * * Returns the number of appended bytes, or -1 in case of an error. */ int tracefs_instance_file_append(struct tracefs_instance *instance, const char *file, const char *str) { return instance_file_write(instance, file, str, O_WRONLY); } /** * tracefs_instance_file_clear - Clear a trace file of specific instance. * Note, it only opens with O_TRUNC and closes the file. If the file has * content that does not get cleared in this way, this will not have any * effect. For example, set_ftrace_filter can have probes that are not * cleared by O_TRUNC: * * echo "schedule:stacktrace" > set_ftrace_filter * * This function will not clear the above "set_ftrace_filter" after that * command. * @instance: ftrace instance, can be NULL for the top instance. * @file: name of the file to clear. * * Returns 0 on success, or -1 in case of an error. */ int tracefs_instance_file_clear(struct tracefs_instance *instance, const char *file) { return instance_file_write(instance, file, NULL, O_WRONLY | O_TRUNC); } /** * tracefs_instance_file_read - Read from a trace file of specific instance. * @instance: ftrace instance, can be NULL for the top instance * @file: name of the file * @psize: returns the number of bytes read * * Returns a pointer to a nul terminated string, read from the file, or NULL in * case of an error. * The return string must be freed by free() */ char *tracefs_instance_file_read(struct tracefs_instance *instance, const char *file, int *psize) { char *buf = NULL; int size = 0; char *path; path = tracefs_instance_get_file(instance, file); if (!path) return NULL; size = str_read_file(path, &buf, true); tracefs_put_tracing_file(path); if (buf && psize) *psize = size; return buf; } /** * tracefs_instance_file_read_number - Read long long integer from a trace file. * @instance: ftrace instance, can be NULL for the top instance * @file: name of the file * @res: The integer from the file. * * Returns 0 if the reading is successful and the result is stored in res, -1 * in case of an error. */ int tracefs_instance_file_read_number(struct tracefs_instance *instance, const char *file, long long *res) { long long num; int ret = -1; int size = 0; char *endptr; char *str; str = tracefs_instance_file_read(instance, file, &size); if (size && str) { errno = 0; num = strtoll(str, &endptr, 0); if (errno == 0 && str != endptr) { *res = num; ret = 0; } } free(str); return ret; } /** * tracefs_instance_file_open - Open a trace file for reading and writing * @instance: ftrace instance, can be NULL for the top instance * @file: name of the file * @mode: file open flags, -1 for default O_RDWR * * Returns -1 in case of an error, or a valid file descriptor otherwise. * The returned FD must be closed with close() */ int tracefs_instance_file_open(struct tracefs_instance *instance, const char *file, int mode) { int flags = O_RDWR; int fd = -1; char *path; path = tracefs_instance_get_file(instance, file); if (!path) return -1; if (mode >= 0) flags = mode; fd = open(path, flags); tracefs_put_tracing_file(path); return fd; } static bool check_file_exists(struct tracefs_instance *instance, const char *name, bool dir) { char file[PATH_MAX]; struct stat st; char *path; int ret; path = tracefs_instance_get_dir(instance); if (name) snprintf(file, PATH_MAX, "%s/%s", path, name); else snprintf(file, PATH_MAX, "%s", path); tracefs_put_tracing_file(path); ret = stat(file, &st); if (ret < 0) return false; return !dir == !S_ISDIR(st.st_mode); } /** * tracefs_instance_exists - Check an instance with given name exists * @name: name of the instance * * Returns true if the instance exists, false otherwise * */ bool tracefs_instance_exists(const char *name) { char file[PATH_MAX]; if (!name) return false; snprintf(file, PATH_MAX, "instances/%s", name); return check_file_exists(NULL, file, true); } /** * tracefs_file_exists - Check if a file with given name exists in given instance * @instance: ftrace instance, can be NULL for the top instance * @name: name of the file * * Returns true if the file exists, false otherwise * * If a directory with the given name exists, false is returned. */ bool tracefs_file_exists(struct tracefs_instance *instance, const char *name) { return check_file_exists(instance, name, false); } /** * tracefs_dir_exists - Check if a directory with given name exists in given instance * @instance: ftrace instance, can be NULL for the top instance * @name: name of the directory * * Returns true if the directory exists, false otherwise */ bool tracefs_dir_exists(struct tracefs_instance *instance, const char *name) { return check_file_exists(instance, name, true); } /** * tracefs_instances_walk - Iterate through all ftrace instances in the system * @callback: user callback, called for each instance. Instance name is passed * as input parameter. If the @callback returns non-zero, * the iteration stops. * @context: user context, passed to the @callback. * * Returns -1 in case of an error, 1 if the iteration was stopped because of the * callback return value or 0 otherwise. */ int tracefs_instances_walk(int (*callback)(const char *, void *), void *context) { struct dirent *dent; char *path = NULL; DIR *dir = NULL; struct stat st; int fret = -1; int ret; path = tracefs_get_tracing_file("instances"); if (!path) return -1; ret = stat(path, &st); if (ret < 0 || !S_ISDIR(st.st_mode)) goto out; dir = opendir(path); if (!dir) goto out; fret = 0; while ((dent = readdir(dir))) { char *instance; if (strcmp(dent->d_name, ".") == 0 || strcmp(dent->d_name, "..") == 0) continue; instance = trace_append_file(path, dent->d_name); ret = stat(instance, &st); free(instance); if (ret < 0 || !S_ISDIR(st.st_mode)) continue; if (callback(dent->d_name, context)) { fret = 1; break; } } out: if (dir) closedir(dir); tracefs_put_tracing_file(path); return fret; } static inline bool match(const char *str, regex_t *re) { if (!re) return true; return regexec(re, str, 0, NULL, 0) == 0; } struct instance_list { regex_t *re; char **list; int failed; }; static int build_list(const char *name, void *data) { struct instance_list *list = data; char **instances; int ret = -1; if (!match(name, list->re)) return 0; instances = tracefs_list_add(list->list, name); if (!instances) goto out; list->list = instances; ret = 0; out: list->failed = ret; return ret; } /** * tracefs_instances - return a list of instance names * @regex: A regex of instances to filter on (NULL to match all) * * Returns a list of names of existing instances, that must be * freed with tracefs_list_free(). Note, if there are no matches * then an empty list will be returned (not NULL). * NULL on error. */ char **tracefs_instances(const char *regex) { struct instance_list list = { .re = NULL, .list = NULL }; regex_t re; int ret; if (regex) { ret = regcomp(&re, regex, REG_ICASE|REG_NOSUB); if (ret < 0) return NULL; list.re = &re; } ret = tracefs_instances_walk(build_list, &list); if (ret < 0 || list.failed) { tracefs_list_free(list.list); list.list = NULL; } else { /* No matches should produce an empty list */ if (!list.list) list.list = trace_list_create_empty(); } return list.list; } /** * tracefs_get_clock - Get the current trace clock * @instance: ftrace instance, can be NULL for the top instance * * Returns the current trace clock of the given instance, or NULL in * case of an error. * The return string must be freed by free() */ char *tracefs_get_clock(struct tracefs_instance *instance) { char *all_clocks = NULL; char *ret = NULL; int bytes = 0; char *clock; char *cont; all_clocks = tracefs_instance_file_read(instance, "trace_clock", &bytes); if (!all_clocks || !bytes) goto out; clock = strstr(all_clocks, "["); if (!clock) goto out; clock++; cont = strstr(clock, "]"); if (!cont) goto out; *cont = '\0'; ret = strdup(clock); out: free(all_clocks); return ret; } /** * tracefs_instance_set_affinity_raw - write a hex bitmask into the affinity * @instance: The instance to set affinity to (NULL for top level) * @mask: String containing the hex value to set the tracing affinity to. * * Sets the tracing affinity CPU mask for @instance. The @mask is the raw * value that is used to write into the tracing system. * * Return 0 on success and -1 on error. */ int tracefs_instance_set_affinity_raw(struct tracefs_instance *instance, const char *mask) { return tracefs_instance_file_write(instance, "tracing_cpumask", mask); } /** * tracefs_instance_set_affinity_set - use a cpu_set to define tracing affinity * @instance: The instance to set affinity to (NULL for top level) * @set: A CPU set that describes the CPU affinity to set tracing to. * @set_size: The size in bytes of @set (use CPU_ALLOC_SIZE() to get this value) * * Sets the tracing affinity CPU mask for @instance. The bits in @set will be * used to set the CPUs to have tracing on. * * If @set is NULL, then all CPUs defined by sysconf(_SC_NPROCESSORS_CONF) * will be set, and @set_size is ignored. * * Return 0 on success and -1 on error. */ int tracefs_instance_set_affinity_set(struct tracefs_instance *instance, cpu_set_t *set, size_t set_size) { struct trace_seq seq; bool free_set = false; bool hit = false; int nr_cpus; int cpu; int ret = -1; int w, n, i; trace_seq_init(&seq); /* NULL set means all CPUs to be set */ if (!set) { nr_cpus = sysconf(_SC_NPROCESSORS_CONF); set = CPU_ALLOC(nr_cpus); if (!set) goto out; set_size = CPU_ALLOC_SIZE(nr_cpus); CPU_ZERO_S(set_size, set); /* Set all CPUS */ for (cpu = 0; cpu < nr_cpus; cpu++) CPU_SET_S(cpu, set_size, set); free_set = true; } /* Convert to a bitmask hex string */ nr_cpus = (set_size + 1) * 8; if (nr_cpus < 1) { /* Must have at least one bit set */ errno = EINVAL; goto out; } /* Start backwards from 32 bits */ for (w = ((nr_cpus + 31) / 32) - 1; w >= 0; w--) { /* Now move one nibble at a time */ for (n = 7; n >= 0; n--) { int nibble = 0; if ((n * 4) + (w * 32) >= nr_cpus) continue; /* One bit at a time */ for (i = 3; i >= 0; i--) { cpu = (w * 32) + (n * 4) + i; if (cpu >= nr_cpus) continue; if (CPU_ISSET_S(cpu, set_size, set)) { nibble |= 1 << i; hit = true; } } if (hit && trace_seq_printf(&seq, "%x", nibble) < 0) goto out; } if (hit && w) if (trace_seq_putc(&seq, ',') < 0) goto out; } if (!hit) { errno = EINVAL; goto out; } trace_seq_terminate(&seq); ret = tracefs_instance_set_affinity_raw(instance, seq.buffer); out: trace_seq_destroy(&seq); if (free_set) CPU_FREE(set); return ret; } /** * tracefs_instance_set_affinity - Set the affinity defined by CPU values. * @instance: The instance to set affinity to (NULL for top level) * @cpu_str: A string of values that define what CPUs to set. * * Sets the tracing affinity CPU mask for @instance. The @cpu_str is a set * of decimal numbers used to state which CPU should be part of the affinity * mask. A range may also be specified via a hyphen. * * For example, "1,4,6-8" * * The numbers do not need to be in order. * * If @cpu_str is NULL, then all CPUs defined by sysconf(_SC_NPROCESSORS_CONF) * will be set. * * Return 0 on success and -1 on error. */ int tracefs_instance_set_affinity(struct tracefs_instance *instance, const char *cpu_str) { cpu_set_t *set = NULL; size_t set_size; char *word; char *cpus; char *del; char *c; int max_cpu = 0; int cpu1, cpu2; int len; int ret = -1; /* NULL cpu_str means to set all CPUs in the mask */ if (!cpu_str) return tracefs_instance_set_affinity_set(instance, NULL, 0); /* First, find out how many CPUs are needed */ cpus = strdup(cpu_str); if (!cpus) return -1; len = strlen(cpus) + 1; for (word = strtok_r(cpus, ",", &del); word; word = strtok_r(NULL, ",", &del)) { cpu1 = atoi(word); if (cpu1 < 0) { errno = EINVAL; goto out; } if (cpu1 > max_cpu) max_cpu = cpu1; cpu2 = -1; if ((c = strchr(word, '-'))) { c++; cpu2 = atoi(c); if (cpu2 < cpu1) { errno = EINVAL; goto out; } if (cpu2 > max_cpu) max_cpu = cpu2; } } /* * Now ideally, cpus should fit cpu_str as it was orginally allocated * by strdup(). But I'm paranoid, and can imagine someone playing tricks * with threads, and changes cpu_str from another thread and messes * with this. At least only copy what we know is allocated. */ strncpy(cpus, cpu_str, len); set = CPU_ALLOC(max_cpu + 1); if (!set) goto out; set_size = CPU_ALLOC_SIZE(max_cpu + 1); CPU_ZERO_S(set_size, set); for (word = strtok_r(cpus, ",", &del); word; word = strtok_r(NULL, ",", &del)) { cpu1 = atoi(word); if (cpu1 < 0 || cpu1 > max_cpu) { /* Someone playing games? */ errno = EACCES; goto out; } cpu2 = cpu1; if ((c = strchr(word, '-'))) { c++; cpu2 = atoi(c); if (cpu2 < cpu1 || cpu2 > max_cpu) { errno = EACCES; goto out; } } for ( ; cpu1 <= cpu2; cpu1++) CPU_SET(cpu1, set); } ret = tracefs_instance_set_affinity_set(instance, set, set_size); out: free(cpus); 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); /* * It is possible that the passed in set_size is not big enough * to hold the cpu we just set. If that's the case, do not report * it as being set. * * The CPU_ISSET_S() should return false if the CPU given to it * is bigger than the set itself. */ return CPU_ISSET_S(cpu_set + cpu, set_size, set) ? 1 : 0; } /** * 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 allocated 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; }