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 CORE_SIB_FMT \ 18 "%s/devices/system/cpu/cpu%d/topology/core_siblings_list" 19 #define DIE_SIB_FMT \ 20 "%s/devices/system/cpu/cpu%d/topology/die_cpus_list" 21 #define THRD_SIB_FMT \ 22 "%s/devices/system/cpu/cpu%d/topology/thread_siblings_list" 23 #define THRD_SIB_FMT_NEW \ 24 "%s/devices/system/cpu/cpu%d/topology/core_cpus_list" 25 #define NODE_ONLINE_FMT \ 26 "%s/devices/system/node/online" 27 #define NODE_MEMINFO_FMT \ 28 "%s/devices/system/node/node%d/meminfo" 29 #define NODE_CPULIST_FMT \ 30 "%s/devices/system/node/node%d/cpulist" 31 32 static int build_cpu_topology(struct cpu_topology *tp, int cpu) 33 { 34 FILE *fp; 35 char filename[MAXPATHLEN]; 36 char *buf = NULL, *p; 37 size_t len = 0; 38 ssize_t sret; 39 u32 i = 0; 40 int ret = -1; 41 42 scnprintf(filename, MAXPATHLEN, CORE_SIB_FMT, 43 sysfs__mountpoint(), cpu); 44 fp = fopen(filename, "r"); 45 if (!fp) 46 goto try_dies; 47 48 sret = getline(&buf, &len, fp); 49 fclose(fp); 50 if (sret <= 0) 51 goto try_dies; 52 53 p = strchr(buf, '\n'); 54 if (p) 55 *p = '\0'; 56 57 for (i = 0; i < tp->core_sib; i++) { 58 if (!strcmp(buf, tp->core_siblings[i])) 59 break; 60 } 61 if (i == tp->core_sib) { 62 tp->core_siblings[i] = buf; 63 tp->core_sib++; 64 buf = NULL; 65 len = 0; 66 } 67 ret = 0; 68 69 try_dies: 70 if (!tp->die_siblings) 71 goto try_threads; 72 73 scnprintf(filename, MAXPATHLEN, DIE_SIB_FMT, 74 sysfs__mountpoint(), cpu); 75 fp = fopen(filename, "r"); 76 if (!fp) 77 goto try_threads; 78 79 sret = getline(&buf, &len, fp); 80 fclose(fp); 81 if (sret <= 0) 82 goto try_threads; 83 84 p = strchr(buf, '\n'); 85 if (p) 86 *p = '\0'; 87 88 for (i = 0; i < tp->die_sib; i++) { 89 if (!strcmp(buf, tp->die_siblings[i])) 90 break; 91 } 92 if (i == tp->die_sib) { 93 tp->die_siblings[i] = buf; 94 tp->die_sib++; 95 buf = NULL; 96 len = 0; 97 } 98 ret = 0; 99 100 try_threads: 101 scnprintf(filename, MAXPATHLEN, THRD_SIB_FMT_NEW, 102 sysfs__mountpoint(), cpu); 103 if (access(filename, F_OK) == -1) { 104 scnprintf(filename, MAXPATHLEN, THRD_SIB_FMT, 105 sysfs__mountpoint(), cpu); 106 } 107 fp = fopen(filename, "r"); 108 if (!fp) 109 goto done; 110 111 if (getline(&buf, &len, fp) <= 0) 112 goto done; 113 114 p = strchr(buf, '\n'); 115 if (p) 116 *p = '\0'; 117 118 for (i = 0; i < tp->thread_sib; i++) { 119 if (!strcmp(buf, tp->thread_siblings[i])) 120 break; 121 } 122 if (i == tp->thread_sib) { 123 tp->thread_siblings[i] = buf; 124 tp->thread_sib++; 125 buf = NULL; 126 } 127 ret = 0; 128 done: 129 if (fp) 130 fclose(fp); 131 free(buf); 132 return ret; 133 } 134 135 void cpu_topology__delete(struct cpu_topology *tp) 136 { 137 u32 i; 138 139 if (!tp) 140 return; 141 142 for (i = 0 ; i < tp->core_sib; i++) 143 zfree(&tp->core_siblings[i]); 144 145 if (tp->die_sib) { 146 for (i = 0 ; i < tp->die_sib; i++) 147 zfree(&tp->die_siblings[i]); 148 } 149 150 for (i = 0 ; i < tp->thread_sib; i++) 151 zfree(&tp->thread_siblings[i]); 152 153 free(tp); 154 } 155 156 static bool has_die_topology(void) 157 { 158 char filename[MAXPATHLEN]; 159 struct utsname uts; 160 161 if (uname(&uts) < 0) 162 return false; 163 164 if (strncmp(uts.machine, "x86_64", 6)) 165 return false; 166 167 scnprintf(filename, MAXPATHLEN, DIE_SIB_FMT, 168 sysfs__mountpoint(), 0); 169 if (access(filename, F_OK) == -1) 170 return false; 171 172 return true; 173 } 174 175 struct cpu_topology *cpu_topology__new(void) 176 { 177 struct cpu_topology *tp = NULL; 178 void *addr; 179 u32 nr, i, nr_addr; 180 size_t sz; 181 long ncpus; 182 int ret = -1; 183 struct perf_cpu_map *map; 184 bool has_die = has_die_topology(); 185 186 ncpus = cpu__max_present_cpu(); 187 188 /* build online CPU map */ 189 map = perf_cpu_map__new(NULL); 190 if (map == NULL) { 191 pr_debug("failed to get system cpumap\n"); 192 return NULL; 193 } 194 195 nr = (u32)(ncpus & UINT_MAX); 196 197 sz = nr * sizeof(char *); 198 if (has_die) 199 nr_addr = 3; 200 else 201 nr_addr = 2; 202 addr = calloc(1, sizeof(*tp) + nr_addr * sz); 203 if (!addr) 204 goto out_free; 205 206 tp = addr; 207 addr += sizeof(*tp); 208 tp->core_siblings = addr; 209 addr += sz; 210 if (has_die) { 211 tp->die_siblings = addr; 212 addr += sz; 213 } 214 tp->thread_siblings = addr; 215 216 for (i = 0; i < nr; i++) { 217 if (!cpu_map__has(map, i)) 218 continue; 219 220 ret = build_cpu_topology(tp, i); 221 if (ret < 0) 222 break; 223 } 224 225 out_free: 226 perf_cpu_map__put(map); 227 if (ret) { 228 cpu_topology__delete(tp); 229 tp = NULL; 230 } 231 return tp; 232 } 233 234 static int load_numa_node(struct numa_topology_node *node, int nr) 235 { 236 char str[MAXPATHLEN]; 237 char field[32]; 238 char *buf = NULL, *p; 239 size_t len = 0; 240 int ret = -1; 241 FILE *fp; 242 u64 mem; 243 244 node->node = (u32) nr; 245 246 scnprintf(str, MAXPATHLEN, NODE_MEMINFO_FMT, 247 sysfs__mountpoint(), nr); 248 fp = fopen(str, "r"); 249 if (!fp) 250 return -1; 251 252 while (getline(&buf, &len, fp) > 0) { 253 /* skip over invalid lines */ 254 if (!strchr(buf, ':')) 255 continue; 256 if (sscanf(buf, "%*s %*d %31s %"PRIu64, field, &mem) != 2) 257 goto err; 258 if (!strcmp(field, "MemTotal:")) 259 node->mem_total = mem; 260 if (!strcmp(field, "MemFree:")) 261 node->mem_free = mem; 262 if (node->mem_total && node->mem_free) 263 break; 264 } 265 266 fclose(fp); 267 fp = NULL; 268 269 scnprintf(str, MAXPATHLEN, NODE_CPULIST_FMT, 270 sysfs__mountpoint(), nr); 271 272 fp = fopen(str, "r"); 273 if (!fp) 274 return -1; 275 276 if (getline(&buf, &len, fp) <= 0) 277 goto err; 278 279 p = strchr(buf, '\n'); 280 if (p) 281 *p = '\0'; 282 283 node->cpus = buf; 284 fclose(fp); 285 return 0; 286 287 err: 288 free(buf); 289 if (fp) 290 fclose(fp); 291 return ret; 292 } 293 294 struct numa_topology *numa_topology__new(void) 295 { 296 struct perf_cpu_map *node_map = NULL; 297 struct numa_topology *tp = NULL; 298 char path[MAXPATHLEN]; 299 char *buf = NULL; 300 size_t len = 0; 301 u32 nr, i; 302 FILE *fp; 303 char *c; 304 305 scnprintf(path, MAXPATHLEN, NODE_ONLINE_FMT, 306 sysfs__mountpoint()); 307 308 fp = fopen(path, "r"); 309 if (!fp) 310 return NULL; 311 312 if (getline(&buf, &len, fp) <= 0) 313 goto out; 314 315 c = strchr(buf, '\n'); 316 if (c) 317 *c = '\0'; 318 319 node_map = perf_cpu_map__new(buf); 320 if (!node_map) 321 goto out; 322 323 nr = (u32) node_map->nr; 324 325 tp = zalloc(sizeof(*tp) + sizeof(tp->nodes[0])*nr); 326 if (!tp) 327 goto out; 328 329 tp->nr = nr; 330 331 for (i = 0; i < nr; i++) { 332 if (load_numa_node(&tp->nodes[i], node_map->map[i])) { 333 numa_topology__delete(tp); 334 tp = NULL; 335 break; 336 } 337 } 338 339 out: 340 free(buf); 341 fclose(fp); 342 perf_cpu_map__put(node_map); 343 return tp; 344 } 345 346 void numa_topology__delete(struct numa_topology *tp) 347 { 348 u32 i; 349 350 for (i = 0; i < tp->nr; i++) 351 zfree(&tp->nodes[i].cpus); 352 353 free(tp); 354 } 355 356 static int load_hybrid_node(struct hybrid_topology_node *node, 357 struct perf_pmu *pmu) 358 { 359 const char *sysfs; 360 char path[PATH_MAX]; 361 char *buf = NULL, *p; 362 FILE *fp; 363 size_t len = 0; 364 365 node->pmu_name = strdup(pmu->name); 366 if (!node->pmu_name) 367 return -1; 368 369 sysfs = sysfs__mountpoint(); 370 if (!sysfs) 371 goto err; 372 373 snprintf(path, PATH_MAX, CPUS_TEMPLATE_CPU, sysfs, pmu->name); 374 fp = fopen(path, "r"); 375 if (!fp) 376 goto err; 377 378 if (getline(&buf, &len, fp) <= 0) { 379 fclose(fp); 380 goto err; 381 } 382 383 p = strchr(buf, '\n'); 384 if (p) 385 *p = '\0'; 386 387 fclose(fp); 388 node->cpus = buf; 389 return 0; 390 391 err: 392 zfree(&node->pmu_name); 393 free(buf); 394 return -1; 395 } 396 397 struct hybrid_topology *hybrid_topology__new(void) 398 { 399 struct perf_pmu *pmu; 400 struct hybrid_topology *tp = NULL; 401 u32 nr, i = 0; 402 403 nr = perf_pmu__hybrid_pmu_num(); 404 if (nr == 0) 405 return NULL; 406 407 tp = zalloc(sizeof(*tp) + sizeof(tp->nodes[0]) * nr); 408 if (!tp) 409 return NULL; 410 411 tp->nr = nr; 412 perf_pmu__for_each_hybrid_pmu(pmu) { 413 if (load_hybrid_node(&tp->nodes[i], pmu)) { 414 hybrid_topology__delete(tp); 415 return NULL; 416 } 417 i++; 418 } 419 420 return tp; 421 } 422 423 void hybrid_topology__delete(struct hybrid_topology *tp) 424 { 425 u32 i; 426 427 for (i = 0; i < tp->nr; i++) { 428 zfree(&tp->nodes[i].pmu_name); 429 zfree(&tp->nodes[i].cpus); 430 } 431 432 free(tp); 433 } 434