1 // SPDX-License-Identifier: GPL-2.0 2 /* Manage affinity to optimize IPIs inside the kernel perf API. */ 3 #define _GNU_SOURCE 1 4 #include <sched.h> 5 #include <stdlib.h> 6 #include <linux/bitmap.h> 7 #include <linux/zalloc.h> 8 #include "perf.h" 9 #include "cpumap.h" 10 #include "affinity.h" 11 12 static int get_cpu_set_size(void) 13 { 14 int sz = cpu__max_cpu().cpu + 8 - 1; 15 /* 16 * sched_getaffinity doesn't like masks smaller than the kernel. 17 * Hopefully that's big enough. 18 */ 19 if (sz < 4096) 20 sz = 4096; 21 return sz / 8; 22 } 23 24 int affinity__setup(struct affinity *a) 25 { 26 int cpu_set_size = get_cpu_set_size(); 27 28 a->orig_cpus = bitmap_zalloc(cpu_set_size * 8); 29 if (!a->orig_cpus) 30 return -1; 31 sched_getaffinity(0, cpu_set_size, (cpu_set_t *)a->orig_cpus); 32 a->sched_cpus = bitmap_zalloc(cpu_set_size * 8); 33 if (!a->sched_cpus) { 34 zfree(&a->orig_cpus); 35 return -1; 36 } 37 bitmap_zero((unsigned long *)a->sched_cpus, cpu_set_size); 38 a->changed = false; 39 return 0; 40 } 41 42 /* 43 * perf_event_open does an IPI internally to the target CPU. 44 * It is more efficient to change perf's affinity to the target 45 * CPU and then set up all events on that CPU, so we amortize 46 * CPU communication. 47 */ 48 void affinity__set(struct affinity *a, int cpu) 49 { 50 int cpu_set_size = get_cpu_set_size(); 51 52 if (cpu == -1) 53 return; 54 a->changed = true; 55 set_bit(cpu, a->sched_cpus); 56 /* 57 * We ignore errors because affinity is just an optimization. 58 * This could happen for example with isolated CPUs or cpusets. 59 * In this case the IPIs inside the kernel's perf API still work. 60 */ 61 sched_setaffinity(0, cpu_set_size, (cpu_set_t *)a->sched_cpus); 62 clear_bit(cpu, a->sched_cpus); 63 } 64 65 void affinity__cleanup(struct affinity *a) 66 { 67 int cpu_set_size = get_cpu_set_size(); 68 69 if (a->changed) 70 sched_setaffinity(0, cpu_set_size, (cpu_set_t *)a->orig_cpus); 71 zfree(&a->sched_cpus); 72 zfree(&a->orig_cpus); 73 } 74