| // SPDX-License-Identifier: GPL-2.0 | |
| /* Manage affinity to optimize IPIs inside the kernel perf API. */ | |
| #define _GNU_SOURCE 1 | |
| #include <sched.h> | |
| #include <stdlib.h> | |
| #include <linux/bitmap.h> | |
| #include <linux/zalloc.h> | |
| #include <perf/cpumap.h> | |
| #include "perf.h" | |
| #include "cpumap.h" | |
| #include "affinity.h" | |
| static int get_cpu_set_size(void) | |
| { | |
| int sz = cpu__max_cpu().cpu + 8 - 1; | |
| /* | |
| * sched_getaffinity doesn't like masks smaller than the kernel. | |
| * Hopefully that's big enough. | |
| */ | |
| if (sz < 4096) | |
| sz = 4096; | |
| return sz / 8; | |
| } | |
| int affinity__setup(struct affinity *a) | |
| { | |
| int cpu_set_size = get_cpu_set_size(); | |
| a->orig_cpus = bitmap_zalloc(cpu_set_size * 8); | |
| if (!a->orig_cpus) | |
| return -1; | |
| sched_getaffinity(0, cpu_set_size, (cpu_set_t *)a->orig_cpus); | |
| a->sched_cpus = bitmap_zalloc(cpu_set_size * 8); | |
| if (!a->sched_cpus) { | |
| zfree(&a->orig_cpus); | |
| return -1; | |
| } | |
| bitmap_zero((unsigned long *)a->sched_cpus, cpu_set_size); | |
| a->changed = false; | |
| return 0; | |
| } | |
| /* | |
| * perf_event_open does an IPI internally to the target CPU. | |
| * It is more efficient to change perf's affinity to the target | |
| * CPU and then set up all events on that CPU, so we amortize | |
| * CPU communication. | |
| */ | |
| void affinity__set(struct affinity *a, int cpu) | |
| { | |
| int cpu_set_size = get_cpu_set_size(); | |
| /* | |
| * Return: | |
| * - if cpu is -1 | |
| * - restrict out of bound access to sched_cpus | |
| */ | |
| if (cpu == -1 || ((cpu >= (cpu_set_size * 8)))) | |
| return; | |
| a->changed = true; | |
| __set_bit(cpu, a->sched_cpus); | |
| /* | |
| * We ignore errors because affinity is just an optimization. | |
| * This could happen for example with isolated CPUs or cpusets. | |
| * In this case the IPIs inside the kernel's perf API still work. | |
| */ | |
| sched_setaffinity(0, cpu_set_size, (cpu_set_t *)a->sched_cpus); | |
| __clear_bit(cpu, a->sched_cpus); | |
| } | |
| static void __affinity__cleanup(struct affinity *a) | |
| { | |
| int cpu_set_size = get_cpu_set_size(); | |
| if (a->changed) | |
| sched_setaffinity(0, cpu_set_size, (cpu_set_t *)a->orig_cpus); | |
| zfree(&a->sched_cpus); | |
| zfree(&a->orig_cpus); | |
| } | |
| void affinity__cleanup(struct affinity *a) | |
| { | |
| if (a != NULL) | |
| __affinity__cleanup(a); | |
| } | |
| void cpu_map__set_affinity(const struct perf_cpu_map *cpumap) | |
| { | |
| int cpu_set_size = get_cpu_set_size(); | |
| unsigned long *cpuset = bitmap_zalloc(cpu_set_size * 8); | |
| struct perf_cpu cpu; | |
| unsigned int idx; | |
| if (!cpuset) | |
| return; | |
| perf_cpu_map__for_each_cpu_skip_any(cpu, idx, cpumap) | |
| __set_bit(cpu.cpu, cpuset); | |
| sched_setaffinity(0, cpu_set_size, (cpu_set_t *)cpuset); | |
| zfree(&cpuset); | |
| } |