perf affinity: Add infrastructure to save/restore affinity
[linux-2.6-microblaze.git] / tools / perf / util / affinity.c
diff --git a/tools/perf/util/affinity.c b/tools/perf/util/affinity.c
new file mode 100644 (file)
index 0000000..a5e31f8
--- /dev/null
@@ -0,0 +1,73 @@
+// 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() + 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_alloc(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_alloc(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();
+
+       if (cpu == -1)
+               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);
+}
+
+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);
+}