linux/tools/lib/perf/cpumap.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2#include <perf/cpumap.h>
   3#include <stdlib.h>
   4#include <linux/refcount.h>
   5#include <internal/cpumap.h>
   6#include <asm/bug.h>
   7#include <stdio.h>
   8#include <string.h>
   9#include <unistd.h>
  10#include <ctype.h>
  11#include <limits.h>
  12
  13struct perf_cpu_map *perf_cpu_map__dummy_new(void)
  14{
  15        struct perf_cpu_map *cpus = malloc(sizeof(*cpus) + sizeof(int));
  16
  17        if (cpus != NULL) {
  18                cpus->nr = 1;
  19                cpus->map[0] = -1;
  20                refcount_set(&cpus->refcnt, 1);
  21        }
  22
  23        return cpus;
  24}
  25
  26static void cpu_map__delete(struct perf_cpu_map *map)
  27{
  28        if (map) {
  29                WARN_ONCE(refcount_read(&map->refcnt) != 0,
  30                          "cpu_map refcnt unbalanced\n");
  31                free(map);
  32        }
  33}
  34
  35struct perf_cpu_map *perf_cpu_map__get(struct perf_cpu_map *map)
  36{
  37        if (map)
  38                refcount_inc(&map->refcnt);
  39        return map;
  40}
  41
  42void perf_cpu_map__put(struct perf_cpu_map *map)
  43{
  44        if (map && refcount_dec_and_test(&map->refcnt))
  45                cpu_map__delete(map);
  46}
  47
  48static struct perf_cpu_map *cpu_map__default_new(void)
  49{
  50        struct perf_cpu_map *cpus;
  51        int nr_cpus;
  52
  53        nr_cpus = sysconf(_SC_NPROCESSORS_ONLN);
  54        if (nr_cpus < 0)
  55                return NULL;
  56
  57        cpus = malloc(sizeof(*cpus) + nr_cpus * sizeof(int));
  58        if (cpus != NULL) {
  59                int i;
  60
  61                for (i = 0; i < nr_cpus; ++i)
  62                        cpus->map[i] = i;
  63
  64                cpus->nr = nr_cpus;
  65                refcount_set(&cpus->refcnt, 1);
  66        }
  67
  68        return cpus;
  69}
  70
  71struct perf_cpu_map *perf_cpu_map__default_new(void)
  72{
  73        return cpu_map__default_new();
  74}
  75
  76static int cmp_int(const void *a, const void *b)
  77{
  78        return *(const int *)a - *(const int*)b;
  79}
  80
  81static struct perf_cpu_map *cpu_map__trim_new(int nr_cpus, int *tmp_cpus)
  82{
  83        size_t payload_size = nr_cpus * sizeof(int);
  84        struct perf_cpu_map *cpus = malloc(sizeof(*cpus) + payload_size);
  85        int i, j;
  86
  87        if (cpus != NULL) {
  88                memcpy(cpus->map, tmp_cpus, payload_size);
  89                qsort(cpus->map, nr_cpus, sizeof(int), cmp_int);
  90                /* Remove dups */
  91                j = 0;
  92                for (i = 0; i < nr_cpus; i++) {
  93                        if (i == 0 || cpus->map[i] != cpus->map[i - 1])
  94                                cpus->map[j++] = cpus->map[i];
  95                }
  96                cpus->nr = j;
  97                assert(j <= nr_cpus);
  98                refcount_set(&cpus->refcnt, 1);
  99        }
 100
 101        return cpus;
 102}
 103
 104struct perf_cpu_map *perf_cpu_map__read(FILE *file)
 105{
 106        struct perf_cpu_map *cpus = NULL;
 107        int nr_cpus = 0;
 108        int *tmp_cpus = NULL, *tmp;
 109        int max_entries = 0;
 110        int n, cpu, prev;
 111        char sep;
 112
 113        sep = 0;
 114        prev = -1;
 115        for (;;) {
 116                n = fscanf(file, "%u%c", &cpu, &sep);
 117                if (n <= 0)
 118                        break;
 119                if (prev >= 0) {
 120                        int new_max = nr_cpus + cpu - prev - 1;
 121
 122                        WARN_ONCE(new_max >= MAX_NR_CPUS, "Perf can support %d CPUs. "
 123                                                          "Consider raising MAX_NR_CPUS\n", MAX_NR_CPUS);
 124
 125                        if (new_max >= max_entries) {
 126                                max_entries = new_max + MAX_NR_CPUS / 2;
 127                                tmp = realloc(tmp_cpus, max_entries * sizeof(int));
 128                                if (tmp == NULL)
 129                                        goto out_free_tmp;
 130                                tmp_cpus = tmp;
 131                        }
 132
 133                        while (++prev < cpu)
 134                                tmp_cpus[nr_cpus++] = prev;
 135                }
 136                if (nr_cpus == max_entries) {
 137                        max_entries += MAX_NR_CPUS;
 138                        tmp = realloc(tmp_cpus, max_entries * sizeof(int));
 139                        if (tmp == NULL)
 140                                goto out_free_tmp;
 141                        tmp_cpus = tmp;
 142                }
 143
 144                tmp_cpus[nr_cpus++] = cpu;
 145                if (n == 2 && sep == '-')
 146                        prev = cpu;
 147                else
 148                        prev = -1;
 149                if (n == 1 || sep == '\n')
 150                        break;
 151        }
 152
 153        if (nr_cpus > 0)
 154                cpus = cpu_map__trim_new(nr_cpus, tmp_cpus);
 155        else
 156                cpus = cpu_map__default_new();
 157out_free_tmp:
 158        free(tmp_cpus);
 159        return cpus;
 160}
 161
 162static struct perf_cpu_map *cpu_map__read_all_cpu_map(void)
 163{
 164        struct perf_cpu_map *cpus = NULL;
 165        FILE *onlnf;
 166
 167        onlnf = fopen("/sys/devices/system/cpu/online", "r");
 168        if (!onlnf)
 169                return cpu_map__default_new();
 170
 171        cpus = perf_cpu_map__read(onlnf);
 172        fclose(onlnf);
 173        return cpus;
 174}
 175
 176struct perf_cpu_map *perf_cpu_map__new(const char *cpu_list)
 177{
 178        struct perf_cpu_map *cpus = NULL;
 179        unsigned long start_cpu, end_cpu = 0;
 180        char *p = NULL;
 181        int i, nr_cpus = 0;
 182        int *tmp_cpus = NULL, *tmp;
 183        int max_entries = 0;
 184
 185        if (!cpu_list)
 186                return cpu_map__read_all_cpu_map();
 187
 188        /*
 189         * must handle the case of empty cpumap to cover
 190         * TOPOLOGY header for NUMA nodes with no CPU
 191         * ( e.g., because of CPU hotplug)
 192         */
 193        if (!isdigit(*cpu_list) && *cpu_list != '\0')
 194                goto out;
 195
 196        while (isdigit(*cpu_list)) {
 197                p = NULL;
 198                start_cpu = strtoul(cpu_list, &p, 0);
 199                if (start_cpu >= INT_MAX
 200                    || (*p != '\0' && *p != ',' && *p != '-'))
 201                        goto invalid;
 202
 203                if (*p == '-') {
 204                        cpu_list = ++p;
 205                        p = NULL;
 206                        end_cpu = strtoul(cpu_list, &p, 0);
 207
 208                        if (end_cpu >= INT_MAX || (*p != '\0' && *p != ','))
 209                                goto invalid;
 210
 211                        if (end_cpu < start_cpu)
 212                                goto invalid;
 213                } else {
 214                        end_cpu = start_cpu;
 215                }
 216
 217                WARN_ONCE(end_cpu >= MAX_NR_CPUS, "Perf can support %d CPUs. "
 218                                                  "Consider raising MAX_NR_CPUS\n", MAX_NR_CPUS);
 219
 220                for (; start_cpu <= end_cpu; start_cpu++) {
 221                        /* check for duplicates */
 222                        for (i = 0; i < nr_cpus; i++)
 223                                if (tmp_cpus[i] == (int)start_cpu)
 224                                        goto invalid;
 225
 226                        if (nr_cpus == max_entries) {
 227                                max_entries += MAX_NR_CPUS;
 228                                tmp = realloc(tmp_cpus, max_entries * sizeof(int));
 229                                if (tmp == NULL)
 230                                        goto invalid;
 231                                tmp_cpus = tmp;
 232                        }
 233                        tmp_cpus[nr_cpus++] = (int)start_cpu;
 234                }
 235                if (*p)
 236                        ++p;
 237
 238                cpu_list = p;
 239        }
 240
 241        if (nr_cpus > 0)
 242                cpus = cpu_map__trim_new(nr_cpus, tmp_cpus);
 243        else if (*cpu_list != '\0')
 244                cpus = cpu_map__default_new();
 245        else
 246                cpus = perf_cpu_map__dummy_new();
 247invalid:
 248        free(tmp_cpus);
 249out:
 250        return cpus;
 251}
 252
 253int perf_cpu_map__cpu(const struct perf_cpu_map *cpus, int idx)
 254{
 255        if (cpus && idx < cpus->nr)
 256                return cpus->map[idx];
 257
 258        return -1;
 259}
 260
 261int perf_cpu_map__nr(const struct perf_cpu_map *cpus)
 262{
 263        return cpus ? cpus->nr : 1;
 264}
 265
 266bool perf_cpu_map__empty(const struct perf_cpu_map *map)
 267{
 268        return map ? map->map[0] == -1 : true;
 269}
 270
 271int perf_cpu_map__idx(struct perf_cpu_map *cpus, int cpu)
 272{
 273        int i;
 274
 275        for (i = 0; i < cpus->nr; ++i) {
 276                if (cpus->map[i] == cpu)
 277                        return i;
 278        }
 279
 280        return -1;
 281}
 282
 283int perf_cpu_map__max(struct perf_cpu_map *map)
 284{
 285        // cpu_map__trim_new() qsort()s it, cpu_map__default_new() sorts it as well.
 286        return map->nr > 0 ? map->map[map->nr - 1] : -1;
 287}
 288
 289/*
 290 * Merge two cpumaps
 291 *
 292 * orig either gets freed and replaced with a new map, or reused
 293 * with no reference count change (similar to "realloc")
 294 * other has its reference count increased.
 295 */
 296
 297struct perf_cpu_map *perf_cpu_map__merge(struct perf_cpu_map *orig,
 298                                         struct perf_cpu_map *other)
 299{
 300        int *tmp_cpus;
 301        int tmp_len;
 302        int i, j, k;
 303        struct perf_cpu_map *merged;
 304
 305        if (!orig && !other)
 306                return NULL;
 307        if (!orig) {
 308                perf_cpu_map__get(other);
 309                return other;
 310        }
 311        if (!other)
 312                return orig;
 313        if (orig->nr == other->nr &&
 314            !memcmp(orig->map, other->map, orig->nr * sizeof(int)))
 315                return orig;
 316
 317        tmp_len = orig->nr + other->nr;
 318        tmp_cpus = malloc(tmp_len * sizeof(int));
 319        if (!tmp_cpus)
 320                return NULL;
 321
 322        /* Standard merge algorithm from wikipedia */
 323        i = j = k = 0;
 324        while (i < orig->nr && j < other->nr) {
 325                if (orig->map[i] <= other->map[j]) {
 326                        if (orig->map[i] == other->map[j])
 327                                j++;
 328                        tmp_cpus[k++] = orig->map[i++];
 329                } else
 330                        tmp_cpus[k++] = other->map[j++];
 331        }
 332
 333        while (i < orig->nr)
 334                tmp_cpus[k++] = orig->map[i++];
 335
 336        while (j < other->nr)
 337                tmp_cpus[k++] = other->map[j++];
 338        assert(k <= tmp_len);
 339
 340        merged = cpu_map__trim_new(k, tmp_cpus);
 341        free(tmp_cpus);
 342        perf_cpu_map__put(orig);
 343        return merged;
 344}
 345