507 lines · c
1// SPDX-License-Identifier: GPL-2.02#include <sys/param.h>3#include <sys/utsname.h>4#include <inttypes.h>5#include <stdlib.h>6#include <string.h>7#include <api/fs/fs.h>8#include <linux/zalloc.h>9#include <perf/cpumap.h>10 11#include "cputopo.h"12#include "cpumap.h"13#include "debug.h"14#include "env.h"15#include "pmu.h"16#include "pmus.h"17 18#define PACKAGE_CPUS_FMT \19 "%s/devices/system/cpu/cpu%d/topology/package_cpus_list"20#define PACKAGE_CPUS_FMT_OLD \21 "%s/devices/system/cpu/cpu%d/topology/core_siblings_list"22#define DIE_CPUS_FMT \23 "%s/devices/system/cpu/cpu%d/topology/die_cpus_list"24#define CORE_CPUS_FMT \25 "%s/devices/system/cpu/cpu%d/topology/core_cpus_list"26#define CORE_CPUS_FMT_OLD \27 "%s/devices/system/cpu/cpu%d/topology/thread_siblings_list"28#define NODE_ONLINE_FMT \29 "%s/devices/system/node/online"30#define NODE_MEMINFO_FMT \31 "%s/devices/system/node/node%d/meminfo"32#define NODE_CPULIST_FMT \33 "%s/devices/system/node/node%d/cpulist"34 35static int build_cpu_topology(struct cpu_topology *tp, int cpu)36{37 FILE *fp;38 char filename[MAXPATHLEN];39 char *buf = NULL, *p;40 size_t len = 0;41 ssize_t sret;42 u32 i = 0;43 int ret = -1;44 45 scnprintf(filename, MAXPATHLEN, PACKAGE_CPUS_FMT,46 sysfs__mountpoint(), cpu);47 if (access(filename, F_OK) == -1) {48 scnprintf(filename, MAXPATHLEN, PACKAGE_CPUS_FMT_OLD,49 sysfs__mountpoint(), cpu);50 }51 fp = fopen(filename, "r");52 if (!fp)53 goto try_dies;54 55 sret = getline(&buf, &len, fp);56 fclose(fp);57 if (sret <= 0)58 goto try_dies;59 60 p = strchr(buf, '\n');61 if (p)62 *p = '\0';63 64 for (i = 0; i < tp->package_cpus_lists; i++) {65 if (!strcmp(buf, tp->package_cpus_list[i]))66 break;67 }68 if (i == tp->package_cpus_lists) {69 tp->package_cpus_list[i] = buf;70 tp->package_cpus_lists++;71 buf = NULL;72 len = 0;73 }74 ret = 0;75 76try_dies:77 if (!tp->die_cpus_list)78 goto try_threads;79 80 scnprintf(filename, MAXPATHLEN, DIE_CPUS_FMT,81 sysfs__mountpoint(), cpu);82 fp = fopen(filename, "r");83 if (!fp)84 goto try_threads;85 86 sret = getline(&buf, &len, fp);87 fclose(fp);88 if (sret <= 0)89 goto try_threads;90 91 p = strchr(buf, '\n');92 if (p)93 *p = '\0';94 95 for (i = 0; i < tp->die_cpus_lists; i++) {96 if (!strcmp(buf, tp->die_cpus_list[i]))97 break;98 }99 if (i == tp->die_cpus_lists) {100 tp->die_cpus_list[i] = buf;101 tp->die_cpus_lists++;102 buf = NULL;103 len = 0;104 }105 ret = 0;106 107try_threads:108 scnprintf(filename, MAXPATHLEN, CORE_CPUS_FMT,109 sysfs__mountpoint(), cpu);110 if (access(filename, F_OK) == -1) {111 scnprintf(filename, MAXPATHLEN, CORE_CPUS_FMT_OLD,112 sysfs__mountpoint(), cpu);113 }114 fp = fopen(filename, "r");115 if (!fp)116 goto done;117 118 if (getline(&buf, &len, fp) <= 0)119 goto done;120 121 p = strchr(buf, '\n');122 if (p)123 *p = '\0';124 125 for (i = 0; i < tp->core_cpus_lists; i++) {126 if (!strcmp(buf, tp->core_cpus_list[i]))127 break;128 }129 if (i == tp->core_cpus_lists) {130 tp->core_cpus_list[i] = buf;131 tp->core_cpus_lists++;132 buf = NULL;133 }134 ret = 0;135done:136 if (fp)137 fclose(fp);138 free(buf);139 return ret;140}141 142void cpu_topology__delete(struct cpu_topology *tp)143{144 u32 i;145 146 if (!tp)147 return;148 149 for (i = 0 ; i < tp->package_cpus_lists; i++)150 zfree(&tp->package_cpus_list[i]);151 152 for (i = 0 ; i < tp->die_cpus_lists; i++)153 zfree(&tp->die_cpus_list[i]);154 155 for (i = 0 ; i < tp->core_cpus_lists; i++)156 zfree(&tp->core_cpus_list[i]);157 158 free(tp);159}160 161bool cpu_topology__smt_on(const struct cpu_topology *topology)162{163 for (u32 i = 0; i < topology->core_cpus_lists; i++) {164 const char *cpu_list = topology->core_cpus_list[i];165 166 /*167 * If there is a need to separate siblings in a core then SMT is168 * enabled.169 */170 if (strchr(cpu_list, ',') || strchr(cpu_list, '-'))171 return true;172 }173 return false;174}175 176bool cpu_topology__core_wide(const struct cpu_topology *topology,177 const char *user_requested_cpu_list)178{179 struct perf_cpu_map *user_requested_cpus;180 181 /*182 * If user_requested_cpu_list is empty then all CPUs are recorded and so183 * core_wide is true.184 */185 if (!user_requested_cpu_list)186 return true;187 188 user_requested_cpus = perf_cpu_map__new(user_requested_cpu_list);189 /* Check that every user requested CPU is the complete set of SMT threads on a core. */190 for (u32 i = 0; i < topology->core_cpus_lists; i++) {191 const char *core_cpu_list = topology->core_cpus_list[i];192 struct perf_cpu_map *core_cpus = perf_cpu_map__new(core_cpu_list);193 struct perf_cpu cpu;194 int idx;195 bool has_first, first = true;196 197 perf_cpu_map__for_each_cpu(cpu, idx, core_cpus) {198 if (first) {199 has_first = perf_cpu_map__has(user_requested_cpus, cpu);200 first = false;201 } else {202 /*203 * If the first core CPU is user requested then204 * all subsequent CPUs in the core must be user205 * requested too. If the first CPU isn't user206 * requested then none of the others must be207 * too.208 */209 if (perf_cpu_map__has(user_requested_cpus, cpu) != has_first) {210 perf_cpu_map__put(core_cpus);211 perf_cpu_map__put(user_requested_cpus);212 return false;213 }214 }215 }216 perf_cpu_map__put(core_cpus);217 }218 perf_cpu_map__put(user_requested_cpus);219 return true;220}221 222static bool has_die_topology(void)223{224 char filename[MAXPATHLEN];225 struct utsname uts;226 227 if (uname(&uts) < 0)228 return false;229 230 if (strncmp(uts.machine, "x86_64", 6) &&231 strncmp(uts.machine, "s390x", 5))232 return false;233 234 scnprintf(filename, MAXPATHLEN, DIE_CPUS_FMT,235 sysfs__mountpoint(), 0);236 if (access(filename, F_OK) == -1)237 return false;238 239 return true;240}241 242const struct cpu_topology *online_topology(void)243{244 static const struct cpu_topology *topology;245 246 if (!topology) {247 topology = cpu_topology__new();248 if (!topology) {249 pr_err("Error creating CPU topology");250 abort();251 }252 }253 return topology;254}255 256struct cpu_topology *cpu_topology__new(void)257{258 struct cpu_topology *tp = NULL;259 void *addr;260 u32 nr, i, nr_addr;261 size_t sz;262 long ncpus;263 int ret = -1;264 struct perf_cpu_map *map;265 bool has_die = has_die_topology();266 267 ncpus = cpu__max_present_cpu().cpu;268 269 /* build online CPU map */270 map = perf_cpu_map__new_online_cpus();271 if (map == NULL) {272 pr_debug("failed to get system cpumap\n");273 return NULL;274 }275 276 nr = (u32)(ncpus & UINT_MAX);277 278 sz = nr * sizeof(char *);279 if (has_die)280 nr_addr = 3;281 else282 nr_addr = 2;283 addr = calloc(1, sizeof(*tp) + nr_addr * sz);284 if (!addr)285 goto out_free;286 287 tp = addr;288 addr += sizeof(*tp);289 tp->package_cpus_list = addr;290 addr += sz;291 if (has_die) {292 tp->die_cpus_list = addr;293 addr += sz;294 }295 tp->core_cpus_list = addr;296 297 for (i = 0; i < nr; i++) {298 if (!perf_cpu_map__has(map, (struct perf_cpu){ .cpu = i }))299 continue;300 301 ret = build_cpu_topology(tp, i);302 if (ret < 0)303 break;304 }305 306out_free:307 perf_cpu_map__put(map);308 if (ret) {309 cpu_topology__delete(tp);310 tp = NULL;311 }312 return tp;313}314 315static int load_numa_node(struct numa_topology_node *node, int nr)316{317 char str[MAXPATHLEN];318 char field[32];319 char *buf = NULL, *p;320 size_t len = 0;321 int ret = -1;322 FILE *fp;323 u64 mem;324 325 node->node = (u32) nr;326 327 scnprintf(str, MAXPATHLEN, NODE_MEMINFO_FMT,328 sysfs__mountpoint(), nr);329 fp = fopen(str, "r");330 if (!fp)331 return -1;332 333 while (getline(&buf, &len, fp) > 0) {334 /* skip over invalid lines */335 if (!strchr(buf, ':'))336 continue;337 if (sscanf(buf, "%*s %*d %31s %"PRIu64, field, &mem) != 2)338 goto err;339 if (!strcmp(field, "MemTotal:"))340 node->mem_total = mem;341 if (!strcmp(field, "MemFree:"))342 node->mem_free = mem;343 if (node->mem_total && node->mem_free)344 break;345 }346 347 fclose(fp);348 fp = NULL;349 350 scnprintf(str, MAXPATHLEN, NODE_CPULIST_FMT,351 sysfs__mountpoint(), nr);352 353 fp = fopen(str, "r");354 if (!fp)355 return -1;356 357 if (getline(&buf, &len, fp) <= 0)358 goto err;359 360 p = strchr(buf, '\n');361 if (p)362 *p = '\0';363 364 node->cpus = buf;365 fclose(fp);366 return 0;367 368err:369 free(buf);370 if (fp)371 fclose(fp);372 return ret;373}374 375struct numa_topology *numa_topology__new(void)376{377 struct perf_cpu_map *node_map = NULL;378 struct numa_topology *tp = NULL;379 char path[MAXPATHLEN];380 char *buf = NULL;381 size_t len = 0;382 u32 nr, i;383 FILE *fp;384 char *c;385 386 scnprintf(path, MAXPATHLEN, NODE_ONLINE_FMT,387 sysfs__mountpoint());388 389 fp = fopen(path, "r");390 if (!fp)391 return NULL;392 393 if (getline(&buf, &len, fp) <= 0)394 goto out;395 396 c = strchr(buf, '\n');397 if (c)398 *c = '\0';399 400 node_map = perf_cpu_map__new(buf);401 if (!node_map)402 goto out;403 404 nr = (u32) perf_cpu_map__nr(node_map);405 406 tp = zalloc(sizeof(*tp) + sizeof(tp->nodes[0])*nr);407 if (!tp)408 goto out;409 410 tp->nr = nr;411 412 for (i = 0; i < nr; i++) {413 if (load_numa_node(&tp->nodes[i], perf_cpu_map__cpu(node_map, i).cpu)) {414 numa_topology__delete(tp);415 tp = NULL;416 break;417 }418 }419 420out:421 free(buf);422 fclose(fp);423 perf_cpu_map__put(node_map);424 return tp;425}426 427void numa_topology__delete(struct numa_topology *tp)428{429 u32 i;430 431 for (i = 0; i < tp->nr; i++)432 zfree(&tp->nodes[i].cpus);433 434 free(tp);435}436 437static int load_hybrid_node(struct hybrid_topology_node *node,438 struct perf_pmu *pmu)439{440 char *buf = NULL, *p;441 FILE *fp;442 size_t len = 0;443 444 node->pmu_name = strdup(pmu->name);445 if (!node->pmu_name)446 return -1;447 448 fp = perf_pmu__open_file(pmu, "cpus");449 if (!fp)450 goto err;451 452 if (getline(&buf, &len, fp) <= 0) {453 fclose(fp);454 goto err;455 }456 457 p = strchr(buf, '\n');458 if (p)459 *p = '\0';460 461 fclose(fp);462 node->cpus = buf;463 return 0;464 465err:466 zfree(&node->pmu_name);467 free(buf);468 return -1;469}470 471struct hybrid_topology *hybrid_topology__new(void)472{473 struct perf_pmu *pmu = NULL;474 struct hybrid_topology *tp = NULL;475 int nr = perf_pmus__num_core_pmus(), i = 0;476 477 if (nr <= 1)478 return NULL;479 480 tp = zalloc(sizeof(*tp) + sizeof(tp->nodes[0]) * nr);481 if (!tp)482 return NULL;483 484 tp->nr = nr;485 while ((pmu = perf_pmus__scan_core(pmu)) != NULL) {486 if (load_hybrid_node(&tp->nodes[i], pmu)) {487 hybrid_topology__delete(tp);488 return NULL;489 }490 i++;491 }492 493 return tp;494}495 496void hybrid_topology__delete(struct hybrid_topology *tp)497{498 u32 i;499 500 for (i = 0; i < tp->nr; i++) {501 zfree(&tp->nodes[i].pmu_name);502 zfree(&tp->nodes[i].cpus);503 }504 505 free(tp);506}507