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