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.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 35 static 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 76 try_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 107 try_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; 135 done: 136 if (fp) 137 fclose(fp); 138 free(buf); 139 return ret; 140 } 141 142 void 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 161 bool 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 is 168 * enabled. 169 */ 170 if (strchr(cpu_list, ',') || strchr(cpu_list, '-')) 171 return true; 172 } 173 return false; 174 } 175 176 bool 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 so 183 * 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 then 204 * all subsequent CPUs in the core must be user 205 * requested too. If the first CPU isn't user 206 * requested then none of the others must be 207 * 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 222 static 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 242 const 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 256 struct 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 else 282 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 306 out_free: 307 perf_cpu_map__put(map); 308 if (ret) { 309 cpu_topology__delete(tp); 310 tp = NULL; 311 } 312 return tp; 313 } 314 315 static 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 368 err: 369 free(buf); 370 if (fp) 371 fclose(fp); 372 return ret; 373 } 374 375 struct 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 420 out: 421 free(buf); 422 fclose(fp); 423 perf_cpu_map__put(node_map); 424 return tp; 425 } 426 427 void 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 437 static 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 465 err: 466 zfree(&node->pmu_name); 467 free(buf); 468 return -1; 469 } 470 471 struct 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 496 void 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
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.