18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci#include <sys/param.h> 38c2ecf20Sopenharmony_ci#include <sys/utsname.h> 48c2ecf20Sopenharmony_ci#include <inttypes.h> 58c2ecf20Sopenharmony_ci#include <stdlib.h> 68c2ecf20Sopenharmony_ci#include <string.h> 78c2ecf20Sopenharmony_ci#include <api/fs/fs.h> 88c2ecf20Sopenharmony_ci#include <linux/zalloc.h> 98c2ecf20Sopenharmony_ci#include <perf/cpumap.h> 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include "cputopo.h" 128c2ecf20Sopenharmony_ci#include "cpumap.h" 138c2ecf20Sopenharmony_ci#include "debug.h" 148c2ecf20Sopenharmony_ci#include "env.h" 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#define CORE_SIB_FMT \ 178c2ecf20Sopenharmony_ci "%s/devices/system/cpu/cpu%d/topology/core_siblings_list" 188c2ecf20Sopenharmony_ci#define DIE_SIB_FMT \ 198c2ecf20Sopenharmony_ci "%s/devices/system/cpu/cpu%d/topology/die_cpus_list" 208c2ecf20Sopenharmony_ci#define THRD_SIB_FMT \ 218c2ecf20Sopenharmony_ci "%s/devices/system/cpu/cpu%d/topology/thread_siblings_list" 228c2ecf20Sopenharmony_ci#define THRD_SIB_FMT_NEW \ 238c2ecf20Sopenharmony_ci "%s/devices/system/cpu/cpu%d/topology/core_cpus_list" 248c2ecf20Sopenharmony_ci#define NODE_ONLINE_FMT \ 258c2ecf20Sopenharmony_ci "%s/devices/system/node/online" 268c2ecf20Sopenharmony_ci#define NODE_MEMINFO_FMT \ 278c2ecf20Sopenharmony_ci "%s/devices/system/node/node%d/meminfo" 288c2ecf20Sopenharmony_ci#define NODE_CPULIST_FMT \ 298c2ecf20Sopenharmony_ci "%s/devices/system/node/node%d/cpulist" 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_cistatic int build_cpu_topology(struct cpu_topology *tp, int cpu) 328c2ecf20Sopenharmony_ci{ 338c2ecf20Sopenharmony_ci FILE *fp; 348c2ecf20Sopenharmony_ci char filename[MAXPATHLEN]; 358c2ecf20Sopenharmony_ci char *buf = NULL, *p; 368c2ecf20Sopenharmony_ci size_t len = 0; 378c2ecf20Sopenharmony_ci ssize_t sret; 388c2ecf20Sopenharmony_ci u32 i = 0; 398c2ecf20Sopenharmony_ci int ret = -1; 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci scnprintf(filename, MAXPATHLEN, CORE_SIB_FMT, 428c2ecf20Sopenharmony_ci sysfs__mountpoint(), cpu); 438c2ecf20Sopenharmony_ci fp = fopen(filename, "r"); 448c2ecf20Sopenharmony_ci if (!fp) 458c2ecf20Sopenharmony_ci goto try_dies; 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci sret = getline(&buf, &len, fp); 488c2ecf20Sopenharmony_ci fclose(fp); 498c2ecf20Sopenharmony_ci if (sret <= 0) 508c2ecf20Sopenharmony_ci goto try_dies; 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci p = strchr(buf, '\n'); 538c2ecf20Sopenharmony_ci if (p) 548c2ecf20Sopenharmony_ci *p = '\0'; 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci for (i = 0; i < tp->core_sib; i++) { 578c2ecf20Sopenharmony_ci if (!strcmp(buf, tp->core_siblings[i])) 588c2ecf20Sopenharmony_ci break; 598c2ecf20Sopenharmony_ci } 608c2ecf20Sopenharmony_ci if (i == tp->core_sib) { 618c2ecf20Sopenharmony_ci tp->core_siblings[i] = buf; 628c2ecf20Sopenharmony_ci tp->core_sib++; 638c2ecf20Sopenharmony_ci buf = NULL; 648c2ecf20Sopenharmony_ci len = 0; 658c2ecf20Sopenharmony_ci } 668c2ecf20Sopenharmony_ci ret = 0; 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_citry_dies: 698c2ecf20Sopenharmony_ci if (!tp->die_siblings) 708c2ecf20Sopenharmony_ci goto try_threads; 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci scnprintf(filename, MAXPATHLEN, DIE_SIB_FMT, 738c2ecf20Sopenharmony_ci sysfs__mountpoint(), cpu); 748c2ecf20Sopenharmony_ci fp = fopen(filename, "r"); 758c2ecf20Sopenharmony_ci if (!fp) 768c2ecf20Sopenharmony_ci goto try_threads; 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci sret = getline(&buf, &len, fp); 798c2ecf20Sopenharmony_ci fclose(fp); 808c2ecf20Sopenharmony_ci if (sret <= 0) 818c2ecf20Sopenharmony_ci goto try_threads; 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci p = strchr(buf, '\n'); 848c2ecf20Sopenharmony_ci if (p) 858c2ecf20Sopenharmony_ci *p = '\0'; 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci for (i = 0; i < tp->die_sib; i++) { 888c2ecf20Sopenharmony_ci if (!strcmp(buf, tp->die_siblings[i])) 898c2ecf20Sopenharmony_ci break; 908c2ecf20Sopenharmony_ci } 918c2ecf20Sopenharmony_ci if (i == tp->die_sib) { 928c2ecf20Sopenharmony_ci tp->die_siblings[i] = buf; 938c2ecf20Sopenharmony_ci tp->die_sib++; 948c2ecf20Sopenharmony_ci buf = NULL; 958c2ecf20Sopenharmony_ci len = 0; 968c2ecf20Sopenharmony_ci } 978c2ecf20Sopenharmony_ci ret = 0; 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_citry_threads: 1008c2ecf20Sopenharmony_ci scnprintf(filename, MAXPATHLEN, THRD_SIB_FMT_NEW, 1018c2ecf20Sopenharmony_ci sysfs__mountpoint(), cpu); 1028c2ecf20Sopenharmony_ci if (access(filename, F_OK) == -1) { 1038c2ecf20Sopenharmony_ci scnprintf(filename, MAXPATHLEN, THRD_SIB_FMT, 1048c2ecf20Sopenharmony_ci sysfs__mountpoint(), cpu); 1058c2ecf20Sopenharmony_ci } 1068c2ecf20Sopenharmony_ci fp = fopen(filename, "r"); 1078c2ecf20Sopenharmony_ci if (!fp) 1088c2ecf20Sopenharmony_ci goto done; 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci if (getline(&buf, &len, fp) <= 0) 1118c2ecf20Sopenharmony_ci goto done; 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci p = strchr(buf, '\n'); 1148c2ecf20Sopenharmony_ci if (p) 1158c2ecf20Sopenharmony_ci *p = '\0'; 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci for (i = 0; i < tp->thread_sib; i++) { 1188c2ecf20Sopenharmony_ci if (!strcmp(buf, tp->thread_siblings[i])) 1198c2ecf20Sopenharmony_ci break; 1208c2ecf20Sopenharmony_ci } 1218c2ecf20Sopenharmony_ci if (i == tp->thread_sib) { 1228c2ecf20Sopenharmony_ci tp->thread_siblings[i] = buf; 1238c2ecf20Sopenharmony_ci tp->thread_sib++; 1248c2ecf20Sopenharmony_ci buf = NULL; 1258c2ecf20Sopenharmony_ci } 1268c2ecf20Sopenharmony_ci ret = 0; 1278c2ecf20Sopenharmony_cidone: 1288c2ecf20Sopenharmony_ci if (fp) 1298c2ecf20Sopenharmony_ci fclose(fp); 1308c2ecf20Sopenharmony_ci free(buf); 1318c2ecf20Sopenharmony_ci return ret; 1328c2ecf20Sopenharmony_ci} 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_civoid cpu_topology__delete(struct cpu_topology *tp) 1358c2ecf20Sopenharmony_ci{ 1368c2ecf20Sopenharmony_ci u32 i; 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci if (!tp) 1398c2ecf20Sopenharmony_ci return; 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci for (i = 0 ; i < tp->core_sib; i++) 1428c2ecf20Sopenharmony_ci zfree(&tp->core_siblings[i]); 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci if (tp->die_sib) { 1458c2ecf20Sopenharmony_ci for (i = 0 ; i < tp->die_sib; i++) 1468c2ecf20Sopenharmony_ci zfree(&tp->die_siblings[i]); 1478c2ecf20Sopenharmony_ci } 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci for (i = 0 ; i < tp->thread_sib; i++) 1508c2ecf20Sopenharmony_ci zfree(&tp->thread_siblings[i]); 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci free(tp); 1538c2ecf20Sopenharmony_ci} 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_cistatic bool has_die_topology(void) 1568c2ecf20Sopenharmony_ci{ 1578c2ecf20Sopenharmony_ci char filename[MAXPATHLEN]; 1588c2ecf20Sopenharmony_ci struct utsname uts; 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci if (uname(&uts) < 0) 1618c2ecf20Sopenharmony_ci return false; 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci if (strncmp(uts.machine, "x86_64", 6)) 1648c2ecf20Sopenharmony_ci return false; 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci scnprintf(filename, MAXPATHLEN, DIE_SIB_FMT, 1678c2ecf20Sopenharmony_ci sysfs__mountpoint(), 0); 1688c2ecf20Sopenharmony_ci if (access(filename, F_OK) == -1) 1698c2ecf20Sopenharmony_ci return false; 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci return true; 1728c2ecf20Sopenharmony_ci} 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_cistruct cpu_topology *cpu_topology__new(void) 1758c2ecf20Sopenharmony_ci{ 1768c2ecf20Sopenharmony_ci struct cpu_topology *tp = NULL; 1778c2ecf20Sopenharmony_ci void *addr; 1788c2ecf20Sopenharmony_ci u32 nr, i, nr_addr; 1798c2ecf20Sopenharmony_ci size_t sz; 1808c2ecf20Sopenharmony_ci long ncpus; 1818c2ecf20Sopenharmony_ci int ret = -1; 1828c2ecf20Sopenharmony_ci struct perf_cpu_map *map; 1838c2ecf20Sopenharmony_ci bool has_die = has_die_topology(); 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci ncpus = cpu__max_present_cpu(); 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci /* build online CPU map */ 1888c2ecf20Sopenharmony_ci map = perf_cpu_map__new(NULL); 1898c2ecf20Sopenharmony_ci if (map == NULL) { 1908c2ecf20Sopenharmony_ci pr_debug("failed to get system cpumap\n"); 1918c2ecf20Sopenharmony_ci return NULL; 1928c2ecf20Sopenharmony_ci } 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci nr = (u32)(ncpus & UINT_MAX); 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci sz = nr * sizeof(char *); 1978c2ecf20Sopenharmony_ci if (has_die) 1988c2ecf20Sopenharmony_ci nr_addr = 3; 1998c2ecf20Sopenharmony_ci else 2008c2ecf20Sopenharmony_ci nr_addr = 2; 2018c2ecf20Sopenharmony_ci addr = calloc(1, sizeof(*tp) + nr_addr * sz); 2028c2ecf20Sopenharmony_ci if (!addr) 2038c2ecf20Sopenharmony_ci goto out_free; 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci tp = addr; 2068c2ecf20Sopenharmony_ci addr += sizeof(*tp); 2078c2ecf20Sopenharmony_ci tp->core_siblings = addr; 2088c2ecf20Sopenharmony_ci addr += sz; 2098c2ecf20Sopenharmony_ci if (has_die) { 2108c2ecf20Sopenharmony_ci tp->die_siblings = addr; 2118c2ecf20Sopenharmony_ci addr += sz; 2128c2ecf20Sopenharmony_ci } 2138c2ecf20Sopenharmony_ci tp->thread_siblings = addr; 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci for (i = 0; i < nr; i++) { 2168c2ecf20Sopenharmony_ci if (!cpu_map__has(map, i)) 2178c2ecf20Sopenharmony_ci continue; 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci ret = build_cpu_topology(tp, i); 2208c2ecf20Sopenharmony_ci if (ret < 0) 2218c2ecf20Sopenharmony_ci break; 2228c2ecf20Sopenharmony_ci } 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ciout_free: 2258c2ecf20Sopenharmony_ci perf_cpu_map__put(map); 2268c2ecf20Sopenharmony_ci if (ret) { 2278c2ecf20Sopenharmony_ci cpu_topology__delete(tp); 2288c2ecf20Sopenharmony_ci tp = NULL; 2298c2ecf20Sopenharmony_ci } 2308c2ecf20Sopenharmony_ci return tp; 2318c2ecf20Sopenharmony_ci} 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_cistatic int load_numa_node(struct numa_topology_node *node, int nr) 2348c2ecf20Sopenharmony_ci{ 2358c2ecf20Sopenharmony_ci char str[MAXPATHLEN]; 2368c2ecf20Sopenharmony_ci char field[32]; 2378c2ecf20Sopenharmony_ci char *buf = NULL, *p; 2388c2ecf20Sopenharmony_ci size_t len = 0; 2398c2ecf20Sopenharmony_ci int ret = -1; 2408c2ecf20Sopenharmony_ci FILE *fp; 2418c2ecf20Sopenharmony_ci u64 mem; 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci node->node = (u32) nr; 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci scnprintf(str, MAXPATHLEN, NODE_MEMINFO_FMT, 2468c2ecf20Sopenharmony_ci sysfs__mountpoint(), nr); 2478c2ecf20Sopenharmony_ci fp = fopen(str, "r"); 2488c2ecf20Sopenharmony_ci if (!fp) 2498c2ecf20Sopenharmony_ci return -1; 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci while (getline(&buf, &len, fp) > 0) { 2528c2ecf20Sopenharmony_ci /* skip over invalid lines */ 2538c2ecf20Sopenharmony_ci if (!strchr(buf, ':')) 2548c2ecf20Sopenharmony_ci continue; 2558c2ecf20Sopenharmony_ci if (sscanf(buf, "%*s %*d %31s %"PRIu64, field, &mem) != 2) 2568c2ecf20Sopenharmony_ci goto err; 2578c2ecf20Sopenharmony_ci if (!strcmp(field, "MemTotal:")) 2588c2ecf20Sopenharmony_ci node->mem_total = mem; 2598c2ecf20Sopenharmony_ci if (!strcmp(field, "MemFree:")) 2608c2ecf20Sopenharmony_ci node->mem_free = mem; 2618c2ecf20Sopenharmony_ci if (node->mem_total && node->mem_free) 2628c2ecf20Sopenharmony_ci break; 2638c2ecf20Sopenharmony_ci } 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci fclose(fp); 2668c2ecf20Sopenharmony_ci fp = NULL; 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci scnprintf(str, MAXPATHLEN, NODE_CPULIST_FMT, 2698c2ecf20Sopenharmony_ci sysfs__mountpoint(), nr); 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci fp = fopen(str, "r"); 2728c2ecf20Sopenharmony_ci if (!fp) 2738c2ecf20Sopenharmony_ci return -1; 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci if (getline(&buf, &len, fp) <= 0) 2768c2ecf20Sopenharmony_ci goto err; 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci p = strchr(buf, '\n'); 2798c2ecf20Sopenharmony_ci if (p) 2808c2ecf20Sopenharmony_ci *p = '\0'; 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci node->cpus = buf; 2838c2ecf20Sopenharmony_ci fclose(fp); 2848c2ecf20Sopenharmony_ci return 0; 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_cierr: 2878c2ecf20Sopenharmony_ci free(buf); 2888c2ecf20Sopenharmony_ci if (fp) 2898c2ecf20Sopenharmony_ci fclose(fp); 2908c2ecf20Sopenharmony_ci return ret; 2918c2ecf20Sopenharmony_ci} 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_cistruct numa_topology *numa_topology__new(void) 2948c2ecf20Sopenharmony_ci{ 2958c2ecf20Sopenharmony_ci struct perf_cpu_map *node_map = NULL; 2968c2ecf20Sopenharmony_ci struct numa_topology *tp = NULL; 2978c2ecf20Sopenharmony_ci char path[MAXPATHLEN]; 2988c2ecf20Sopenharmony_ci char *buf = NULL; 2998c2ecf20Sopenharmony_ci size_t len = 0; 3008c2ecf20Sopenharmony_ci u32 nr, i; 3018c2ecf20Sopenharmony_ci FILE *fp; 3028c2ecf20Sopenharmony_ci char *c; 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci scnprintf(path, MAXPATHLEN, NODE_ONLINE_FMT, 3058c2ecf20Sopenharmony_ci sysfs__mountpoint()); 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci fp = fopen(path, "r"); 3088c2ecf20Sopenharmony_ci if (!fp) 3098c2ecf20Sopenharmony_ci return NULL; 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci if (getline(&buf, &len, fp) <= 0) 3128c2ecf20Sopenharmony_ci goto out; 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci c = strchr(buf, '\n'); 3158c2ecf20Sopenharmony_ci if (c) 3168c2ecf20Sopenharmony_ci *c = '\0'; 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci node_map = perf_cpu_map__new(buf); 3198c2ecf20Sopenharmony_ci if (!node_map) 3208c2ecf20Sopenharmony_ci goto out; 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci nr = (u32) node_map->nr; 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci tp = zalloc(sizeof(*tp) + sizeof(tp->nodes[0])*nr); 3258c2ecf20Sopenharmony_ci if (!tp) 3268c2ecf20Sopenharmony_ci goto out; 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci tp->nr = nr; 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci for (i = 0; i < nr; i++) { 3318c2ecf20Sopenharmony_ci if (load_numa_node(&tp->nodes[i], node_map->map[i])) { 3328c2ecf20Sopenharmony_ci numa_topology__delete(tp); 3338c2ecf20Sopenharmony_ci tp = NULL; 3348c2ecf20Sopenharmony_ci break; 3358c2ecf20Sopenharmony_ci } 3368c2ecf20Sopenharmony_ci } 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ciout: 3398c2ecf20Sopenharmony_ci free(buf); 3408c2ecf20Sopenharmony_ci fclose(fp); 3418c2ecf20Sopenharmony_ci perf_cpu_map__put(node_map); 3428c2ecf20Sopenharmony_ci return tp; 3438c2ecf20Sopenharmony_ci} 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_civoid numa_topology__delete(struct numa_topology *tp) 3468c2ecf20Sopenharmony_ci{ 3478c2ecf20Sopenharmony_ci u32 i; 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_ci for (i = 0; i < tp->nr; i++) 3508c2ecf20Sopenharmony_ci zfree(&tp->nodes[i].cpus); 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci free(tp); 3538c2ecf20Sopenharmony_ci} 354