| // 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.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); |
| } |