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