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