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