linux/tools/perf/util/cpumap.c
<<
>>
Prefs
   1#include "util.h"
   2#include <api/fs/fs.h>
   3#include "../perf.h"
   4#include "cpumap.h"
   5#include <assert.h>
   6#include <stdio.h>
   7#include <stdlib.h>
   8
   9static struct cpu_map *cpu_map__default_new(void)
  10{
  11        struct cpu_map *cpus;
  12        int nr_cpus;
  13
  14        nr_cpus = sysconf(_SC_NPROCESSORS_ONLN);
  15        if (nr_cpus < 0)
  16                return NULL;
  17
  18        cpus = malloc(sizeof(*cpus) + nr_cpus * sizeof(int));
  19        if (cpus != NULL) {
  20                int i;
  21                for (i = 0; i < nr_cpus; ++i)
  22                        cpus->map[i] = i;
  23
  24                cpus->nr = nr_cpus;
  25        }
  26
  27        return cpus;
  28}
  29
  30static struct cpu_map *cpu_map__trim_new(int nr_cpus, int *tmp_cpus)
  31{
  32        size_t payload_size = nr_cpus * sizeof(int);
  33        struct cpu_map *cpus = malloc(sizeof(*cpus) + payload_size);
  34
  35        if (cpus != NULL) {
  36                cpus->nr = nr_cpus;
  37                memcpy(cpus->map, tmp_cpus, payload_size);
  38        }
  39
  40        return cpus;
  41}
  42
  43struct cpu_map *cpu_map__read(FILE *file)
  44{
  45        struct cpu_map *cpus = NULL;
  46        int nr_cpus = 0;
  47        int *tmp_cpus = NULL, *tmp;
  48        int max_entries = 0;
  49        int n, cpu, prev;
  50        char sep;
  51
  52        sep = 0;
  53        prev = -1;
  54        for (;;) {
  55                n = fscanf(file, "%u%c", &cpu, &sep);
  56                if (n <= 0)
  57                        break;
  58                if (prev >= 0) {
  59                        int new_max = nr_cpus + cpu - prev - 1;
  60
  61                        if (new_max >= max_entries) {
  62                                max_entries = new_max + MAX_NR_CPUS / 2;
  63                                tmp = realloc(tmp_cpus, max_entries * sizeof(int));
  64                                if (tmp == NULL)
  65                                        goto out_free_tmp;
  66                                tmp_cpus = tmp;
  67                        }
  68
  69                        while (++prev < cpu)
  70                                tmp_cpus[nr_cpus++] = prev;
  71                }
  72                if (nr_cpus == max_entries) {
  73                        max_entries += MAX_NR_CPUS;
  74                        tmp = realloc(tmp_cpus, max_entries * sizeof(int));
  75                        if (tmp == NULL)
  76                                goto out_free_tmp;
  77                        tmp_cpus = tmp;
  78                }
  79
  80                tmp_cpus[nr_cpus++] = cpu;
  81                if (n == 2 && sep == '-')
  82                        prev = cpu;
  83                else
  84                        prev = -1;
  85                if (n == 1 || sep == '\n')
  86                        break;
  87        }
  88
  89        if (nr_cpus > 0)
  90                cpus = cpu_map__trim_new(nr_cpus, tmp_cpus);
  91        else
  92                cpus = cpu_map__default_new();
  93out_free_tmp:
  94        free(tmp_cpus);
  95        return cpus;
  96}
  97
  98static struct cpu_map *cpu_map__read_all_cpu_map(void)
  99{
 100        struct cpu_map *cpus = NULL;
 101        FILE *onlnf;
 102
 103        onlnf = fopen("/sys/devices/system/cpu/online", "r");
 104        if (!onlnf)
 105                return cpu_map__default_new();
 106
 107        cpus = cpu_map__read(onlnf);
 108        fclose(onlnf);
 109        return cpus;
 110}
 111
 112struct cpu_map *cpu_map__new(const char *cpu_list)
 113{
 114        struct cpu_map *cpus = NULL;
 115        unsigned long start_cpu, end_cpu = 0;
 116        char *p = NULL;
 117        int i, nr_cpus = 0;
 118        int *tmp_cpus = NULL, *tmp;
 119        int max_entries = 0;
 120
 121        if (!cpu_list)
 122                return cpu_map__read_all_cpu_map();
 123
 124        if (!isdigit(*cpu_list))
 125                goto out;
 126
 127        while (isdigit(*cpu_list)) {
 128                p = NULL;
 129                start_cpu = strtoul(cpu_list, &p, 0);
 130                if (start_cpu >= INT_MAX
 131                    || (*p != '\0' && *p != ',' && *p != '-'))
 132                        goto invalid;
 133
 134                if (*p == '-') {
 135                        cpu_list = ++p;
 136                        p = NULL;
 137                        end_cpu = strtoul(cpu_list, &p, 0);
 138
 139                        if (end_cpu >= INT_MAX || (*p != '\0' && *p != ','))
 140                                goto invalid;
 141
 142                        if (end_cpu < start_cpu)
 143                                goto invalid;
 144                } else {
 145                        end_cpu = start_cpu;
 146                }
 147
 148                for (; start_cpu <= end_cpu; start_cpu++) {
 149                        /* check for duplicates */
 150                        for (i = 0; i < nr_cpus; i++)
 151                                if (tmp_cpus[i] == (int)start_cpu)
 152                                        goto invalid;
 153
 154                        if (nr_cpus == max_entries) {
 155                                max_entries += MAX_NR_CPUS;
 156                                tmp = realloc(tmp_cpus, max_entries * sizeof(int));
 157                                if (tmp == NULL)
 158                                        goto invalid;
 159                                tmp_cpus = tmp;
 160                        }
 161                        tmp_cpus[nr_cpus++] = (int)start_cpu;
 162                }
 163                if (*p)
 164                        ++p;
 165
 166                cpu_list = p;
 167        }
 168
 169        if (nr_cpus > 0)
 170                cpus = cpu_map__trim_new(nr_cpus, tmp_cpus);
 171        else
 172                cpus = cpu_map__default_new();
 173invalid:
 174        free(tmp_cpus);
 175out:
 176        return cpus;
 177}
 178
 179size_t cpu_map__fprintf(struct cpu_map *map, FILE *fp)
 180{
 181        int i;
 182        size_t printed = fprintf(fp, "%d cpu%s: ",
 183                                 map->nr, map->nr > 1 ? "s" : "");
 184        for (i = 0; i < map->nr; ++i)
 185                printed += fprintf(fp, "%s%d", i ? ", " : "", map->map[i]);
 186
 187        return printed + fprintf(fp, "\n");
 188}
 189
 190struct cpu_map *cpu_map__dummy_new(void)
 191{
 192        struct cpu_map *cpus = malloc(sizeof(*cpus) + sizeof(int));
 193
 194        if (cpus != NULL) {
 195                cpus->nr = 1;
 196                cpus->map[0] = -1;
 197        }
 198
 199        return cpus;
 200}
 201
 202void cpu_map__delete(struct cpu_map *map)
 203{
 204        free(map);
 205}
 206
 207int cpu_map__get_socket(struct cpu_map *map, int idx)
 208{
 209        FILE *fp;
 210        const char *mnt;
 211        char path[PATH_MAX];
 212        int cpu, ret;
 213
 214        if (idx > map->nr)
 215                return -1;
 216
 217        cpu = map->map[idx];
 218
 219        mnt = sysfs__mountpoint();
 220        if (!mnt)
 221                return -1;
 222
 223        snprintf(path, PATH_MAX,
 224                "%s/devices/system/cpu/cpu%d/topology/physical_package_id",
 225                mnt, cpu);
 226
 227        fp = fopen(path, "r");
 228        if (!fp)
 229                return -1;
 230        ret = fscanf(fp, "%d", &cpu);
 231        fclose(fp);
 232        return ret == 1 ? cpu : -1;
 233}
 234
 235static int cmp_ids(const void *a, const void *b)
 236{
 237        return *(int *)a - *(int *)b;
 238}
 239
 240static int cpu_map__build_map(struct cpu_map *cpus, struct cpu_map **res,
 241                              int (*f)(struct cpu_map *map, int cpu))
 242{
 243        struct cpu_map *c;
 244        int nr = cpus->nr;
 245        int cpu, s1, s2;
 246
 247        /* allocate as much as possible */
 248        c = calloc(1, sizeof(*c) + nr * sizeof(int));
 249        if (!c)
 250                return -1;
 251
 252        for (cpu = 0; cpu < nr; cpu++) {
 253                s1 = f(cpus, cpu);
 254                for (s2 = 0; s2 < c->nr; s2++) {
 255                        if (s1 == c->map[s2])
 256                                break;
 257                }
 258                if (s2 == c->nr) {
 259                        c->map[c->nr] = s1;
 260                        c->nr++;
 261                }
 262        }
 263        /* ensure we process id in increasing order */
 264        qsort(c->map, c->nr, sizeof(int), cmp_ids);
 265
 266        *res = c;
 267        return 0;
 268}
 269
 270int cpu_map__get_core(struct cpu_map *map, int idx)
 271{
 272        FILE *fp;
 273        const char *mnt;
 274        char path[PATH_MAX];
 275        int cpu, ret, s;
 276
 277        if (idx > map->nr)
 278                return -1;
 279
 280        cpu = map->map[idx];
 281
 282        mnt = sysfs__mountpoint();
 283        if (!mnt)
 284                return -1;
 285
 286        snprintf(path, PATH_MAX,
 287                "%s/devices/system/cpu/cpu%d/topology/core_id",
 288                mnt, cpu);
 289
 290        fp = fopen(path, "r");
 291        if (!fp)
 292                return -1;
 293        ret = fscanf(fp, "%d", &cpu);
 294        fclose(fp);
 295        if (ret != 1)
 296                return -1;
 297
 298        s = cpu_map__get_socket(map, idx);
 299        if (s == -1)
 300                return -1;
 301
 302        /*
 303         * encode socket in upper 16 bits
 304         * core_id is relative to socket, and
 305         * we need a global id. So we combine
 306         * socket+ core id
 307         */
 308        return (s << 16) | (cpu & 0xffff);
 309}
 310
 311int cpu_map__build_socket_map(struct cpu_map *cpus, struct cpu_map **sockp)
 312{
 313        return cpu_map__build_map(cpus, sockp, cpu_map__get_socket);
 314}
 315
 316int cpu_map__build_core_map(struct cpu_map *cpus, struct cpu_map **corep)
 317{
 318        return cpu_map__build_map(cpus, corep, cpu_map__get_core);
 319}
 320
 321/* setup simple routines to easily access node numbers given a cpu number */
 322static int get_max_num(char *path, int *max)
 323{
 324        size_t num;
 325        char *buf;
 326        int err = 0;
 327
 328        if (filename__read_str(path, &buf, &num))
 329                return -1;
 330
 331        buf[num] = '\0';
 332
 333        /* start on the right, to find highest node num */
 334        while (--num) {
 335                if ((buf[num] == ',') || (buf[num] == '-')) {
 336                        num++;
 337                        break;
 338                }
 339        }
 340        if (sscanf(&buf[num], "%d", max) < 1) {
 341                err = -1;
 342                goto out;
 343        }
 344
 345        /* convert from 0-based to 1-based */
 346        (*max)++;
 347
 348out:
 349        free(buf);
 350        return err;
 351}
 352
 353/* Determine highest possible cpu in the system for sparse allocation */
 354static void set_max_cpu_num(void)
 355{
 356        const char *mnt;
 357        char path[PATH_MAX];
 358        int ret = -1;
 359
 360        /* set up default */
 361        max_cpu_num = 4096;
 362
 363        mnt = sysfs__mountpoint();
 364        if (!mnt)
 365                goto out;
 366
 367        /* get the highest possible cpu number for a sparse allocation */
 368        ret = snprintf(path, PATH_MAX, "%s/devices/system/cpu/possible", mnt);
 369        if (ret == PATH_MAX) {
 370                pr_err("sysfs path crossed PATH_MAX(%d) size\n", PATH_MAX);
 371                goto out;
 372        }
 373
 374        ret = get_max_num(path, &max_cpu_num);
 375
 376out:
 377        if (ret)
 378                pr_err("Failed to read max cpus, using default of %d\n", max_cpu_num);
 379}
 380
 381/* Determine highest possible node in the system for sparse allocation */
 382static void set_max_node_num(void)
 383{
 384        const char *mnt;
 385        char path[PATH_MAX];
 386        int ret = -1;
 387
 388        /* set up default */
 389        max_node_num = 8;
 390
 391        mnt = sysfs__mountpoint();
 392        if (!mnt)
 393                goto out;
 394
 395        /* get the highest possible cpu number for a sparse allocation */
 396        ret = snprintf(path, PATH_MAX, "%s/devices/system/node/possible", mnt);
 397        if (ret == PATH_MAX) {
 398                pr_err("sysfs path crossed PATH_MAX(%d) size\n", PATH_MAX);
 399                goto out;
 400        }
 401
 402        ret = get_max_num(path, &max_node_num);
 403
 404out:
 405        if (ret)
 406                pr_err("Failed to read max nodes, using default of %d\n", max_node_num);
 407}
 408
 409static int init_cpunode_map(void)
 410{
 411        int i;
 412
 413        set_max_cpu_num();
 414        set_max_node_num();
 415
 416        cpunode_map = calloc(max_cpu_num, sizeof(int));
 417        if (!cpunode_map) {
 418                pr_err("%s: calloc failed\n", __func__);
 419                return -1;
 420        }
 421
 422        for (i = 0; i < max_cpu_num; i++)
 423                cpunode_map[i] = -1;
 424
 425        return 0;
 426}
 427
 428int cpu__setup_cpunode_map(void)
 429{
 430        struct dirent *dent1, *dent2;
 431        DIR *dir1, *dir2;
 432        unsigned int cpu, mem;
 433        char buf[PATH_MAX];
 434        char path[PATH_MAX];
 435        const char *mnt;
 436        int n;
 437
 438        /* initialize globals */
 439        if (init_cpunode_map())
 440                return -1;
 441
 442        mnt = sysfs__mountpoint();
 443        if (!mnt)
 444                return 0;
 445
 446        n = snprintf(path, PATH_MAX, "%s/devices/system/node", mnt);
 447        if (n == PATH_MAX) {
 448                pr_err("sysfs path crossed PATH_MAX(%d) size\n", PATH_MAX);
 449                return -1;
 450        }
 451
 452        dir1 = opendir(path);
 453        if (!dir1)
 454                return 0;
 455
 456        /* walk tree and setup map */
 457        while ((dent1 = readdir(dir1)) != NULL) {
 458                if (dent1->d_type != DT_DIR || sscanf(dent1->d_name, "node%u", &mem) < 1)
 459                        continue;
 460
 461                n = snprintf(buf, PATH_MAX, "%s/%s", path, dent1->d_name);
 462                if (n == PATH_MAX) {
 463                        pr_err("sysfs path crossed PATH_MAX(%d) size\n", PATH_MAX);
 464                        continue;
 465                }
 466
 467                dir2 = opendir(buf);
 468                if (!dir2)
 469                        continue;
 470                while ((dent2 = readdir(dir2)) != NULL) {
 471                        if (dent2->d_type != DT_LNK || sscanf(dent2->d_name, "cpu%u", &cpu) < 1)
 472                                continue;
 473                        cpunode_map[cpu] = mem;
 474                }
 475                closedir(dir2);
 476        }
 477        closedir(dir1);
 478        return 0;
 479}
 480