0001
0002 #include <sys/param.h>
0003 #include <sys/utsname.h>
0004 #include <inttypes.h>
0005 #include <stdlib.h>
0006 #include <string.h>
0007 #include <api/fs/fs.h>
0008 #include <linux/zalloc.h>
0009 #include <perf/cpumap.h>
0010
0011 #include "cputopo.h"
0012 #include "cpumap.h"
0013 #include "debug.h"
0014 #include "env.h"
0015 #include "pmu-hybrid.h"
0016
0017 #define PACKAGE_CPUS_FMT \
0018 "%s/devices/system/cpu/cpu%d/topology/package_cpus_list"
0019 #define PACKAGE_CPUS_FMT_OLD \
0020 "%s/devices/system/cpu/cpu%d/topology/core_siblings_list"
0021 #define DIE_CPUS_FMT \
0022 "%s/devices/system/cpu/cpu%d/topology/die_cpus_list"
0023 #define CORE_CPUS_FMT \
0024 "%s/devices/system/cpu/cpu%d/topology/core_cpus_list"
0025 #define CORE_CPUS_FMT_OLD \
0026 "%s/devices/system/cpu/cpu%d/topology/thread_siblings_list"
0027 #define NODE_ONLINE_FMT \
0028 "%s/devices/system/node/online"
0029 #define NODE_MEMINFO_FMT \
0030 "%s/devices/system/node/node%d/meminfo"
0031 #define NODE_CPULIST_FMT \
0032 "%s/devices/system/node/node%d/cpulist"
0033
0034 static int build_cpu_topology(struct cpu_topology *tp, int cpu)
0035 {
0036 FILE *fp;
0037 char filename[MAXPATHLEN];
0038 char *buf = NULL, *p;
0039 size_t len = 0;
0040 ssize_t sret;
0041 u32 i = 0;
0042 int ret = -1;
0043
0044 scnprintf(filename, MAXPATHLEN, PACKAGE_CPUS_FMT,
0045 sysfs__mountpoint(), cpu);
0046 if (access(filename, F_OK) == -1) {
0047 scnprintf(filename, MAXPATHLEN, PACKAGE_CPUS_FMT_OLD,
0048 sysfs__mountpoint(), cpu);
0049 }
0050 fp = fopen(filename, "r");
0051 if (!fp)
0052 goto try_dies;
0053
0054 sret = getline(&buf, &len, fp);
0055 fclose(fp);
0056 if (sret <= 0)
0057 goto try_dies;
0058
0059 p = strchr(buf, '\n');
0060 if (p)
0061 *p = '\0';
0062
0063 for (i = 0; i < tp->package_cpus_lists; i++) {
0064 if (!strcmp(buf, tp->package_cpus_list[i]))
0065 break;
0066 }
0067 if (i == tp->package_cpus_lists) {
0068 tp->package_cpus_list[i] = buf;
0069 tp->package_cpus_lists++;
0070 buf = NULL;
0071 len = 0;
0072 }
0073 ret = 0;
0074
0075 try_dies:
0076 if (!tp->die_cpus_list)
0077 goto try_threads;
0078
0079 scnprintf(filename, MAXPATHLEN, DIE_CPUS_FMT,
0080 sysfs__mountpoint(), cpu);
0081 fp = fopen(filename, "r");
0082 if (!fp)
0083 goto try_threads;
0084
0085 sret = getline(&buf, &len, fp);
0086 fclose(fp);
0087 if (sret <= 0)
0088 goto try_threads;
0089
0090 p = strchr(buf, '\n');
0091 if (p)
0092 *p = '\0';
0093
0094 for (i = 0; i < tp->die_cpus_lists; i++) {
0095 if (!strcmp(buf, tp->die_cpus_list[i]))
0096 break;
0097 }
0098 if (i == tp->die_cpus_lists) {
0099 tp->die_cpus_list[i] = buf;
0100 tp->die_cpus_lists++;
0101 buf = NULL;
0102 len = 0;
0103 }
0104 ret = 0;
0105
0106 try_threads:
0107 scnprintf(filename, MAXPATHLEN, CORE_CPUS_FMT,
0108 sysfs__mountpoint(), cpu);
0109 if (access(filename, F_OK) == -1) {
0110 scnprintf(filename, MAXPATHLEN, CORE_CPUS_FMT_OLD,
0111 sysfs__mountpoint(), cpu);
0112 }
0113 fp = fopen(filename, "r");
0114 if (!fp)
0115 goto done;
0116
0117 if (getline(&buf, &len, fp) <= 0)
0118 goto done;
0119
0120 p = strchr(buf, '\n');
0121 if (p)
0122 *p = '\0';
0123
0124 for (i = 0; i < tp->core_cpus_lists; i++) {
0125 if (!strcmp(buf, tp->core_cpus_list[i]))
0126 break;
0127 }
0128 if (i == tp->core_cpus_lists) {
0129 tp->core_cpus_list[i] = buf;
0130 tp->core_cpus_lists++;
0131 buf = NULL;
0132 }
0133 ret = 0;
0134 done:
0135 if (fp)
0136 fclose(fp);
0137 free(buf);
0138 return ret;
0139 }
0140
0141 void cpu_topology__delete(struct cpu_topology *tp)
0142 {
0143 u32 i;
0144
0145 if (!tp)
0146 return;
0147
0148 for (i = 0 ; i < tp->package_cpus_lists; i++)
0149 zfree(&tp->package_cpus_list[i]);
0150
0151 for (i = 0 ; i < tp->die_cpus_lists; i++)
0152 zfree(&tp->die_cpus_list[i]);
0153
0154 for (i = 0 ; i < tp->core_cpus_lists; i++)
0155 zfree(&tp->core_cpus_list[i]);
0156
0157 free(tp);
0158 }
0159
0160 static bool has_die_topology(void)
0161 {
0162 char filename[MAXPATHLEN];
0163 struct utsname uts;
0164
0165 if (uname(&uts) < 0)
0166 return false;
0167
0168 if (strncmp(uts.machine, "x86_64", 6) &&
0169 strncmp(uts.machine, "s390x", 5))
0170 return false;
0171
0172 scnprintf(filename, MAXPATHLEN, DIE_CPUS_FMT,
0173 sysfs__mountpoint(), 0);
0174 if (access(filename, F_OK) == -1)
0175 return false;
0176
0177 return true;
0178 }
0179
0180 struct cpu_topology *cpu_topology__new(void)
0181 {
0182 struct cpu_topology *tp = NULL;
0183 void *addr;
0184 u32 nr, i, nr_addr;
0185 size_t sz;
0186 long ncpus;
0187 int ret = -1;
0188 struct perf_cpu_map *map;
0189 bool has_die = has_die_topology();
0190
0191 ncpus = cpu__max_present_cpu().cpu;
0192
0193
0194 map = perf_cpu_map__new(NULL);
0195 if (map == NULL) {
0196 pr_debug("failed to get system cpumap\n");
0197 return NULL;
0198 }
0199
0200 nr = (u32)(ncpus & UINT_MAX);
0201
0202 sz = nr * sizeof(char *);
0203 if (has_die)
0204 nr_addr = 3;
0205 else
0206 nr_addr = 2;
0207 addr = calloc(1, sizeof(*tp) + nr_addr * sz);
0208 if (!addr)
0209 goto out_free;
0210
0211 tp = addr;
0212 addr += sizeof(*tp);
0213 tp->package_cpus_list = addr;
0214 addr += sz;
0215 if (has_die) {
0216 tp->die_cpus_list = addr;
0217 addr += sz;
0218 }
0219 tp->core_cpus_list = addr;
0220
0221 for (i = 0; i < nr; i++) {
0222 if (!perf_cpu_map__has(map, (struct perf_cpu){ .cpu = i }))
0223 continue;
0224
0225 ret = build_cpu_topology(tp, i);
0226 if (ret < 0)
0227 break;
0228 }
0229
0230 out_free:
0231 perf_cpu_map__put(map);
0232 if (ret) {
0233 cpu_topology__delete(tp);
0234 tp = NULL;
0235 }
0236 return tp;
0237 }
0238
0239 static int load_numa_node(struct numa_topology_node *node, int nr)
0240 {
0241 char str[MAXPATHLEN];
0242 char field[32];
0243 char *buf = NULL, *p;
0244 size_t len = 0;
0245 int ret = -1;
0246 FILE *fp;
0247 u64 mem;
0248
0249 node->node = (u32) nr;
0250
0251 scnprintf(str, MAXPATHLEN, NODE_MEMINFO_FMT,
0252 sysfs__mountpoint(), nr);
0253 fp = fopen(str, "r");
0254 if (!fp)
0255 return -1;
0256
0257 while (getline(&buf, &len, fp) > 0) {
0258
0259 if (!strchr(buf, ':'))
0260 continue;
0261 if (sscanf(buf, "%*s %*d %31s %"PRIu64, field, &mem) != 2)
0262 goto err;
0263 if (!strcmp(field, "MemTotal:"))
0264 node->mem_total = mem;
0265 if (!strcmp(field, "MemFree:"))
0266 node->mem_free = mem;
0267 if (node->mem_total && node->mem_free)
0268 break;
0269 }
0270
0271 fclose(fp);
0272 fp = NULL;
0273
0274 scnprintf(str, MAXPATHLEN, NODE_CPULIST_FMT,
0275 sysfs__mountpoint(), nr);
0276
0277 fp = fopen(str, "r");
0278 if (!fp)
0279 return -1;
0280
0281 if (getline(&buf, &len, fp) <= 0)
0282 goto err;
0283
0284 p = strchr(buf, '\n');
0285 if (p)
0286 *p = '\0';
0287
0288 node->cpus = buf;
0289 fclose(fp);
0290 return 0;
0291
0292 err:
0293 free(buf);
0294 if (fp)
0295 fclose(fp);
0296 return ret;
0297 }
0298
0299 struct numa_topology *numa_topology__new(void)
0300 {
0301 struct perf_cpu_map *node_map = NULL;
0302 struct numa_topology *tp = NULL;
0303 char path[MAXPATHLEN];
0304 char *buf = NULL;
0305 size_t len = 0;
0306 u32 nr, i;
0307 FILE *fp;
0308 char *c;
0309
0310 scnprintf(path, MAXPATHLEN, NODE_ONLINE_FMT,
0311 sysfs__mountpoint());
0312
0313 fp = fopen(path, "r");
0314 if (!fp)
0315 return NULL;
0316
0317 if (getline(&buf, &len, fp) <= 0)
0318 goto out;
0319
0320 c = strchr(buf, '\n');
0321 if (c)
0322 *c = '\0';
0323
0324 node_map = perf_cpu_map__new(buf);
0325 if (!node_map)
0326 goto out;
0327
0328 nr = (u32) perf_cpu_map__nr(node_map);
0329
0330 tp = zalloc(sizeof(*tp) + sizeof(tp->nodes[0])*nr);
0331 if (!tp)
0332 goto out;
0333
0334 tp->nr = nr;
0335
0336 for (i = 0; i < nr; i++) {
0337 if (load_numa_node(&tp->nodes[i], perf_cpu_map__cpu(node_map, i).cpu)) {
0338 numa_topology__delete(tp);
0339 tp = NULL;
0340 break;
0341 }
0342 }
0343
0344 out:
0345 free(buf);
0346 fclose(fp);
0347 perf_cpu_map__put(node_map);
0348 return tp;
0349 }
0350
0351 void numa_topology__delete(struct numa_topology *tp)
0352 {
0353 u32 i;
0354
0355 for (i = 0; i < tp->nr; i++)
0356 zfree(&tp->nodes[i].cpus);
0357
0358 free(tp);
0359 }
0360
0361 static int load_hybrid_node(struct hybrid_topology_node *node,
0362 struct perf_pmu *pmu)
0363 {
0364 const char *sysfs;
0365 char path[PATH_MAX];
0366 char *buf = NULL, *p;
0367 FILE *fp;
0368 size_t len = 0;
0369
0370 node->pmu_name = strdup(pmu->name);
0371 if (!node->pmu_name)
0372 return -1;
0373
0374 sysfs = sysfs__mountpoint();
0375 if (!sysfs)
0376 goto err;
0377
0378 snprintf(path, PATH_MAX, CPUS_TEMPLATE_CPU, sysfs, pmu->name);
0379 fp = fopen(path, "r");
0380 if (!fp)
0381 goto err;
0382
0383 if (getline(&buf, &len, fp) <= 0) {
0384 fclose(fp);
0385 goto err;
0386 }
0387
0388 p = strchr(buf, '\n');
0389 if (p)
0390 *p = '\0';
0391
0392 fclose(fp);
0393 node->cpus = buf;
0394 return 0;
0395
0396 err:
0397 zfree(&node->pmu_name);
0398 free(buf);
0399 return -1;
0400 }
0401
0402 struct hybrid_topology *hybrid_topology__new(void)
0403 {
0404 struct perf_pmu *pmu;
0405 struct hybrid_topology *tp = NULL;
0406 u32 nr, i = 0;
0407
0408 nr = perf_pmu__hybrid_pmu_num();
0409 if (nr == 0)
0410 return NULL;
0411
0412 tp = zalloc(sizeof(*tp) + sizeof(tp->nodes[0]) * nr);
0413 if (!tp)
0414 return NULL;
0415
0416 tp->nr = nr;
0417 perf_pmu__for_each_hybrid_pmu(pmu) {
0418 if (load_hybrid_node(&tp->nodes[i], pmu)) {
0419 hybrid_topology__delete(tp);
0420 return NULL;
0421 }
0422 i++;
0423 }
0424
0425 return tp;
0426 }
0427
0428 void hybrid_topology__delete(struct hybrid_topology *tp)
0429 {
0430 u32 i;
0431
0432 for (i = 0; i < tp->nr; i++) {
0433 zfree(&tp->nodes[i].pmu_name);
0434 zfree(&tp->nodes[i].cpus);
0435 }
0436
0437 free(tp);
0438 }