162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/* Manage affinity to optimize IPIs inside the kernel perf API. */
362306a36Sopenharmony_ci#define _GNU_SOURCE 1
462306a36Sopenharmony_ci#include <sched.h>
562306a36Sopenharmony_ci#include <stdlib.h>
662306a36Sopenharmony_ci#include <linux/bitmap.h>
762306a36Sopenharmony_ci#include <linux/zalloc.h>
862306a36Sopenharmony_ci#include "perf.h"
962306a36Sopenharmony_ci#include "cpumap.h"
1062306a36Sopenharmony_ci#include "affinity.h"
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_cistatic int get_cpu_set_size(void)
1362306a36Sopenharmony_ci{
1462306a36Sopenharmony_ci	int sz = cpu__max_cpu().cpu + 8 - 1;
1562306a36Sopenharmony_ci	/*
1662306a36Sopenharmony_ci	 * sched_getaffinity doesn't like masks smaller than the kernel.
1762306a36Sopenharmony_ci	 * Hopefully that's big enough.
1862306a36Sopenharmony_ci	 */
1962306a36Sopenharmony_ci	if (sz < 4096)
2062306a36Sopenharmony_ci		sz = 4096;
2162306a36Sopenharmony_ci	return sz / 8;
2262306a36Sopenharmony_ci}
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ciint affinity__setup(struct affinity *a)
2562306a36Sopenharmony_ci{
2662306a36Sopenharmony_ci	int cpu_set_size = get_cpu_set_size();
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci	a->orig_cpus = bitmap_zalloc(cpu_set_size * 8);
2962306a36Sopenharmony_ci	if (!a->orig_cpus)
3062306a36Sopenharmony_ci		return -1;
3162306a36Sopenharmony_ci	sched_getaffinity(0, cpu_set_size, (cpu_set_t *)a->orig_cpus);
3262306a36Sopenharmony_ci	a->sched_cpus = bitmap_zalloc(cpu_set_size * 8);
3362306a36Sopenharmony_ci	if (!a->sched_cpus) {
3462306a36Sopenharmony_ci		zfree(&a->orig_cpus);
3562306a36Sopenharmony_ci		return -1;
3662306a36Sopenharmony_ci	}
3762306a36Sopenharmony_ci	bitmap_zero((unsigned long *)a->sched_cpus, cpu_set_size);
3862306a36Sopenharmony_ci	a->changed = false;
3962306a36Sopenharmony_ci	return 0;
4062306a36Sopenharmony_ci}
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci/*
4362306a36Sopenharmony_ci * perf_event_open does an IPI internally to the target CPU.
4462306a36Sopenharmony_ci * It is more efficient to change perf's affinity to the target
4562306a36Sopenharmony_ci * CPU and then set up all events on that CPU, so we amortize
4662306a36Sopenharmony_ci * CPU communication.
4762306a36Sopenharmony_ci */
4862306a36Sopenharmony_civoid affinity__set(struct affinity *a, int cpu)
4962306a36Sopenharmony_ci{
5062306a36Sopenharmony_ci	int cpu_set_size = get_cpu_set_size();
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci	/*
5362306a36Sopenharmony_ci	 * Return:
5462306a36Sopenharmony_ci	 * - if cpu is -1
5562306a36Sopenharmony_ci	 * - restrict out of bound access to sched_cpus
5662306a36Sopenharmony_ci	 */
5762306a36Sopenharmony_ci	if (cpu == -1 || ((cpu >= (cpu_set_size * 8))))
5862306a36Sopenharmony_ci		return;
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci	a->changed = true;
6162306a36Sopenharmony_ci	__set_bit(cpu, a->sched_cpus);
6262306a36Sopenharmony_ci	/*
6362306a36Sopenharmony_ci	 * We ignore errors because affinity is just an optimization.
6462306a36Sopenharmony_ci	 * This could happen for example with isolated CPUs or cpusets.
6562306a36Sopenharmony_ci	 * In this case the IPIs inside the kernel's perf API still work.
6662306a36Sopenharmony_ci	 */
6762306a36Sopenharmony_ci	sched_setaffinity(0, cpu_set_size, (cpu_set_t *)a->sched_cpus);
6862306a36Sopenharmony_ci	__clear_bit(cpu, a->sched_cpus);
6962306a36Sopenharmony_ci}
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_cistatic void __affinity__cleanup(struct affinity *a)
7262306a36Sopenharmony_ci{
7362306a36Sopenharmony_ci	int cpu_set_size = get_cpu_set_size();
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci	if (a->changed)
7662306a36Sopenharmony_ci		sched_setaffinity(0, cpu_set_size, (cpu_set_t *)a->orig_cpus);
7762306a36Sopenharmony_ci	zfree(&a->sched_cpus);
7862306a36Sopenharmony_ci	zfree(&a->orig_cpus);
7962306a36Sopenharmony_ci}
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_civoid affinity__cleanup(struct affinity *a)
8262306a36Sopenharmony_ci{
8362306a36Sopenharmony_ci	if (a != NULL)
8462306a36Sopenharmony_ci		__affinity__cleanup(a);
8562306a36Sopenharmony_ci}
86