18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* Manage affinity to optimize IPIs inside the kernel perf API. */ 38c2ecf20Sopenharmony_ci#define _GNU_SOURCE 1 48c2ecf20Sopenharmony_ci#include <sched.h> 58c2ecf20Sopenharmony_ci#include <stdlib.h> 68c2ecf20Sopenharmony_ci#include <linux/bitmap.h> 78c2ecf20Sopenharmony_ci#include <linux/zalloc.h> 88c2ecf20Sopenharmony_ci#include "perf.h" 98c2ecf20Sopenharmony_ci#include "cpumap.h" 108c2ecf20Sopenharmony_ci#include "affinity.h" 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_cistatic int get_cpu_set_size(void) 138c2ecf20Sopenharmony_ci{ 148c2ecf20Sopenharmony_ci int sz = cpu__max_cpu() + 8 - 1; 158c2ecf20Sopenharmony_ci /* 168c2ecf20Sopenharmony_ci * sched_getaffinity doesn't like masks smaller than the kernel. 178c2ecf20Sopenharmony_ci * Hopefully that's big enough. 188c2ecf20Sopenharmony_ci */ 198c2ecf20Sopenharmony_ci if (sz < 4096) 208c2ecf20Sopenharmony_ci sz = 4096; 218c2ecf20Sopenharmony_ci return sz / 8; 228c2ecf20Sopenharmony_ci} 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ciint affinity__setup(struct affinity *a) 258c2ecf20Sopenharmony_ci{ 268c2ecf20Sopenharmony_ci int cpu_set_size = get_cpu_set_size(); 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci a->orig_cpus = bitmap_alloc(cpu_set_size * 8); 298c2ecf20Sopenharmony_ci if (!a->orig_cpus) 308c2ecf20Sopenharmony_ci return -1; 318c2ecf20Sopenharmony_ci sched_getaffinity(0, cpu_set_size, (cpu_set_t *)a->orig_cpus); 328c2ecf20Sopenharmony_ci a->sched_cpus = bitmap_alloc(cpu_set_size * 8); 338c2ecf20Sopenharmony_ci if (!a->sched_cpus) { 348c2ecf20Sopenharmony_ci zfree(&a->orig_cpus); 358c2ecf20Sopenharmony_ci return -1; 368c2ecf20Sopenharmony_ci } 378c2ecf20Sopenharmony_ci bitmap_zero((unsigned long *)a->sched_cpus, cpu_set_size); 388c2ecf20Sopenharmony_ci a->changed = false; 398c2ecf20Sopenharmony_ci return 0; 408c2ecf20Sopenharmony_ci} 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci/* 438c2ecf20Sopenharmony_ci * perf_event_open does an IPI internally to the target CPU. 448c2ecf20Sopenharmony_ci * It is more efficient to change perf's affinity to the target 458c2ecf20Sopenharmony_ci * CPU and then set up all events on that CPU, so we amortize 468c2ecf20Sopenharmony_ci * CPU communication. 478c2ecf20Sopenharmony_ci */ 488c2ecf20Sopenharmony_civoid affinity__set(struct affinity *a, int cpu) 498c2ecf20Sopenharmony_ci{ 508c2ecf20Sopenharmony_ci int cpu_set_size = get_cpu_set_size(); 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci if (cpu == -1) 538c2ecf20Sopenharmony_ci return; 548c2ecf20Sopenharmony_ci a->changed = true; 558c2ecf20Sopenharmony_ci set_bit(cpu, a->sched_cpus); 568c2ecf20Sopenharmony_ci /* 578c2ecf20Sopenharmony_ci * We ignore errors because affinity is just an optimization. 588c2ecf20Sopenharmony_ci * This could happen for example with isolated CPUs or cpusets. 598c2ecf20Sopenharmony_ci * In this case the IPIs inside the kernel's perf API still work. 608c2ecf20Sopenharmony_ci */ 618c2ecf20Sopenharmony_ci sched_setaffinity(0, cpu_set_size, (cpu_set_t *)a->sched_cpus); 628c2ecf20Sopenharmony_ci clear_bit(cpu, a->sched_cpus); 638c2ecf20Sopenharmony_ci} 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_civoid affinity__cleanup(struct affinity *a) 668c2ecf20Sopenharmony_ci{ 678c2ecf20Sopenharmony_ci int cpu_set_size = get_cpu_set_size(); 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci if (a->changed) 708c2ecf20Sopenharmony_ci sched_setaffinity(0, cpu_set_size, (cpu_set_t *)a->orig_cpus); 718c2ecf20Sopenharmony_ci zfree(&a->sched_cpus); 728c2ecf20Sopenharmony_ci zfree(&a->orig_cpus); 738c2ecf20Sopenharmony_ci} 74