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 static bool has_die_topology(void) 161 { 162 char filename[MAXPATHLEN]; 163 struct utsname uts; 164 165 if (uname(&uts) < 0) 166 return false; 167 168 if (strncmp(uts.machine, "x86_64", 6) && 169 strncmp(uts.machine, "s390x", 5)) 170 return false; 171 172 scnprintf(filename, MAXPATHLEN, DIE_CPUS_FMT, 173 sysfs__mountpoint(), 0); 174 if (access(filename, F_OK) == -1) 175 return false; 176 177 return true; 178 } 179 180 struct cpu_topology *cpu_topology__new(void) 181 { 182 struct cpu_topology *tp = NULL; 183 void *addr; 184 u32 nr, i, nr_addr; 185 size_t sz; 186 long ncpus; 187 int ret = -1; 188 struct perf_cpu_map *map; 189 bool has_die = has_die_topology(); 190 191 ncpus = cpu__max_present_cpu().cpu; 192 193 /* build online CPU map */ 194 map = perf_cpu_map__new(NULL); 195 if (map == NULL) { 196 pr_debug("failed to get system cpumap\n"); 197 return NULL; 198 } 199 200 nr = (u32)(ncpus & UINT_MAX); 201 202 sz = nr * sizeof(char *); 203 if (has_die) 204 nr_addr = 3; 205 else 206 nr_addr = 2; 207 addr = calloc(1, sizeof(*tp) + nr_addr * sz); 208 if (!addr) 209 goto out_free; 210 211 tp = addr; 212 addr += sizeof(*tp); 213 tp->package_cpus_list = addr; 214 addr += sz; 215 if (has_die) { 216 tp->die_cpus_list = addr; 217 addr += sz; 218 } 219 tp->core_cpus_list = addr; 220 221 for (i = 0; i < nr; i++) { 222 if (!perf_cpu_map__has(map, (struct perf_cpu){ .cpu = i })) 223 continue; 224 225 ret = build_cpu_topology(tp, i); 226 if (ret < 0) 227 break; 228 } 229 230 out_free: 231 perf_cpu_map__put(map); 232 if (ret) { 233 cpu_topology__delete(tp); 234 tp = NULL; 235 } 236 return tp; 237 } 238 239 static int load_numa_node(struct numa_topology_node *node, int nr) 240 { 241 char str[MAXPATHLEN]; 242 char field[32]; 243 char *buf = NULL, *p; 244 size_t len = 0; 245 int ret = -1; 246 FILE *fp; 247 u64 mem; 248 249 node->node = (u32) nr; 250 251 scnprintf(str, MAXPATHLEN, NODE_MEMINFO_FMT, 252 sysfs__mountpoint(), nr); 253 fp = fopen(str, "r"); 254 if (!fp) 255 return -1; 256 257 while (getline(&buf, &len, fp) > 0) { 258 /* skip over invalid lines */ 259 if (!strchr(buf, ':')) 260 continue; 261 if (sscanf(buf, "%*s %*d %31s %"PRIu64, field, &mem) != 2) 262 goto err; 263 if (!strcmp(field, "MemTotal:")) 264 node->mem_total = mem; 265 if (!strcmp(field, "MemFree:")) 266 node->mem_free = mem; 267 if (node->mem_total && node->mem_free) 268 break; 269 } 270 271 fclose(fp); 272 fp = NULL; 273 274 scnprintf(str, MAXPATHLEN, NODE_CPULIST_FMT, 275 sysfs__mountpoint(), nr); 276 277 fp = fopen(str, "r"); 278 if (!fp) 279 return -1; 280 281 if (getline(&buf, &len, fp) <= 0) 282 goto err; 283 284 p = strchr(buf, '\n'); 285 if (p) 286 *p = '\0'; 287 288 node->cpus = buf; 289 fclose(fp); 290 return 0; 291 292 err: 293 free(buf); 294 if (fp) 295 fclose(fp); 296 return ret; 297 } 298 299 struct numa_topology *numa_topology__new(void) 300 { 301 struct perf_cpu_map *node_map = NULL; 302 struct numa_topology *tp = NULL; 303 char path[MAXPATHLEN]; 304 char *buf = NULL; 305 size_t len = 0; 306 u32 nr, i; 307 FILE *fp; 308 char *c; 309 310 scnprintf(path, MAXPATHLEN, NODE_ONLINE_FMT, 311 sysfs__mountpoint()); 312 313 fp = fopen(path, "r"); 314 if (!fp) 315 return NULL; 316 317 if (getline(&buf, &len, fp) <= 0) 318 goto out; 319 320 c = strchr(buf, '\n'); 321 if (c) 322 *c = '\0'; 323 324 node_map = perf_cpu_map__new(buf); 325 if (!node_map) 326 goto out; 327 328 nr = (u32) perf_cpu_map__nr(node_map); 329 330 tp = zalloc(sizeof(*tp) + sizeof(tp->nodes[0])*nr); 331 if (!tp) 332 goto out; 333 334 tp->nr = nr; 335 336 for (i = 0; i < nr; i++) { 337 if (load_numa_node(&tp->nodes[i], perf_cpu_map__cpu(node_map, i).cpu)) { 338 numa_topology__delete(tp); 339 tp = NULL; 340 break; 341 } 342 } 343 344 out: 345 free(buf); 346 fclose(fp); 347 perf_cpu_map__put(node_map); 348 return tp; 349 } 350 351 void numa_topology__delete(struct numa_topology *tp) 352 { 353 u32 i; 354 355 for (i = 0; i < tp->nr; i++) 356 zfree(&tp->nodes[i].cpus); 357 358 free(tp); 359 } 360 361 static int load_hybrid_node(struct hybrid_topology_node *node, 362 struct perf_pmu *pmu) 363 { 364 const char *sysfs; 365 char path[PATH_MAX]; 366 char *buf = NULL, *p; 367 FILE *fp; 368 size_t len = 0; 369 370 node->pmu_name = strdup(pmu->name); 371 if (!node->pmu_name) 372 return -1; 373 374 sysfs = sysfs__mountpoint(); 375 if (!sysfs) 376 goto err; 377 378 snprintf(path, PATH_MAX, CPUS_TEMPLATE_CPU, sysfs, pmu->name); 379 fp = fopen(path, "r"); 380 if (!fp) 381 goto err; 382 383 if (getline(&buf, &len, fp) <= 0) { 384 fclose(fp); 385 goto err; 386 } 387 388 p = strchr(buf, '\n'); 389 if (p) 390 *p = '\0'; 391 392 fclose(fp); 393 node->cpus = buf; 394 return 0; 395 396 err: 397 zfree(&node->pmu_name); 398 free(buf); 399 return -1; 400 } 401 402 struct hybrid_topology *hybrid_topology__new(void) 403 { 404 struct perf_pmu *pmu; 405 struct hybrid_topology *tp = NULL; 406 u32 nr, i = 0; 407 408 nr = perf_pmu__hybrid_pmu_num(); 409 if (nr == 0) 410 return NULL; 411 412 tp = zalloc(sizeof(*tp) + sizeof(tp->nodes[0]) * nr); 413 if (!tp) 414 return NULL; 415 416 tp->nr = nr; 417 perf_pmu__for_each_hybrid_pmu(pmu) { 418 if (load_hybrid_node(&tp->nodes[i], pmu)) { 419 hybrid_topology__delete(tp); 420 return NULL; 421 } 422 i++; 423 } 424 425 return tp; 426 } 427 428 void hybrid_topology__delete(struct hybrid_topology *tp) 429 { 430 u32 i; 431 432 for (i = 0; i < tp->nr; i++) { 433 zfree(&tp->nodes[i].pmu_name); 434 zfree(&tp->nodes[i].cpus); 435 } 436 437 free(tp); 438 } 439