Linux Audio

Check our new training course

Loading...
v6.2
  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 <string.h>
  7#include <api/fs/fs.h>
  8#include <linux/zalloc.h>
  9#include <perf/cpumap.h>
 10
 11#include "cputopo.h"
 12#include "cpumap.h"
 13#include "debug.h"
 14#include "env.h"
 15#include "pmu-hybrid.h"
 16
 17#define PACKAGE_CPUS_FMT \
 18	"%s/devices/system/cpu/cpu%d/topology/package_cpus_list"
 19#define PACKAGE_CPUS_FMT_OLD \
 20	"%s/devices/system/cpu/cpu%d/topology/core_siblings_list"
 21#define DIE_CPUS_FMT \
 22	"%s/devices/system/cpu/cpu%d/topology/die_cpus_list"
 23#define CORE_CPUS_FMT \
 24	"%s/devices/system/cpu/cpu%d/topology/core_cpus_list"
 25#define CORE_CPUS_FMT_OLD \
 26	"%s/devices/system/cpu/cpu%d/topology/thread_siblings_list"
 
 
 27#define NODE_ONLINE_FMT \
 28	"%s/devices/system/node/online"
 29#define NODE_MEMINFO_FMT \
 30	"%s/devices/system/node/node%d/meminfo"
 31#define NODE_CPULIST_FMT \
 32	"%s/devices/system/node/node%d/cpulist"
 33
 34static int build_cpu_topology(struct cpu_topology *tp, int cpu)
 35{
 36	FILE *fp;
 37	char filename[MAXPATHLEN];
 38	char *buf = NULL, *p;
 39	size_t len = 0;
 40	ssize_t sret;
 41	u32 i = 0;
 42	int ret = -1;
 43
 44	scnprintf(filename, MAXPATHLEN, PACKAGE_CPUS_FMT,
 45		  sysfs__mountpoint(), cpu);
 46	if (access(filename, F_OK) == -1) {
 47		scnprintf(filename, MAXPATHLEN, PACKAGE_CPUS_FMT_OLD,
 48			sysfs__mountpoint(), cpu);
 49	}
 50	fp = fopen(filename, "r");
 51	if (!fp)
 52		goto try_dies;
 53
 54	sret = getline(&buf, &len, fp);
 55	fclose(fp);
 56	if (sret <= 0)
 57		goto try_dies;
 58
 59	p = strchr(buf, '\n');
 60	if (p)
 61		*p = '\0';
 62
 63	for (i = 0; i < tp->package_cpus_lists; i++) {
 64		if (!strcmp(buf, tp->package_cpus_list[i]))
 65			break;
 66	}
 67	if (i == tp->package_cpus_lists) {
 68		tp->package_cpus_list[i] = buf;
 69		tp->package_cpus_lists++;
 70		buf = NULL;
 71		len = 0;
 72	}
 73	ret = 0;
 74
 75try_dies:
 76	if (!tp->die_cpus_list)
 77		goto try_threads;
 78
 79	scnprintf(filename, MAXPATHLEN, DIE_CPUS_FMT,
 80		  sysfs__mountpoint(), cpu);
 81	fp = fopen(filename, "r");
 82	if (!fp)
 83		goto try_threads;
 84
 85	sret = getline(&buf, &len, fp);
 86	fclose(fp);
 87	if (sret <= 0)
 88		goto try_threads;
 89
 90	p = strchr(buf, '\n');
 91	if (p)
 92		*p = '\0';
 93
 94	for (i = 0; i < tp->die_cpus_lists; i++) {
 95		if (!strcmp(buf, tp->die_cpus_list[i]))
 96			break;
 97	}
 98	if (i == tp->die_cpus_lists) {
 99		tp->die_cpus_list[i] = buf;
100		tp->die_cpus_lists++;
101		buf = NULL;
102		len = 0;
103	}
104	ret = 0;
105
106try_threads:
107	scnprintf(filename, MAXPATHLEN, CORE_CPUS_FMT,
108		  sysfs__mountpoint(), cpu);
109	if (access(filename, F_OK) == -1) {
110		scnprintf(filename, MAXPATHLEN, CORE_CPUS_FMT_OLD,
111			  sysfs__mountpoint(), cpu);
112	}
113	fp = fopen(filename, "r");
114	if (!fp)
115		goto done;
116
117	if (getline(&buf, &len, fp) <= 0)
118		goto done;
119
120	p = strchr(buf, '\n');
121	if (p)
122		*p = '\0';
123
124	for (i = 0; i < tp->core_cpus_lists; i++) {
125		if (!strcmp(buf, tp->core_cpus_list[i]))
126			break;
127	}
128	if (i == tp->core_cpus_lists) {
129		tp->core_cpus_list[i] = buf;
130		tp->core_cpus_lists++;
131		buf = NULL;
132	}
133	ret = 0;
134done:
135	if (fp)
136		fclose(fp);
137	free(buf);
138	return ret;
139}
140
141void cpu_topology__delete(struct cpu_topology *tp)
142{
143	u32 i;
144
145	if (!tp)
146		return;
147
148	for (i = 0 ; i < tp->package_cpus_lists; i++)
149		zfree(&tp->package_cpus_list[i]);
150
151	for (i = 0 ; i < tp->die_cpus_lists; i++)
152		zfree(&tp->die_cpus_list[i]);
153
154	for (i = 0 ; i < tp->core_cpus_lists; i++)
155		zfree(&tp->core_cpus_list[i]);
156
157	free(tp);
158}
159
160bool cpu_topology__smt_on(const struct cpu_topology *topology)
161{
162	for (u32 i = 0; i < topology->core_cpus_lists; i++) {
163		const char *cpu_list = topology->core_cpus_list[i];
164
165		/*
166		 * If there is a need to separate siblings in a core then SMT is
167		 * enabled.
168		 */
169		if (strchr(cpu_list, ',') || strchr(cpu_list, '-'))
170			return true;
171	}
172	return false;
173}
174
175bool cpu_topology__core_wide(const struct cpu_topology *topology,
176			     const char *user_requested_cpu_list)
177{
178	struct perf_cpu_map *user_requested_cpus;
179
180	/*
181	 * If user_requested_cpu_list is empty then all CPUs are recorded and so
182	 * core_wide is true.
183	 */
184	if (!user_requested_cpu_list)
185		return true;
186
187	user_requested_cpus = perf_cpu_map__new(user_requested_cpu_list);
188	/* Check that every user requested CPU is the complete set of SMT threads on a core. */
189	for (u32 i = 0; i < topology->core_cpus_lists; i++) {
190		const char *core_cpu_list = topology->core_cpus_list[i];
191		struct perf_cpu_map *core_cpus = perf_cpu_map__new(core_cpu_list);
192		struct perf_cpu cpu;
193		int idx;
194		bool has_first, first = true;
195
196		perf_cpu_map__for_each_cpu(cpu, idx, core_cpus) {
197			if (first) {
198				has_first = perf_cpu_map__has(user_requested_cpus, cpu);
199				first = false;
200			} else {
201				/*
202				 * If the first core CPU is user requested then
203				 * all subsequent CPUs in the core must be user
204				 * requested too. If the first CPU isn't user
205				 * requested then none of the others must be
206				 * too.
207				 */
208				if (perf_cpu_map__has(user_requested_cpus, cpu) != has_first) {
209					perf_cpu_map__put(core_cpus);
210					perf_cpu_map__put(user_requested_cpus);
211					return false;
212				}
213			}
214		}
215		perf_cpu_map__put(core_cpus);
216	}
217	perf_cpu_map__put(user_requested_cpus);
218	return true;
219}
220
221static bool has_die_topology(void)
222{
223	char filename[MAXPATHLEN];
224	struct utsname uts;
225
226	if (uname(&uts) < 0)
227		return false;
228
229	if (strncmp(uts.machine, "x86_64", 6) &&
230	    strncmp(uts.machine, "s390x", 5))
231		return false;
232
233	scnprintf(filename, MAXPATHLEN, DIE_CPUS_FMT,
234		  sysfs__mountpoint(), 0);
235	if (access(filename, F_OK) == -1)
236		return false;
237
238	return true;
239}
240
241struct cpu_topology *cpu_topology__new(void)
242{
243	struct cpu_topology *tp = NULL;
244	void *addr;
245	u32 nr, i, nr_addr;
246	size_t sz;
247	long ncpus;
248	int ret = -1;
249	struct perf_cpu_map *map;
250	bool has_die = has_die_topology();
251
252	ncpus = cpu__max_present_cpu().cpu;
253
254	/* build online CPU map */
255	map = perf_cpu_map__new(NULL);
256	if (map == NULL) {
257		pr_debug("failed to get system cpumap\n");
258		return NULL;
259	}
260
261	nr = (u32)(ncpus & UINT_MAX);
262
263	sz = nr * sizeof(char *);
264	if (has_die)
265		nr_addr = 3;
266	else
267		nr_addr = 2;
268	addr = calloc(1, sizeof(*tp) + nr_addr * sz);
269	if (!addr)
270		goto out_free;
271
272	tp = addr;
273	addr += sizeof(*tp);
274	tp->package_cpus_list = addr;
275	addr += sz;
276	if (has_die) {
277		tp->die_cpus_list = addr;
278		addr += sz;
279	}
280	tp->core_cpus_list = addr;
281
282	for (i = 0; i < nr; i++) {
283		if (!perf_cpu_map__has(map, (struct perf_cpu){ .cpu = i }))
284			continue;
285
286		ret = build_cpu_topology(tp, i);
287		if (ret < 0)
288			break;
289	}
290
291out_free:
292	perf_cpu_map__put(map);
293	if (ret) {
294		cpu_topology__delete(tp);
295		tp = NULL;
296	}
297	return tp;
298}
299
300static int load_numa_node(struct numa_topology_node *node, int nr)
301{
302	char str[MAXPATHLEN];
303	char field[32];
304	char *buf = NULL, *p;
305	size_t len = 0;
306	int ret = -1;
307	FILE *fp;
308	u64 mem;
309
310	node->node = (u32) nr;
311
312	scnprintf(str, MAXPATHLEN, NODE_MEMINFO_FMT,
313		  sysfs__mountpoint(), nr);
314	fp = fopen(str, "r");
315	if (!fp)
316		return -1;
317
318	while (getline(&buf, &len, fp) > 0) {
319		/* skip over invalid lines */
320		if (!strchr(buf, ':'))
321			continue;
322		if (sscanf(buf, "%*s %*d %31s %"PRIu64, field, &mem) != 2)
323			goto err;
324		if (!strcmp(field, "MemTotal:"))
325			node->mem_total = mem;
326		if (!strcmp(field, "MemFree:"))
327			node->mem_free = mem;
328		if (node->mem_total && node->mem_free)
329			break;
330	}
331
332	fclose(fp);
333	fp = NULL;
334
335	scnprintf(str, MAXPATHLEN, NODE_CPULIST_FMT,
336		  sysfs__mountpoint(), nr);
337
338	fp = fopen(str, "r");
339	if (!fp)
340		return -1;
341
342	if (getline(&buf, &len, fp) <= 0)
343		goto err;
344
345	p = strchr(buf, '\n');
346	if (p)
347		*p = '\0';
348
349	node->cpus = buf;
350	fclose(fp);
351	return 0;
352
353err:
354	free(buf);
355	if (fp)
356		fclose(fp);
357	return ret;
358}
359
360struct numa_topology *numa_topology__new(void)
361{
362	struct perf_cpu_map *node_map = NULL;
363	struct numa_topology *tp = NULL;
364	char path[MAXPATHLEN];
365	char *buf = NULL;
366	size_t len = 0;
367	u32 nr, i;
368	FILE *fp;
369	char *c;
370
371	scnprintf(path, MAXPATHLEN, NODE_ONLINE_FMT,
372		  sysfs__mountpoint());
373
374	fp = fopen(path, "r");
375	if (!fp)
376		return NULL;
377
378	if (getline(&buf, &len, fp) <= 0)
379		goto out;
380
381	c = strchr(buf, '\n');
382	if (c)
383		*c = '\0';
384
385	node_map = perf_cpu_map__new(buf);
386	if (!node_map)
387		goto out;
388
389	nr = (u32) perf_cpu_map__nr(node_map);
390
391	tp = zalloc(sizeof(*tp) + sizeof(tp->nodes[0])*nr);
392	if (!tp)
393		goto out;
394
395	tp->nr = nr;
396
397	for (i = 0; i < nr; i++) {
398		if (load_numa_node(&tp->nodes[i], perf_cpu_map__cpu(node_map, i).cpu)) {
399			numa_topology__delete(tp);
400			tp = NULL;
401			break;
402		}
403	}
404
405out:
406	free(buf);
407	fclose(fp);
408	perf_cpu_map__put(node_map);
409	return tp;
410}
411
412void numa_topology__delete(struct numa_topology *tp)
413{
414	u32 i;
415
416	for (i = 0; i < tp->nr; i++)
417		zfree(&tp->nodes[i].cpus);
418
419	free(tp);
420}
421
422static int load_hybrid_node(struct hybrid_topology_node *node,
423			    struct perf_pmu *pmu)
424{
425	const char *sysfs;
426	char path[PATH_MAX];
427	char *buf = NULL, *p;
428	FILE *fp;
429	size_t len = 0;
430
431	node->pmu_name = strdup(pmu->name);
432	if (!node->pmu_name)
433		return -1;
434
435	sysfs = sysfs__mountpoint();
436	if (!sysfs)
437		goto err;
438
439	snprintf(path, PATH_MAX, CPUS_TEMPLATE_CPU, sysfs, pmu->name);
440	fp = fopen(path, "r");
441	if (!fp)
442		goto err;
443
444	if (getline(&buf, &len, fp) <= 0) {
445		fclose(fp);
446		goto err;
447	}
448
449	p = strchr(buf, '\n');
450	if (p)
451		*p = '\0';
452
453	fclose(fp);
454	node->cpus = buf;
455	return 0;
456
457err:
458	zfree(&node->pmu_name);
459	free(buf);
460	return -1;
461}
462
463struct hybrid_topology *hybrid_topology__new(void)
464{
465	struct perf_pmu *pmu;
466	struct hybrid_topology *tp = NULL;
467	u32 nr, i = 0;
468
469	nr = perf_pmu__hybrid_pmu_num();
470	if (nr == 0)
471		return NULL;
472
473	tp = zalloc(sizeof(*tp) + sizeof(tp->nodes[0]) * nr);
474	if (!tp)
475		return NULL;
476
477	tp->nr = nr;
478	perf_pmu__for_each_hybrid_pmu(pmu) {
479		if (load_hybrid_node(&tp->nodes[i], pmu)) {
480			hybrid_topology__delete(tp);
481			return NULL;
482		}
483		i++;
484	}
485
486	return tp;
487}
488
489void hybrid_topology__delete(struct hybrid_topology *tp)
490{
491	u32 i;
492
493	for (i = 0; i < tp->nr; i++) {
494		zfree(&tp->nodes[i].pmu_name);
495		zfree(&tp->nodes[i].cpus);
496	}
497
498	free(tp);
499}
v5.4
  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 <string.h>
  7#include <api/fs/fs.h>
  8#include <linux/zalloc.h>
  9#include <perf/cpumap.h>
 10
 11#include "cputopo.h"
 12#include "cpumap.h"
 13#include "debug.h"
 14#include "env.h"
 
 15
 16#define CORE_SIB_FMT \
 
 
 17	"%s/devices/system/cpu/cpu%d/topology/core_siblings_list"
 18#define DIE_SIB_FMT \
 19	"%s/devices/system/cpu/cpu%d/topology/die_cpus_list"
 20#define THRD_SIB_FMT \
 
 
 21	"%s/devices/system/cpu/cpu%d/topology/thread_siblings_list"
 22#define THRD_SIB_FMT_NEW \
 23	"%s/devices/system/cpu/cpu%d/topology/core_cpus_list"
 24#define NODE_ONLINE_FMT \
 25	"%s/devices/system/node/online"
 26#define NODE_MEMINFO_FMT \
 27	"%s/devices/system/node/node%d/meminfo"
 28#define NODE_CPULIST_FMT \
 29	"%s/devices/system/node/node%d/cpulist"
 30
 31static int build_cpu_topology(struct cpu_topology *tp, int cpu)
 32{
 33	FILE *fp;
 34	char filename[MAXPATHLEN];
 35	char *buf = NULL, *p;
 36	size_t len = 0;
 37	ssize_t sret;
 38	u32 i = 0;
 39	int ret = -1;
 40
 41	scnprintf(filename, MAXPATHLEN, CORE_SIB_FMT,
 42		  sysfs__mountpoint(), cpu);
 
 
 
 
 43	fp = fopen(filename, "r");
 44	if (!fp)
 45		goto try_dies;
 46
 47	sret = getline(&buf, &len, fp);
 48	fclose(fp);
 49	if (sret <= 0)
 50		goto try_dies;
 51
 52	p = strchr(buf, '\n');
 53	if (p)
 54		*p = '\0';
 55
 56	for (i = 0; i < tp->core_sib; i++) {
 57		if (!strcmp(buf, tp->core_siblings[i]))
 58			break;
 59	}
 60	if (i == tp->core_sib) {
 61		tp->core_siblings[i] = buf;
 62		tp->core_sib++;
 63		buf = NULL;
 64		len = 0;
 65	}
 66	ret = 0;
 67
 68try_dies:
 69	if (!tp->die_siblings)
 70		goto try_threads;
 71
 72	scnprintf(filename, MAXPATHLEN, DIE_SIB_FMT,
 73		  sysfs__mountpoint(), cpu);
 74	fp = fopen(filename, "r");
 75	if (!fp)
 76		goto try_threads;
 77
 78	sret = getline(&buf, &len, fp);
 79	fclose(fp);
 80	if (sret <= 0)
 81		goto try_threads;
 82
 83	p = strchr(buf, '\n');
 84	if (p)
 85		*p = '\0';
 86
 87	for (i = 0; i < tp->die_sib; i++) {
 88		if (!strcmp(buf, tp->die_siblings[i]))
 89			break;
 90	}
 91	if (i == tp->die_sib) {
 92		tp->die_siblings[i] = buf;
 93		tp->die_sib++;
 94		buf = NULL;
 95		len = 0;
 96	}
 97	ret = 0;
 98
 99try_threads:
100	scnprintf(filename, MAXPATHLEN, THRD_SIB_FMT_NEW,
101		  sysfs__mountpoint(), cpu);
102	if (access(filename, F_OK) == -1) {
103		scnprintf(filename, MAXPATHLEN, THRD_SIB_FMT,
104			  sysfs__mountpoint(), cpu);
105	}
106	fp = fopen(filename, "r");
107	if (!fp)
108		goto done;
109
110	if (getline(&buf, &len, fp) <= 0)
111		goto done;
112
113	p = strchr(buf, '\n');
114	if (p)
115		*p = '\0';
116
117	for (i = 0; i < tp->thread_sib; i++) {
118		if (!strcmp(buf, tp->thread_siblings[i]))
119			break;
120	}
121	if (i == tp->thread_sib) {
122		tp->thread_siblings[i] = buf;
123		tp->thread_sib++;
124		buf = NULL;
125	}
126	ret = 0;
127done:
128	if (fp)
129		fclose(fp);
130	free(buf);
131	return ret;
132}
133
134void cpu_topology__delete(struct cpu_topology *tp)
135{
136	u32 i;
137
138	if (!tp)
139		return;
140
141	for (i = 0 ; i < tp->core_sib; i++)
142		zfree(&tp->core_siblings[i]);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
143
144	if (tp->die_sib) {
145		for (i = 0 ; i < tp->die_sib; i++)
146			zfree(&tp->die_siblings[i]);
 
 
 
147	}
 
 
148
149	for (i = 0 ; i < tp->thread_sib; i++)
150		zfree(&tp->thread_siblings[i]);
 
 
151
152	free(tp);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
153}
154
155static bool has_die_topology(void)
156{
157	char filename[MAXPATHLEN];
158	struct utsname uts;
159
160	if (uname(&uts) < 0)
161		return false;
162
163	if (strncmp(uts.machine, "x86_64", 6))
 
164		return false;
165
166	scnprintf(filename, MAXPATHLEN, DIE_SIB_FMT,
167		  sysfs__mountpoint(), 0);
168	if (access(filename, F_OK) == -1)
169		return false;
170
171	return true;
172}
173
174struct cpu_topology *cpu_topology__new(void)
175{
176	struct cpu_topology *tp = NULL;
177	void *addr;
178	u32 nr, i, nr_addr;
179	size_t sz;
180	long ncpus;
181	int ret = -1;
182	struct perf_cpu_map *map;
183	bool has_die = has_die_topology();
184
185	ncpus = cpu__max_present_cpu();
186
187	/* build online CPU map */
188	map = perf_cpu_map__new(NULL);
189	if (map == NULL) {
190		pr_debug("failed to get system cpumap\n");
191		return NULL;
192	}
193
194	nr = (u32)(ncpus & UINT_MAX);
195
196	sz = nr * sizeof(char *);
197	if (has_die)
198		nr_addr = 3;
199	else
200		nr_addr = 2;
201	addr = calloc(1, sizeof(*tp) + nr_addr * sz);
202	if (!addr)
203		goto out_free;
204
205	tp = addr;
206	addr += sizeof(*tp);
207	tp->core_siblings = addr;
208	addr += sz;
209	if (has_die) {
210		tp->die_siblings = addr;
211		addr += sz;
212	}
213	tp->thread_siblings = addr;
214
215	for (i = 0; i < nr; i++) {
216		if (!cpu_map__has(map, i))
217			continue;
218
219		ret = build_cpu_topology(tp, i);
220		if (ret < 0)
221			break;
222	}
223
224out_free:
225	perf_cpu_map__put(map);
226	if (ret) {
227		cpu_topology__delete(tp);
228		tp = NULL;
229	}
230	return tp;
231}
232
233static int load_numa_node(struct numa_topology_node *node, int nr)
234{
235	char str[MAXPATHLEN];
236	char field[32];
237	char *buf = NULL, *p;
238	size_t len = 0;
239	int ret = -1;
240	FILE *fp;
241	u64 mem;
242
243	node->node = (u32) nr;
244
245	scnprintf(str, MAXPATHLEN, NODE_MEMINFO_FMT,
246		  sysfs__mountpoint(), nr);
247	fp = fopen(str, "r");
248	if (!fp)
249		return -1;
250
251	while (getline(&buf, &len, fp) > 0) {
252		/* skip over invalid lines */
253		if (!strchr(buf, ':'))
254			continue;
255		if (sscanf(buf, "%*s %*d %31s %"PRIu64, field, &mem) != 2)
256			goto err;
257		if (!strcmp(field, "MemTotal:"))
258			node->mem_total = mem;
259		if (!strcmp(field, "MemFree:"))
260			node->mem_free = mem;
261		if (node->mem_total && node->mem_free)
262			break;
263	}
264
265	fclose(fp);
266	fp = NULL;
267
268	scnprintf(str, MAXPATHLEN, NODE_CPULIST_FMT,
269		  sysfs__mountpoint(), nr);
270
271	fp = fopen(str, "r");
272	if (!fp)
273		return -1;
274
275	if (getline(&buf, &len, fp) <= 0)
276		goto err;
277
278	p = strchr(buf, '\n');
279	if (p)
280		*p = '\0';
281
282	node->cpus = buf;
283	fclose(fp);
284	return 0;
285
286err:
287	free(buf);
288	if (fp)
289		fclose(fp);
290	return ret;
291}
292
293struct numa_topology *numa_topology__new(void)
294{
295	struct perf_cpu_map *node_map = NULL;
296	struct numa_topology *tp = NULL;
297	char path[MAXPATHLEN];
298	char *buf = NULL;
299	size_t len = 0;
300	u32 nr, i;
301	FILE *fp;
302	char *c;
303
304	scnprintf(path, MAXPATHLEN, NODE_ONLINE_FMT,
305		  sysfs__mountpoint());
306
307	fp = fopen(path, "r");
308	if (!fp)
309		return NULL;
310
311	if (getline(&buf, &len, fp) <= 0)
312		goto out;
313
314	c = strchr(buf, '\n');
315	if (c)
316		*c = '\0';
317
318	node_map = perf_cpu_map__new(buf);
319	if (!node_map)
320		goto out;
321
322	nr = (u32) node_map->nr;
323
324	tp = zalloc(sizeof(*tp) + sizeof(tp->nodes[0])*nr);
325	if (!tp)
326		goto out;
327
328	tp->nr = nr;
329
330	for (i = 0; i < nr; i++) {
331		if (load_numa_node(&tp->nodes[i], node_map->map[i])) {
332			numa_topology__delete(tp);
333			tp = NULL;
334			break;
335		}
336	}
337
338out:
339	free(buf);
340	fclose(fp);
341	perf_cpu_map__put(node_map);
342	return tp;
343}
344
345void numa_topology__delete(struct numa_topology *tp)
346{
347	u32 i;
348
349	for (i = 0; i < tp->nr; i++)
350		zfree(&tp->nodes[i].cpus);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
351
352	free(tp);
353}