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