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