1 /*
2 * Copyright © 2010-2021 Inria. All rights reserved.
3 * Copyright © 2010-2013 Université Bordeaux
4 * Copyright © 2010-2011 Cisco Systems, Inc. All rights reserved.
5 * See COPYING in top-level directory.
6 *
7 *
8 * This backend is only used when the operating system does not export
9 * the necessary hardware topology information to user-space applications.
10 * Currently, FreeBSD and NetBSD only add PUs and then fallback to this
11 * backend for CPU/Cache discovery.
12 *
13 * Other backends such as Linux have their own way to retrieve various
14 * pieces of hardware topology information from the operating system
15 * on various architectures, without having to use this x86-specific code.
16 * But this backend is still used after them to annotate some objects with
17 * additional details (CPU info in Package, Inclusiveness in Caches).
18 */
19
20 #include "private/autogen/config.h"
21 #include "hwloc.h"
22 #include "private/private.h"
23 #include "private/debug.h"
24 #include "private/misc.h"
25 #include "private/cpuid-x86.h"
26
27 #include <sys/types.h>
28 #ifdef HAVE_DIRENT_H
29 #include <dirent.h>
30 #endif
31 #ifdef HAVE_VALGRIND_VALGRIND_H
32 #include <valgrind/valgrind.h>
33 #endif
34
35 struct hwloc_x86_backend_data_s {
36 unsigned nbprocs;
37 hwloc_bitmap_t apicid_set;
38 int apicid_unique;
39 char *src_cpuiddump_path;
40 int is_knl;
41 };
42
43 /************************************
44 * Management of cpuid dump as input
45 */
46
47 struct cpuiddump {
48 unsigned nr;
49 struct cpuiddump_entry {
50 unsigned inmask; /* which of ine[abcd]x are set on input */
51 unsigned ineax;
52 unsigned inebx;
53 unsigned inecx;
54 unsigned inedx;
55 unsigned outeax;
56 unsigned outebx;
57 unsigned outecx;
58 unsigned outedx;
59 } *entries;
60 };
61
62 static void
cpuiddump_free(struct cpuiddump * cpuiddump)63 cpuiddump_free(struct cpuiddump *cpuiddump)
64 {
65 if (cpuiddump->nr)
66 free(cpuiddump->entries);
67 free(cpuiddump);
68 }
69
70 static struct cpuiddump *
cpuiddump_read(const char * dirpath,unsigned idx)71 cpuiddump_read(const char *dirpath, unsigned idx)
72 {
73 struct cpuiddump *cpuiddump;
74 struct cpuiddump_entry *cur;
75 size_t filenamelen;
76 char *filename;
77 FILE *file;
78 char line[128];
79 unsigned nr;
80
81 cpuiddump = malloc(sizeof(*cpuiddump));
82 if (!cpuiddump) {
83 fprintf(stderr, "Failed to allocate cpuiddump for PU #%u, ignoring cpuiddump.\n", idx);
84 goto out;
85 }
86
87 filenamelen = strlen(dirpath) + 15;
88 filename = malloc(filenamelen);
89 if (!filename)
90 goto out_with_dump;
91 snprintf(filename, filenamelen, "%s/pu%u", dirpath, idx);
92 file = fopen(filename, "r");
93 if (!file) {
94 fprintf(stderr, "Could not read dumped cpuid file %s, ignoring cpuiddump.\n", filename);
95 goto out_with_filename;
96 }
97
98 nr = 0;
99 while (fgets(line, sizeof(line), file))
100 nr++;
101 cpuiddump->entries = malloc(nr * sizeof(struct cpuiddump_entry));
102 if (!cpuiddump->entries) {
103 fprintf(stderr, "Failed to allocate %u cpuiddump entries for PU #%u, ignoring cpuiddump.\n", nr, idx);
104 goto out_with_file;
105 }
106
107 fseek(file, 0, SEEK_SET);
108 cur = &cpuiddump->entries[0];
109 nr = 0;
110 while (fgets(line, sizeof(line), file)) {
111 if (*line == '#')
112 continue;
113 if (sscanf(line, "%x %x %x %x %x => %x %x %x %x",
114 &cur->inmask,
115 &cur->ineax, &cur->inebx, &cur->inecx, &cur->inedx,
116 &cur->outeax, &cur->outebx, &cur->outecx, &cur->outedx) == 9) {
117 cur++;
118 nr++;
119 }
120 }
121
122 cpuiddump->nr = nr;
123 fclose(file);
124 free(filename);
125 return cpuiddump;
126
127 out_with_file:
128 fclose(file);
129 out_with_filename:
130 free(filename);
131 out_with_dump:
132 free(cpuiddump);
133 out:
134 return NULL;
135 }
136
137 static void
cpuiddump_find_by_input(unsigned * eax,unsigned * ebx,unsigned * ecx,unsigned * edx,struct cpuiddump * cpuiddump)138 cpuiddump_find_by_input(unsigned *eax, unsigned *ebx, unsigned *ecx, unsigned *edx, struct cpuiddump *cpuiddump)
139 {
140 unsigned i;
141
142 for(i=0; i<cpuiddump->nr; i++) {
143 struct cpuiddump_entry *entry = &cpuiddump->entries[i];
144 if ((entry->inmask & 0x1) && *eax != entry->ineax)
145 continue;
146 if ((entry->inmask & 0x2) && *ebx != entry->inebx)
147 continue;
148 if ((entry->inmask & 0x4) && *ecx != entry->inecx)
149 continue;
150 if ((entry->inmask & 0x8) && *edx != entry->inedx)
151 continue;
152 *eax = entry->outeax;
153 *ebx = entry->outebx;
154 *ecx = entry->outecx;
155 *edx = entry->outedx;
156 return;
157 }
158
159 fprintf(stderr, "Couldn't find %x,%x,%x,%x in dumped cpuid, returning 0s.\n",
160 *eax, *ebx, *ecx, *edx);
161 *eax = 0;
162 *ebx = 0;
163 *ecx = 0;
164 *edx = 0;
165 }
166
cpuid_or_from_dump(unsigned * eax,unsigned * ebx,unsigned * ecx,unsigned * edx,struct cpuiddump * src_cpuiddump)167 static void cpuid_or_from_dump(unsigned *eax, unsigned *ebx, unsigned *ecx, unsigned *edx, struct cpuiddump *src_cpuiddump)
168 {
169 if (src_cpuiddump) {
170 cpuiddump_find_by_input(eax, ebx, ecx, edx, src_cpuiddump);
171 } else {
172 hwloc_x86_cpuid(eax, ebx, ecx, edx);
173 }
174 }
175
176 /*******************************
177 * Core detection routines and structures
178 */
179
180 enum hwloc_x86_disc_flags {
181 HWLOC_X86_DISC_FLAG_FULL = (1<<0), /* discover everything instead of only annotating */
182 HWLOC_X86_DISC_FLAG_TOPOEXT_NUMANODES = (1<<1) /* use AMD topoext numanode information */
183 };
184
185 #define has_topoext(features) ((features)[6] & (1 << 22))
186 #define has_x2apic(features) ((features)[4] & (1 << 21))
187 #define has_hybrid(features) ((features)[18] & (1 << 15))
188
189 struct cacheinfo {
190 hwloc_obj_cache_type_t type;
191 unsigned level;
192 unsigned nbthreads_sharing;
193 unsigned cacheid;
194
195 unsigned linesize;
196 unsigned linepart;
197 int inclusive;
198 int ways;
199 unsigned sets;
200 unsigned long size;
201 };
202
203 struct procinfo {
204 unsigned present;
205 unsigned apicid;
206 #define PKG 0
207 #define CORE 1
208 #define NODE 2
209 #define UNIT 3
210 #define TILE 4
211 #define MODULE 5
212 #define DIE 6
213 #define HWLOC_X86_PROCINFO_ID_NR 7
214 unsigned ids[HWLOC_X86_PROCINFO_ID_NR];
215 unsigned *otherids;
216 unsigned levels;
217 unsigned numcaches;
218 struct cacheinfo *cache;
219 char cpuvendor[13];
220 char cpumodel[3*4*4+1];
221 unsigned cpustepping;
222 unsigned cpumodelnumber;
223 unsigned cpufamilynumber;
224
225 unsigned hybridcoretype;
226 unsigned hybridnativemodel;
227 };
228
229 enum cpuid_type {
230 intel,
231 amd,
232 zhaoxin,
233 hygon,
234 unknown
235 };
236
237 /* AMD legacy cache information from specific CPUID 0x80000005-6 leaves */
setup__amd_cache_legacy(struct procinfo * infos,unsigned level,hwloc_obj_cache_type_t type,unsigned nbthreads_sharing,unsigned cpuid)238 static void setup__amd_cache_legacy(struct procinfo *infos, unsigned level, hwloc_obj_cache_type_t type, unsigned nbthreads_sharing, unsigned cpuid)
239 {
240 struct cacheinfo *cache, *tmpcaches;
241 unsigned cachenum;
242 unsigned long size = 0;
243
244 if (level == 1)
245 size = ((cpuid >> 24)) << 10;
246 else if (level == 2)
247 size = ((cpuid >> 16)) << 10;
248 else if (level == 3)
249 size = ((cpuid >> 18)) << 19;
250 if (!size)
251 return;
252
253 tmpcaches = realloc(infos->cache, (infos->numcaches+1)*sizeof(*infos->cache));
254 if (!tmpcaches)
255 /* failed to allocated, ignore that cache */
256 return;
257 infos->cache = tmpcaches;
258 cachenum = infos->numcaches++;
259
260 cache = &infos->cache[cachenum];
261
262 cache->type = type;
263 cache->level = level;
264 cache->nbthreads_sharing = nbthreads_sharing;
265 cache->linesize = cpuid & 0xff;
266 cache->linepart = 0;
267 cache->inclusive = 0; /* old AMD (K8-K10) supposed to have exclusive caches */
268
269 if (level == 1) {
270 cache->ways = (cpuid >> 16) & 0xff;
271 if (cache->ways == 0xff)
272 /* Fully associative */
273 cache->ways = -1;
274 } else {
275 static const unsigned ways_tab[] = { 0, 1, 2, 0, 4, 0, 8, 0, 16, 0, 32, 48, 64, 96, 128, -1 };
276 unsigned ways = (cpuid >> 12) & 0xf;
277 cache->ways = ways_tab[ways];
278 }
279 cache->size = size;
280 cache->sets = 0;
281
282 hwloc_debug("cache L%u t%u linesize %u ways %d size %luKB\n", cache->level, cache->nbthreads_sharing, cache->linesize, cache->ways, cache->size >> 10);
283 }
284
285 /* AMD legacy cache information from CPUID 0x80000005-6 leaves */
read_amd_caches_legacy(struct procinfo * infos,struct cpuiddump * src_cpuiddump,unsigned legacy_max_log_proc)286 static void read_amd_caches_legacy(struct procinfo *infos, struct cpuiddump *src_cpuiddump, unsigned legacy_max_log_proc)
287 {
288 unsigned eax, ebx, ecx, edx;
289
290 eax = 0x80000005;
291 cpuid_or_from_dump(&eax, &ebx, &ecx, &edx, src_cpuiddump);
292 setup__amd_cache_legacy(infos, 1, HWLOC_OBJ_CACHE_DATA, 1, ecx); /* private L1d */
293 setup__amd_cache_legacy(infos, 1, HWLOC_OBJ_CACHE_INSTRUCTION, 1, edx); /* private L1i */
294
295 eax = 0x80000006;
296 cpuid_or_from_dump(&eax, &ebx, &ecx, &edx, src_cpuiddump);
297 if (ecx & 0xf000)
298 /* This is actually supported on Intel but LinePerTag isn't returned in bits 8-11.
299 * Could be useful if some Intels (at least before Core micro-architecture)
300 * support this leaf without leaf 0x4.
301 */
302 setup__amd_cache_legacy(infos, 2, HWLOC_OBJ_CACHE_UNIFIED, 1, ecx); /* private L2u */
303 if (edx & 0xf000)
304 setup__amd_cache_legacy(infos, 3, HWLOC_OBJ_CACHE_UNIFIED, legacy_max_log_proc, edx); /* package-wide L3u */
305 }
306
307 /* AMD caches from CPUID 0x8000001d leaf (topoext) */
read_amd_caches_topoext(struct procinfo * infos,struct cpuiddump * src_cpuiddump)308 static void read_amd_caches_topoext(struct procinfo *infos, struct cpuiddump *src_cpuiddump)
309 {
310 unsigned eax, ebx, ecx, edx;
311 unsigned cachenum;
312 struct cacheinfo *cache;
313
314 /* the code below doesn't want any other cache yet */
315 assert(!infos->numcaches);
316
317 for (cachenum = 0; ; cachenum++) {
318 eax = 0x8000001d;
319 ecx = cachenum;
320 cpuid_or_from_dump(&eax, &ebx, &ecx, &edx, src_cpuiddump);
321 if ((eax & 0x1f) == 0)
322 break;
323 infos->numcaches++;
324 }
325
326 cache = infos->cache = malloc(infos->numcaches * sizeof(*infos->cache));
327 if (cache) {
328 for (cachenum = 0; ; cachenum++) {
329 unsigned long linesize, linepart, ways, sets;
330 eax = 0x8000001d;
331 ecx = cachenum;
332 cpuid_or_from_dump(&eax, &ebx, &ecx, &edx, src_cpuiddump);
333
334 if ((eax & 0x1f) == 0)
335 break;
336 switch (eax & 0x1f) {
337 case 1: cache->type = HWLOC_OBJ_CACHE_DATA; break;
338 case 2: cache->type = HWLOC_OBJ_CACHE_INSTRUCTION; break;
339 default: cache->type = HWLOC_OBJ_CACHE_UNIFIED; break;
340 }
341
342 cache->level = (eax >> 5) & 0x7;
343 /* Note: actually number of cores */
344 cache->nbthreads_sharing = ((eax >> 14) & 0xfff) + 1;
345
346 cache->linesize = linesize = (ebx & 0xfff) + 1;
347 cache->linepart = linepart = ((ebx >> 12) & 0x3ff) + 1;
348 ways = ((ebx >> 22) & 0x3ff) + 1;
349
350 if (eax & (1 << 9))
351 /* Fully associative */
352 cache->ways = -1;
353 else
354 cache->ways = ways;
355 cache->sets = sets = ecx + 1;
356 cache->size = linesize * linepart * ways * sets;
357 cache->inclusive = edx & 0x2;
358
359 hwloc_debug("cache %u L%u%c t%u linesize %lu linepart %lu ways %lu sets %lu, size %luKB\n",
360 cachenum, cache->level,
361 cache->type == HWLOC_OBJ_CACHE_DATA ? 'd' : cache->type == HWLOC_OBJ_CACHE_INSTRUCTION ? 'i' : 'u',
362 cache->nbthreads_sharing, linesize, linepart, ways, sets, cache->size >> 10);
363
364 cache++;
365 }
366 } else {
367 infos->numcaches = 0;
368 }
369 }
370
371 /* Intel cache info from CPUID 0x04 leaf */
read_intel_caches(struct hwloc_x86_backend_data_s * data,struct procinfo * infos,struct cpuiddump * src_cpuiddump)372 static void read_intel_caches(struct hwloc_x86_backend_data_s *data, struct procinfo *infos, struct cpuiddump *src_cpuiddump)
373 {
374 unsigned level;
375 struct cacheinfo *tmpcaches;
376 unsigned eax, ebx, ecx, edx;
377 unsigned oldnumcaches = infos->numcaches; /* in case we got caches above */
378 unsigned cachenum;
379 struct cacheinfo *cache;
380
381 for (cachenum = 0; ; cachenum++) {
382 eax = 0x04;
383 ecx = cachenum;
384 cpuid_or_from_dump(&eax, &ebx, &ecx, &edx, src_cpuiddump);
385
386 hwloc_debug("cache %u type %u\n", cachenum, eax & 0x1f);
387 if ((eax & 0x1f) == 0)
388 break;
389 level = (eax >> 5) & 0x7;
390 if (data->is_knl && level == 3)
391 /* KNL reports wrong L3 information (size always 0, cpuset always the entire machine, ignore it */
392 break;
393 infos->numcaches++;
394 }
395
396 tmpcaches = realloc(infos->cache, infos->numcaches * sizeof(*infos->cache));
397 if (!tmpcaches) {
398 infos->numcaches = oldnumcaches;
399 } else {
400 infos->cache = tmpcaches;
401 cache = &infos->cache[oldnumcaches];
402
403 for (cachenum = 0; ; cachenum++) {
404 unsigned long linesize, linepart, ways, sets;
405 eax = 0x04;
406 ecx = cachenum;
407 cpuid_or_from_dump(&eax, &ebx, &ecx, &edx, src_cpuiddump);
408
409 if ((eax & 0x1f) == 0)
410 break;
411 level = (eax >> 5) & 0x7;
412 if (data->is_knl && level == 3)
413 /* KNL reports wrong L3 information (size always 0, cpuset always the entire machine, ignore it */
414 break;
415 switch (eax & 0x1f) {
416 case 1: cache->type = HWLOC_OBJ_CACHE_DATA; break;
417 case 2: cache->type = HWLOC_OBJ_CACHE_INSTRUCTION; break;
418 default: cache->type = HWLOC_OBJ_CACHE_UNIFIED; break;
419 }
420
421 cache->level = level;
422 cache->nbthreads_sharing = ((eax >> 14) & 0xfff) + 1;
423
424 cache->linesize = linesize = (ebx & 0xfff) + 1;
425 cache->linepart = linepart = ((ebx >> 12) & 0x3ff) + 1;
426 ways = ((ebx >> 22) & 0x3ff) + 1;
427 if (eax & (1 << 9))
428 /* Fully associative */
429 cache->ways = -1;
430 else
431 cache->ways = ways;
432 cache->sets = sets = ecx + 1;
433 cache->size = linesize * linepart * ways * sets;
434 cache->inclusive = edx & 0x2;
435
436 hwloc_debug("cache %u L%u%c t%u linesize %lu linepart %lu ways %lu sets %lu, size %luKB\n",
437 cachenum, cache->level,
438 cache->type == HWLOC_OBJ_CACHE_DATA ? 'd' : cache->type == HWLOC_OBJ_CACHE_INSTRUCTION ? 'i' : 'u',
439 cache->nbthreads_sharing, linesize, linepart, ways, sets, cache->size >> 10);
440 cache++;
441 }
442 }
443 }
444
445 /* AMD core/thread info from CPUID 0x80000008 leaf */
read_amd_cores_legacy(struct procinfo * infos,struct cpuiddump * src_cpuiddump)446 static void read_amd_cores_legacy(struct procinfo *infos, struct cpuiddump *src_cpuiddump)
447 {
448 unsigned eax, ebx, ecx, edx;
449 unsigned max_nbcores;
450 unsigned max_nbthreads;
451 unsigned coreidsize;
452 unsigned logprocid;
453 unsigned threadid __hwloc_attribute_unused;
454
455 eax = 0x80000008;
456 cpuid_or_from_dump(&eax, &ebx, &ecx, &edx, src_cpuiddump);
457
458 coreidsize = (ecx >> 12) & 0xf;
459 hwloc_debug("core ID size: %u\n", coreidsize);
460 if (!coreidsize) {
461 max_nbcores = (ecx & 0xff) + 1;
462 } else
463 max_nbcores = 1 << coreidsize;
464 hwloc_debug("Thus max # of cores: %u\n", max_nbcores);
465
466 /* No multithreaded AMD for this old CPUID leaf */
467 max_nbthreads = 1 ;
468 hwloc_debug("and max # of threads: %u\n", max_nbthreads);
469
470 /* legacy_max_log_proc is deprecated, it can be smaller than max_nbcores,
471 * which is the maximum number of cores that the processor could theoretically support
472 * (see "Multiple Core Calculation" in the AMD CPUID specification).
473 * Recompute packageid/coreid accordingly.
474 */
475 infos->ids[PKG] = infos->apicid / max_nbcores;
476 logprocid = infos->apicid % max_nbcores;
477 infos->ids[CORE] = logprocid / max_nbthreads;
478 threadid = logprocid % max_nbthreads;
479 hwloc_debug("this is thread %u of core %u\n", threadid, infos->ids[CORE]);
480 }
481
482 /* AMD unit/node from CPUID 0x8000001e leaf (topoext) */
read_amd_cores_topoext(struct procinfo * infos,unsigned long flags,struct cpuiddump * src_cpuiddump)483 static void read_amd_cores_topoext(struct procinfo *infos, unsigned long flags, struct cpuiddump *src_cpuiddump)
484 {
485 unsigned apic_id, nodes_per_proc = 0;
486 unsigned eax, ebx, ecx, edx;
487
488 eax = 0x8000001e;
489 cpuid_or_from_dump(&eax, &ebx, &ecx, &edx, src_cpuiddump);
490 infos->apicid = apic_id = eax;
491
492 if (flags & HWLOC_X86_DISC_FLAG_TOPOEXT_NUMANODES) {
493 if (infos->cpufamilynumber == 0x16) {
494 /* ecx is reserved */
495 infos->ids[NODE] = 0;
496 nodes_per_proc = 1;
497 } else {
498 /* AMD other families or Hygon family 18h */
499 infos->ids[NODE] = ecx & 0xff;
500 nodes_per_proc = ((ecx >> 8) & 7) + 1;
501 }
502 if ((infos->cpufamilynumber == 0x15 && nodes_per_proc > 2)
503 || ((infos->cpufamilynumber == 0x17 || infos->cpufamilynumber == 0x18) && nodes_per_proc > 4)) {
504 hwloc_debug("warning: undefined nodes_per_proc value %u, assuming it means %u\n", nodes_per_proc, nodes_per_proc);
505 }
506 }
507
508 if (infos->cpufamilynumber <= 0x16) { /* topoext appeared in 0x15 and compute-units were only used in 0x15 and 0x16 */
509 unsigned cores_per_unit;
510 /* coreid was obtained from read_amd_cores_legacy() earlier */
511 infos->ids[UNIT] = ebx & 0xff;
512 cores_per_unit = ((ebx >> 8) & 0xff) + 1;
513 hwloc_debug("topoext %08x, %u nodes, node %u, %u cores in unit %u\n", apic_id, nodes_per_proc, infos->ids[NODE], cores_per_unit, infos->ids[UNIT]);
514 /* coreid and unitid are package-wide (core 0-15 and unit 0-7 on 16-core 2-NUMAnode processor).
515 * The Linux kernel reduces theses to NUMA-node-wide (by applying %core_per_node and %unit_per node respectively).
516 * It's not clear if we should do this as well.
517 */
518 } else {
519 unsigned threads_per_core;
520 infos->ids[CORE] = ebx & 0xff;
521 threads_per_core = ((ebx >> 8) & 0xff) + 1;
522 hwloc_debug("topoext %08x, %u nodes, node %u, %u threads in core %u\n", apic_id, nodes_per_proc, infos->ids[NODE], threads_per_core, infos->ids[CORE]);
523 }
524 }
525
526 /* Intel core/thread or even die/module/tile from CPUID 0x0b or 0x1f leaves (v1 and v2 extended topology enumeration) */
read_intel_cores_exttopoenum(struct procinfo * infos,unsigned leaf,struct cpuiddump * src_cpuiddump)527 static void read_intel_cores_exttopoenum(struct procinfo *infos, unsigned leaf, struct cpuiddump *src_cpuiddump)
528 {
529 unsigned level, apic_nextshift, apic_number, apic_type, apic_id = 0, apic_shift = 0, id;
530 unsigned threadid __hwloc_attribute_unused = 0; /* shut-up compiler */
531 unsigned eax, ebx, ecx = 0, edx;
532 int apic_packageshift = 0;
533
534 for (level = 0; ; level++) {
535 ecx = level;
536 eax = leaf;
537 cpuid_or_from_dump(&eax, &ebx, &ecx, &edx, src_cpuiddump);
538 if (!eax && !ebx)
539 break;
540 apic_packageshift = eax & 0x1f;
541 }
542
543 if (level) {
544 infos->otherids = malloc(level * sizeof(*infos->otherids));
545 if (infos->otherids) {
546 infos->levels = level;
547 for (level = 0; ; level++) {
548 ecx = level;
549 eax = leaf;
550 cpuid_or_from_dump(&eax, &ebx, &ecx, &edx, src_cpuiddump);
551 if (!eax && !ebx)
552 break;
553 apic_nextshift = eax & 0x1f;
554 apic_number = ebx & 0xffff;
555 apic_type = (ecx & 0xff00) >> 8;
556 apic_id = edx;
557 id = (apic_id >> apic_shift) & ((1 << (apic_packageshift - apic_shift)) - 1);
558 hwloc_debug("x2APIC %08x %u: nextshift %u num %2u type %u id %2u\n", apic_id, level, apic_nextshift, apic_number, apic_type, id);
559 infos->apicid = apic_id;
560 infos->otherids[level] = UINT_MAX;
561 switch (apic_type) {
562 case 1:
563 threadid = id;
564 /* apic_number is the actual number of threads per core */
565 break;
566 case 2:
567 infos->ids[CORE] = id;
568 /* apic_number is the actual number of threads per die */
569 break;
570 case 3:
571 infos->ids[MODULE] = id;
572 /* apic_number is the actual number of threads per tile */
573 break;
574 case 4:
575 infos->ids[TILE] = id;
576 /* apic_number is the actual number of threads per die */
577 break;
578 case 5:
579 infos->ids[DIE] = id;
580 /* apic_number is the actual number of threads per package */
581 break;
582 default:
583 hwloc_debug("x2APIC %u: unknown type %u\n", level, apic_type);
584 infos->otherids[level] = apic_id >> apic_shift;
585 break;
586 }
587 apic_shift = apic_nextshift;
588 }
589 infos->apicid = apic_id;
590 infos->ids[PKG] = apic_id >> apic_shift;
591 hwloc_debug("x2APIC remainder: %u\n", infos->ids[PKG]);
592 hwloc_debug("this is thread %u of core %u\n", threadid, infos->ids[CORE]);
593 }
594 }
595 }
596
597 /* Fetch information from the processor itself thanks to cpuid and store it in
598 * infos for summarize to analyze them globally */
look_proc(struct hwloc_backend * backend,struct procinfo * infos,unsigned long flags,unsigned highest_cpuid,unsigned highest_ext_cpuid,unsigned * features,enum cpuid_type cpuid_type,struct cpuiddump * src_cpuiddump)599 static void look_proc(struct hwloc_backend *backend, struct procinfo *infos, unsigned long flags, unsigned highest_cpuid, unsigned highest_ext_cpuid, unsigned *features, enum cpuid_type cpuid_type, struct cpuiddump *src_cpuiddump)
600 {
601 struct hwloc_x86_backend_data_s *data = backend->private_data;
602 unsigned eax, ebx, ecx = 0, edx;
603 unsigned cachenum;
604 struct cacheinfo *cache;
605 unsigned regs[4];
606 unsigned legacy_max_log_proc; /* not valid on Intel processors with > 256 threads, or when cpuid 0x80000008 is supported */
607 unsigned legacy_log_proc_id;
608 unsigned _model, _extendedmodel, _family, _extendedfamily;
609
610 infos->present = 1;
611
612 /* Get apicid, legacy_max_log_proc, packageid, legacy_log_proc_id from cpuid 0x01 */
613 eax = 0x01;
614 cpuid_or_from_dump(&eax, &ebx, &ecx, &edx, src_cpuiddump);
615 infos->apicid = ebx >> 24;
616 if (edx & (1 << 28))
617 legacy_max_log_proc = 1 << hwloc_flsl(((ebx >> 16) & 0xff) - 1);
618 else
619 legacy_max_log_proc = 1;
620 hwloc_debug("APIC ID 0x%02x legacy_max_log_proc %u\n", infos->apicid, legacy_max_log_proc);
621 infos->ids[PKG] = infos->apicid / legacy_max_log_proc;
622 legacy_log_proc_id = infos->apicid % legacy_max_log_proc;
623 hwloc_debug("phys %u legacy thread %u\n", infos->ids[PKG], legacy_log_proc_id);
624
625 /* Get cpu model/family/stepping numbers from same cpuid */
626 _model = (eax>>4) & 0xf;
627 _extendedmodel = (eax>>16) & 0xf;
628 _family = (eax>>8) & 0xf;
629 _extendedfamily = (eax>>20) & 0xff;
630 if ((cpuid_type == intel || cpuid_type == amd || cpuid_type == hygon) && _family == 0xf) {
631 infos->cpufamilynumber = _family + _extendedfamily;
632 } else {
633 infos->cpufamilynumber = _family;
634 }
635 if ((cpuid_type == intel && (_family == 0x6 || _family == 0xf))
636 || ((cpuid_type == amd || cpuid_type == hygon) && _family == 0xf)
637 || (cpuid_type == zhaoxin && (_family == 0x6 || _family == 0x7))) {
638 infos->cpumodelnumber = _model + (_extendedmodel << 4);
639 } else {
640 infos->cpumodelnumber = _model;
641 }
642 infos->cpustepping = eax & 0xf;
643
644 if (cpuid_type == intel && infos->cpufamilynumber == 0x6 &&
645 (infos->cpumodelnumber == 0x57 || infos->cpumodelnumber == 0x85))
646 data->is_knl = 1; /* KNM is the same as KNL */
647
648 /* Get cpu vendor string from cpuid 0x00 */
649 memset(regs, 0, sizeof(regs));
650 regs[0] = 0;
651 cpuid_or_from_dump(®s[0], ®s[1], ®s[3], ®s[2], src_cpuiddump);
652 memcpy(infos->cpuvendor, regs+1, 4*3);
653 /* infos was calloc'ed, already ends with \0 */
654
655 /* Get cpu model string from cpuid 0x80000002-4 */
656 if (highest_ext_cpuid >= 0x80000004) {
657 memset(regs, 0, sizeof(regs));
658 regs[0] = 0x80000002;
659 cpuid_or_from_dump(®s[0], ®s[1], ®s[2], ®s[3], src_cpuiddump);
660 memcpy(infos->cpumodel, regs, 4*4);
661 regs[0] = 0x80000003;
662 cpuid_or_from_dump(®s[0], ®s[1], ®s[2], ®s[3], src_cpuiddump);
663 memcpy(infos->cpumodel + 4*4, regs, 4*4);
664 regs[0] = 0x80000004;
665 cpuid_or_from_dump(®s[0], ®s[1], ®s[2], ®s[3], src_cpuiddump);
666 memcpy(infos->cpumodel + 4*4*2, regs, 4*4);
667 /* infos was calloc'ed, already ends with \0 */
668 }
669
670 if ((cpuid_type != amd && cpuid_type != hygon) && highest_cpuid >= 0x04) {
671 /* Get core/thread information from first cache reported by cpuid 0x04
672 * (not supported on AMD)
673 */
674 eax = 0x04;
675 ecx = 0;
676 cpuid_or_from_dump(&eax, &ebx, &ecx, &edx, src_cpuiddump);
677 if ((eax & 0x1f) != 0) {
678 /* cache looks valid */
679 unsigned max_nbcores;
680 unsigned max_nbthreads;
681 unsigned threadid __hwloc_attribute_unused;
682 max_nbcores = ((eax >> 26) & 0x3f) + 1;
683 max_nbthreads = legacy_max_log_proc / max_nbcores;
684 hwloc_debug("thus %u threads\n", max_nbthreads);
685 threadid = legacy_log_proc_id % max_nbthreads;
686 infos->ids[CORE] = legacy_log_proc_id / max_nbthreads;
687 hwloc_debug("this is thread %u of core %u\n", threadid, infos->ids[CORE]);
688 }
689 }
690
691 if (highest_cpuid >= 0x1a && has_hybrid(features)) {
692 /* Get hybrid cpu information from cpuid 0x1a */
693 eax = 0x1a;
694 ecx = 0;
695 cpuid_or_from_dump(&eax, &ebx, &ecx, &edx, src_cpuiddump);
696 infos->hybridcoretype = eax >> 24;
697 infos->hybridnativemodel = eax & 0xffffff;
698 }
699
700 /*********************************************************************************
701 * Get the hierarchy of thread, core, die, package, etc. from CPU-specific leaves
702 */
703
704 if (cpuid_type != intel && cpuid_type != zhaoxin && highest_ext_cpuid >= 0x80000008 && !has_x2apic(features)) {
705 /* Get core/thread information from cpuid 0x80000008
706 * (not supported on Intel)
707 * We could ignore this codepath when x2apic is supported, but we may need
708 * nodeids if HWLOC_X86_TOPOEXT_NUMANODES is set.
709 */
710 read_amd_cores_legacy(infos, src_cpuiddump);
711 }
712
713 if (cpuid_type != intel && cpuid_type != zhaoxin && has_topoext(features)) {
714 /* Get apicid, nodeid, unitid/coreid from cpuid 0x8000001e (AMD topology extension).
715 * Requires read_amd_cores_legacy() for coreid on family 0x15-16.
716 *
717 * Only needed when x2apic supported if NUMA nodes are needed.
718 */
719 read_amd_cores_topoext(infos, flags, src_cpuiddump);
720 }
721
722 if ((cpuid_type == intel) && highest_cpuid >= 0x1f) {
723 /* Get package/die/module/tile/core/thread information from cpuid 0x1f
724 * (Intel v2 Extended Topology Enumeration)
725 */
726 read_intel_cores_exttopoenum(infos, 0x1f, src_cpuiddump);
727
728 } else if ((cpuid_type == intel || cpuid_type == amd || cpuid_type == zhaoxin)
729 && highest_cpuid >= 0x0b && has_x2apic(features)) {
730 /* Get package/core/thread information from cpuid 0x0b
731 * (Intel v1 Extended Topology Enumeration)
732 */
733 read_intel_cores_exttopoenum(infos, 0x0b, src_cpuiddump);
734 }
735
736 /**************************************
737 * Get caches from CPU-specific leaves
738 */
739
740 infos->numcaches = 0;
741 infos->cache = NULL;
742
743 if (cpuid_type != intel && cpuid_type != zhaoxin && has_topoext(features)) {
744 /* Get cache information from cpuid 0x8000001d (AMD topology extension) */
745 read_amd_caches_topoext(infos, src_cpuiddump);
746
747 } else if (cpuid_type != intel && cpuid_type != zhaoxin && highest_ext_cpuid >= 0x80000006) {
748 /* If there's no topoext,
749 * get cache information from cpuid 0x80000005 and 0x80000006.
750 * (not supported on Intel)
751 * It looks like we cannot have 0x80000005 without 0x80000006.
752 */
753 read_amd_caches_legacy(infos, src_cpuiddump, legacy_max_log_proc);
754 }
755
756 if ((cpuid_type != amd && cpuid_type != hygon) && highest_cpuid >= 0x04) {
757 /* Get cache information from cpuid 0x04
758 * (not supported on AMD)
759 */
760 read_intel_caches(data, infos, src_cpuiddump);
761 }
762
763 /* Now that we have all info, compute cacheids and apply quirks */
764 for (cachenum = 0; cachenum < infos->numcaches; cachenum++) {
765 cache = &infos->cache[cachenum];
766
767 /* default cacheid value */
768 cache->cacheid = infos->apicid / cache->nbthreads_sharing;
769
770 if (cpuid_type == intel) {
771 /* round nbthreads_sharing to nearest power of two to build a mask (for clearing lower bits) */
772 unsigned bits = hwloc_flsl(cache->nbthreads_sharing-1);
773 unsigned mask = ~((1U<<bits) - 1);
774 cache->cacheid = infos->apicid & mask;
775
776 } else if (cpuid_type == amd) {
777 /* AMD quirks */
778 if (infos->cpufamilynumber == 0x17
779 && cache->level == 3 && cache->nbthreads_sharing == 6) {
780 /* AMD family 0x17 always shares L3 between 8 APIC ids,
781 * even when only 6 APIC ids are enabled and reported in nbthreads_sharing
782 * (on 24-core CPUs).
783 */
784 cache->cacheid = infos->apicid / 8;
785
786 } else if (infos->cpufamilynumber== 0x10 && infos->cpumodelnumber == 0x9
787 && cache->level == 3
788 && (cache->ways == -1 || (cache->ways % 2 == 0)) && cache->nbthreads_sharing >= 8) {
789 /* Fix AMD family 0x10 model 0x9 (Magny-Cours) with 8 or 12 cores.
790 * The L3 (and its associativity) is actually split into two halves).
791 */
792 if (cache->nbthreads_sharing == 16)
793 cache->nbthreads_sharing = 12; /* nbthreads_sharing is a power of 2 but the processor actually has 8 or 12 cores */
794 cache->nbthreads_sharing /= 2;
795 cache->size /= 2;
796 if (cache->ways != -1)
797 cache->ways /= 2;
798 /* AMD Magny-Cours 12-cores processor reserve APIC ids as AAAAAABBBBBB....
799 * among first L3 (A), second L3 (B), and unexisting cores (.).
800 * On multi-socket servers, L3 in non-first sockets may have APIC id ranges
801 * such as [16-21] that are not aligned on multiple of nbthreads_sharing (6).
802 * That means, we can't just compare apicid/nbthreads_sharing to identify siblings.
803 */
804 cache->cacheid = (infos->apicid % legacy_max_log_proc) / cache->nbthreads_sharing /* cacheid within the package */
805 + 2 * (infos->apicid / legacy_max_log_proc); /* add 2 caches per previous package */
806
807 } else if (infos->cpufamilynumber == 0x15
808 && (infos->cpumodelnumber == 0x1 /* Bulldozer */ || infos->cpumodelnumber == 0x2 /* Piledriver */)
809 && cache->level == 3 && cache->nbthreads_sharing == 6) {
810 /* AMD Bulldozer and Piledriver 12-core processors have same APIC ids as Magny-Cours below,
811 * but we can't merge the checks because the original nbthreads_sharing must be exactly 6 here.
812 */
813 cache->cacheid = (infos->apicid % legacy_max_log_proc) / cache->nbthreads_sharing /* cacheid within the package */
814 + 2 * (infos->apicid / legacy_max_log_proc); /* add 2 cache per previous package */
815 }
816 } else if (cpuid_type == hygon) {
817 if (infos->cpufamilynumber == 0x18
818 && cache->level == 3 && cache->nbthreads_sharing == 6) {
819 /* Hygon family 0x18 always shares L3 between 8 APIC ids,
820 * even when only 6 APIC ids are enabled and reported in nbthreads_sharing
821 * (on 24-core CPUs).
822 */
823 cache->cacheid = infos->apicid / 8;
824 }
825 }
826 }
827
828 if (hwloc_bitmap_isset(data->apicid_set, infos->apicid))
829 data->apicid_unique = 0;
830 else
831 hwloc_bitmap_set(data->apicid_set, infos->apicid);
832 }
833
834 static void
hwloc_x86_add_cpuinfos(hwloc_obj_t obj,struct procinfo * info,int replace)835 hwloc_x86_add_cpuinfos(hwloc_obj_t obj, struct procinfo *info, int replace)
836 {
837 char number[12];
838 if (info->cpuvendor[0])
839 hwloc__add_info_nodup(&obj->infos, &obj->infos_count, "CPUVendor", info->cpuvendor, replace);
840 snprintf(number, sizeof(number), "%u", info->cpufamilynumber);
841 hwloc__add_info_nodup(&obj->infos, &obj->infos_count, "CPUFamilyNumber", number, replace);
842 snprintf(number, sizeof(number), "%u", info->cpumodelnumber);
843 hwloc__add_info_nodup(&obj->infos, &obj->infos_count, "CPUModelNumber", number, replace);
844 if (info->cpumodel[0]) {
845 const char *c = info->cpumodel;
846 while (*c == ' ')
847 c++;
848 hwloc__add_info_nodup(&obj->infos, &obj->infos_count, "CPUModel", c, replace);
849 }
850 snprintf(number, sizeof(number), "%u", info->cpustepping);
851 hwloc__add_info_nodup(&obj->infos, &obj->infos_count, "CPUStepping", number, replace);
852 }
853
854 static void
hwloc_x86_add_groups(hwloc_topology_t topology,struct procinfo * infos,unsigned nbprocs,hwloc_bitmap_t remaining_cpuset,unsigned type,const char * subtype,unsigned kind,int dont_merge)855 hwloc_x86_add_groups(hwloc_topology_t topology,
856 struct procinfo *infos,
857 unsigned nbprocs,
858 hwloc_bitmap_t remaining_cpuset,
859 unsigned type,
860 const char *subtype,
861 unsigned kind,
862 int dont_merge)
863 {
864 hwloc_bitmap_t obj_cpuset;
865 hwloc_obj_t obj;
866 unsigned i, j;
867
868 while ((i = hwloc_bitmap_first(remaining_cpuset)) != (unsigned) -1) {
869 unsigned packageid = infos[i].ids[PKG];
870 unsigned id = infos[i].ids[type];
871
872 if (id == (unsigned)-1) {
873 hwloc_bitmap_clr(remaining_cpuset, i);
874 continue;
875 }
876
877 obj_cpuset = hwloc_bitmap_alloc();
878 for (j = i; j < nbprocs; j++) {
879 if (infos[j].ids[type] == (unsigned) -1) {
880 hwloc_bitmap_clr(remaining_cpuset, j);
881 continue;
882 }
883
884 if (infos[j].ids[PKG] == packageid && infos[j].ids[type] == id) {
885 hwloc_bitmap_set(obj_cpuset, j);
886 hwloc_bitmap_clr(remaining_cpuset, j);
887 }
888 }
889
890 obj = hwloc_alloc_setup_object(topology, HWLOC_OBJ_GROUP, id);
891 obj->cpuset = obj_cpuset;
892 obj->subtype = strdup(subtype);
893 obj->attr->group.kind = kind;
894 obj->attr->group.dont_merge = dont_merge;
895 hwloc_debug_2args_bitmap("os %s %u has cpuset %s\n",
896 subtype, id, obj_cpuset);
897 hwloc__insert_object_by_cpuset(topology, NULL, obj, "x86:group");
898 }
899 }
900
901 /* Analyse information stored in infos, and build/annotate topology levels accordingly */
summarize(struct hwloc_backend * backend,struct procinfo * infos,unsigned long flags)902 static void summarize(struct hwloc_backend *backend, struct procinfo *infos, unsigned long flags)
903 {
904 struct hwloc_topology *topology = backend->topology;
905 struct hwloc_x86_backend_data_s *data = backend->private_data;
906 unsigned nbprocs = data->nbprocs;
907 hwloc_bitmap_t complete_cpuset = hwloc_bitmap_alloc();
908 unsigned i, j, l, level;
909 int one = -1;
910 hwloc_bitmap_t remaining_cpuset;
911 int gotnuma = 0;
912 int fulldiscovery = (flags & HWLOC_X86_DISC_FLAG_FULL);
913
914 #ifdef HWLOC_DEBUG
915 hwloc_debug("\nSummary of x86 CPUID topology:\n");
916 for(i=0; i<nbprocs; i++) {
917 hwloc_debug("PU %u present=%u apicid=%u on PKG %d CORE %d DIE %d NODE %d\n",
918 i, infos[i].present, infos[i].apicid,
919 infos[i].ids[PKG], infos[i].ids[CORE], infos[i].ids[DIE], infos[i].ids[NODE]);
920 }
921 hwloc_debug("\n");
922 #endif
923
924 for (i = 0; i < nbprocs; i++)
925 if (infos[i].present) {
926 hwloc_bitmap_set(complete_cpuset, i);
927 one = i;
928 }
929
930 if (one == -1) {
931 hwloc_bitmap_free(complete_cpuset);
932 return;
933 }
934
935 remaining_cpuset = hwloc_bitmap_alloc();
936
937 /* Ideally, when fulldiscovery=0, we could add any object that doesn't exist yet.
938 * But what if the x86 and the native backends disagree because one is buggy? Which one to trust?
939 * We only add missing caches, and annotate other existing objects for now.
940 */
941
942 if (hwloc_filter_check_keep_object_type(topology, HWLOC_OBJ_PACKAGE)) {
943 /* Look for packages */
944 hwloc_obj_t package;
945
946 hwloc_bitmap_copy(remaining_cpuset, complete_cpuset);
947 while ((i = hwloc_bitmap_first(remaining_cpuset)) != (unsigned) -1) {
948 if (fulldiscovery) {
949 unsigned packageid = infos[i].ids[PKG];
950 hwloc_bitmap_t package_cpuset = hwloc_bitmap_alloc();
951
952 for (j = i; j < nbprocs; j++) {
953 if (infos[j].ids[PKG] == packageid) {
954 hwloc_bitmap_set(package_cpuset, j);
955 hwloc_bitmap_clr(remaining_cpuset, j);
956 }
957 }
958 package = hwloc_alloc_setup_object(topology, HWLOC_OBJ_PACKAGE, packageid);
959 package->cpuset = package_cpuset;
960
961 hwloc_x86_add_cpuinfos(package, &infos[i], 0);
962
963 hwloc_debug_1arg_bitmap("os package %u has cpuset %s\n",
964 packageid, package_cpuset);
965 hwloc__insert_object_by_cpuset(topology, NULL, package, "x86:package");
966
967 } else {
968 /* Annotate packages previously-existing packages */
969 hwloc_bitmap_t set = hwloc_bitmap_alloc();
970 hwloc_bitmap_set(set, i);
971 package = hwloc_get_next_obj_covering_cpuset_by_type(topology, set, HWLOC_OBJ_PACKAGE, NULL);
972 hwloc_bitmap_free(set);
973 if (package) {
974 /* Found package above that PU, annotate if no such attribute yet */
975 hwloc_x86_add_cpuinfos(package, &infos[i], 1);
976 hwloc_bitmap_andnot(remaining_cpuset, remaining_cpuset, package->cpuset);
977 } else {
978 /* No package, annotate the root object */
979 hwloc_x86_add_cpuinfos(hwloc_get_root_obj(topology), &infos[i], 1);
980 break;
981 }
982 }
983 }
984 }
985
986 /* Look for Numa nodes inside packages (cannot be filtered-out) */
987 if (fulldiscovery && (flags & HWLOC_X86_DISC_FLAG_TOPOEXT_NUMANODES)) {
988 hwloc_bitmap_t node_cpuset;
989 hwloc_obj_t node;
990
991 /* FIXME: if there's memory inside the root object, divide it into NUMA nodes? */
992
993 hwloc_bitmap_copy(remaining_cpuset, complete_cpuset);
994 while ((i = hwloc_bitmap_first(remaining_cpuset)) != (unsigned) -1) {
995 unsigned packageid = infos[i].ids[PKG];
996 unsigned nodeid = infos[i].ids[NODE];
997
998 if (nodeid == (unsigned)-1) {
999 hwloc_bitmap_clr(remaining_cpuset, i);
1000 continue;
1001 }
1002
1003 node_cpuset = hwloc_bitmap_alloc();
1004 for (j = i; j < nbprocs; j++) {
1005 if (infos[j].ids[NODE] == (unsigned) -1) {
1006 hwloc_bitmap_clr(remaining_cpuset, j);
1007 continue;
1008 }
1009
1010 if (infos[j].ids[PKG] == packageid && infos[j].ids[NODE] == nodeid) {
1011 hwloc_bitmap_set(node_cpuset, j);
1012 hwloc_bitmap_clr(remaining_cpuset, j);
1013 }
1014 }
1015 node = hwloc_alloc_setup_object(topology, HWLOC_OBJ_NUMANODE, nodeid);
1016 node->cpuset = node_cpuset;
1017 node->nodeset = hwloc_bitmap_alloc();
1018 hwloc_bitmap_set(node->nodeset, nodeid);
1019 hwloc_debug_1arg_bitmap("os node %u has cpuset %s\n",
1020 nodeid, node_cpuset);
1021 hwloc__insert_object_by_cpuset(topology, NULL, node, "x86:numa");
1022 gotnuma++;
1023 }
1024 }
1025
1026 if (hwloc_filter_check_keep_object_type(topology, HWLOC_OBJ_GROUP)) {
1027 if (fulldiscovery) {
1028 /* Look for AMD Compute units inside packages */
1029 hwloc_bitmap_copy(remaining_cpuset, complete_cpuset);
1030 hwloc_x86_add_groups(topology, infos, nbprocs, remaining_cpuset,
1031 UNIT, "Compute Unit",
1032 HWLOC_GROUP_KIND_AMD_COMPUTE_UNIT, 0);
1033 /* Look for Intel Modules inside packages */
1034 hwloc_bitmap_copy(remaining_cpuset, complete_cpuset);
1035 hwloc_x86_add_groups(topology, infos, nbprocs, remaining_cpuset,
1036 MODULE, "Module",
1037 HWLOC_GROUP_KIND_INTEL_MODULE, 0);
1038 /* Look for Intel Tiles inside packages */
1039 hwloc_bitmap_copy(remaining_cpuset, complete_cpuset);
1040 hwloc_x86_add_groups(topology, infos, nbprocs, remaining_cpuset,
1041 TILE, "Tile",
1042 HWLOC_GROUP_KIND_INTEL_TILE, 0);
1043
1044 /* Look for unknown objects */
1045 if (infos[one].otherids) {
1046 for (level = infos[one].levels-1; level <= infos[one].levels-1; level--) {
1047 if (infos[one].otherids[level] != UINT_MAX) {
1048 hwloc_bitmap_t unknown_cpuset;
1049 hwloc_obj_t unknown_obj;
1050
1051 hwloc_bitmap_copy(remaining_cpuset, complete_cpuset);
1052 while ((i = hwloc_bitmap_first(remaining_cpuset)) != (unsigned) -1) {
1053 unsigned unknownid = infos[i].otherids[level];
1054
1055 unknown_cpuset = hwloc_bitmap_alloc();
1056 for (j = i; j < nbprocs; j++) {
1057 if (infos[j].otherids[level] == unknownid) {
1058 hwloc_bitmap_set(unknown_cpuset, j);
1059 hwloc_bitmap_clr(remaining_cpuset, j);
1060 }
1061 }
1062 unknown_obj = hwloc_alloc_setup_object(topology, HWLOC_OBJ_GROUP, unknownid);
1063 unknown_obj->cpuset = unknown_cpuset;
1064 unknown_obj->attr->group.kind = HWLOC_GROUP_KIND_INTEL_EXTTOPOENUM_UNKNOWN;
1065 unknown_obj->attr->group.subkind = level;
1066 hwloc_debug_2args_bitmap("os unknown%u %u has cpuset %s\n",
1067 level, unknownid, unknown_cpuset);
1068 hwloc__insert_object_by_cpuset(topology, NULL, unknown_obj, "x86:group:unknown");
1069 }
1070 }
1071 }
1072 }
1073 }
1074 }
1075
1076 if (hwloc_filter_check_keep_object_type(topology, HWLOC_OBJ_DIE)) {
1077 /* Look for Intel Dies inside packages */
1078 if (fulldiscovery) {
1079 hwloc_bitmap_t die_cpuset;
1080 hwloc_obj_t die;
1081
1082 hwloc_bitmap_copy(remaining_cpuset, complete_cpuset);
1083 while ((i = hwloc_bitmap_first(remaining_cpuset)) != (unsigned) -1) {
1084 unsigned packageid = infos[i].ids[PKG];
1085 unsigned dieid = infos[i].ids[DIE];
1086
1087 if (dieid == (unsigned) -1) {
1088 hwloc_bitmap_clr(remaining_cpuset, i);
1089 continue;
1090 }
1091
1092 die_cpuset = hwloc_bitmap_alloc();
1093 for (j = i; j < nbprocs; j++) {
1094 if (infos[j].ids[DIE] == (unsigned) -1) {
1095 hwloc_bitmap_clr(remaining_cpuset, j);
1096 continue;
1097 }
1098
1099 if (infos[j].ids[PKG] == packageid && infos[j].ids[DIE] == dieid) {
1100 hwloc_bitmap_set(die_cpuset, j);
1101 hwloc_bitmap_clr(remaining_cpuset, j);
1102 }
1103 }
1104 die = hwloc_alloc_setup_object(topology, HWLOC_OBJ_DIE, dieid);
1105 die->cpuset = die_cpuset;
1106 hwloc_debug_1arg_bitmap("os die %u has cpuset %s\n",
1107 dieid, die_cpuset);
1108 hwloc__insert_object_by_cpuset(topology, NULL, die, "x86:die");
1109 }
1110 }
1111 }
1112
1113 if (hwloc_filter_check_keep_object_type(topology, HWLOC_OBJ_CORE)) {
1114 /* Look for cores */
1115 if (fulldiscovery) {
1116 hwloc_bitmap_t core_cpuset;
1117 hwloc_obj_t core;
1118
1119 hwloc_bitmap_copy(remaining_cpuset, complete_cpuset);
1120 while ((i = hwloc_bitmap_first(remaining_cpuset)) != (unsigned) -1) {
1121 unsigned packageid = infos[i].ids[PKG];
1122 unsigned nodeid = infos[i].ids[NODE];
1123 unsigned coreid = infos[i].ids[CORE];
1124
1125 if (coreid == (unsigned) -1) {
1126 hwloc_bitmap_clr(remaining_cpuset, i);
1127 continue;
1128 }
1129
1130 core_cpuset = hwloc_bitmap_alloc();
1131 for (j = i; j < nbprocs; j++) {
1132 if (infos[j].ids[CORE] == (unsigned) -1) {
1133 hwloc_bitmap_clr(remaining_cpuset, j);
1134 continue;
1135 }
1136
1137 if (infos[j].ids[PKG] == packageid && infos[j].ids[NODE] == nodeid && infos[j].ids[CORE] == coreid) {
1138 hwloc_bitmap_set(core_cpuset, j);
1139 hwloc_bitmap_clr(remaining_cpuset, j);
1140 }
1141 }
1142 core = hwloc_alloc_setup_object(topology, HWLOC_OBJ_CORE, coreid);
1143 core->cpuset = core_cpuset;
1144 hwloc_debug_1arg_bitmap("os core %u has cpuset %s\n",
1145 coreid, core_cpuset);
1146 hwloc__insert_object_by_cpuset(topology, NULL, core, "x86:core");
1147 }
1148 }
1149 }
1150
1151 /* Look for PUs (cannot be filtered-out) */
1152 if (fulldiscovery) {
1153 hwloc_debug("%s", "\n\n * CPU cpusets *\n\n");
1154 for (i=0; i<nbprocs; i++)
1155 if(infos[i].present) { /* Only add present PU. We don't know if others actually exist */
1156 struct hwloc_obj *obj = hwloc_alloc_setup_object(topology, HWLOC_OBJ_PU, i);
1157 obj->cpuset = hwloc_bitmap_alloc();
1158 hwloc_bitmap_only(obj->cpuset, i);
1159 hwloc_debug_1arg_bitmap("PU %u has cpuset %s\n", i, obj->cpuset);
1160 hwloc__insert_object_by_cpuset(topology, NULL, obj, "x86:pu");
1161 }
1162 }
1163
1164 /* Look for caches */
1165 /* First find max level */
1166 level = 0;
1167 for (i = 0; i < nbprocs; i++)
1168 for (j = 0; j < infos[i].numcaches; j++)
1169 if (infos[i].cache[j].level > level)
1170 level = infos[i].cache[j].level;
1171 while (level > 0) {
1172 hwloc_obj_cache_type_t type;
1173 HWLOC_BUILD_ASSERT(HWLOC_OBJ_CACHE_DATA == HWLOC_OBJ_CACHE_UNIFIED+1);
1174 HWLOC_BUILD_ASSERT(HWLOC_OBJ_CACHE_INSTRUCTION == HWLOC_OBJ_CACHE_DATA+1);
1175 for (type = HWLOC_OBJ_CACHE_UNIFIED; type <= HWLOC_OBJ_CACHE_INSTRUCTION; type++) {
1176 /* Look for caches of that type at level level */
1177 hwloc_obj_type_t otype;
1178 hwloc_obj_t cache;
1179
1180 otype = hwloc_cache_type_by_depth_type(level, type);
1181 if (otype == HWLOC_OBJ_TYPE_NONE)
1182 continue;
1183 if (!hwloc_filter_check_keep_object_type(topology, otype))
1184 continue;
1185
1186 hwloc_bitmap_copy(remaining_cpuset, complete_cpuset);
1187 while ((i = hwloc_bitmap_first(remaining_cpuset)) != (unsigned) -1) {
1188 hwloc_bitmap_t puset;
1189
1190 for (l = 0; l < infos[i].numcaches; l++) {
1191 if (infos[i].cache[l].level == level && infos[i].cache[l].type == type)
1192 break;
1193 }
1194 if (l == infos[i].numcaches) {
1195 /* no cache Llevel of that type in i */
1196 hwloc_bitmap_clr(remaining_cpuset, i);
1197 continue;
1198 }
1199
1200 puset = hwloc_bitmap_alloc();
1201 hwloc_bitmap_set(puset, i);
1202 cache = hwloc_get_next_obj_covering_cpuset_by_type(topology, puset, otype, NULL);
1203 hwloc_bitmap_free(puset);
1204
1205 if (cache) {
1206 /* Found cache above that PU, annotate if no such attribute yet */
1207 if (!hwloc_obj_get_info_by_name(cache, "Inclusive"))
1208 hwloc_obj_add_info(cache, "Inclusive", infos[i].cache[l].inclusive ? "1" : "0");
1209 hwloc_bitmap_andnot(remaining_cpuset, remaining_cpuset, cache->cpuset);
1210 } else {
1211 /* Add the missing cache */
1212 hwloc_bitmap_t cache_cpuset;
1213 unsigned packageid = infos[i].ids[PKG];
1214 unsigned cacheid = infos[i].cache[l].cacheid;
1215 /* Now look for others sharing it */
1216 cache_cpuset = hwloc_bitmap_alloc();
1217 for (j = i; j < nbprocs; j++) {
1218 unsigned l2;
1219 for (l2 = 0; l2 < infos[j].numcaches; l2++) {
1220 if (infos[j].cache[l2].level == level && infos[j].cache[l2].type == type)
1221 break;
1222 }
1223 if (l2 == infos[j].numcaches) {
1224 /* no cache Llevel of that type in j */
1225 hwloc_bitmap_clr(remaining_cpuset, j);
1226 continue;
1227 }
1228 if (infos[j].ids[PKG] == packageid && infos[j].cache[l2].cacheid == cacheid) {
1229 hwloc_bitmap_set(cache_cpuset, j);
1230 hwloc_bitmap_clr(remaining_cpuset, j);
1231 }
1232 }
1233 cache = hwloc_alloc_setup_object(topology, otype, HWLOC_UNKNOWN_INDEX);
1234 cache->attr->cache.depth = level;
1235 cache->attr->cache.size = infos[i].cache[l].size;
1236 cache->attr->cache.linesize = infos[i].cache[l].linesize;
1237 cache->attr->cache.associativity = infos[i].cache[l].ways;
1238 cache->attr->cache.type = infos[i].cache[l].type;
1239 cache->cpuset = cache_cpuset;
1240 hwloc_obj_add_info(cache, "Inclusive", infos[i].cache[l].inclusive ? "1" : "0");
1241 hwloc_debug_2args_bitmap("os L%u cache %u has cpuset %s\n",
1242 level, cacheid, cache_cpuset);
1243 hwloc__insert_object_by_cpuset(topology, NULL, cache, "x86:cache");
1244 }
1245 }
1246 }
1247 level--;
1248 }
1249
1250 /* FIXME: if KNL and L2 disabled, add tiles instead of L2 */
1251
1252 hwloc_bitmap_free(remaining_cpuset);
1253 hwloc_bitmap_free(complete_cpuset);
1254
1255 if (gotnuma)
1256 topology->support.discovery->numa = 1;
1257 }
1258
1259 static int
look_procs(struct hwloc_backend * backend,struct procinfo * infos,unsigned long flags,unsigned highest_cpuid,unsigned highest_ext_cpuid,unsigned * features,enum cpuid_type cpuid_type,int (* get_cpubind)(hwloc_topology_t topology,hwloc_cpuset_t set,int flags),int (* set_cpubind)(hwloc_topology_t topology,hwloc_const_cpuset_t set,int flags),hwloc_bitmap_t restrict_set)1260 look_procs(struct hwloc_backend *backend, struct procinfo *infos, unsigned long flags,
1261 unsigned highest_cpuid, unsigned highest_ext_cpuid, unsigned *features, enum cpuid_type cpuid_type,
1262 int (*get_cpubind)(hwloc_topology_t topology, hwloc_cpuset_t set, int flags),
1263 int (*set_cpubind)(hwloc_topology_t topology, hwloc_const_cpuset_t set, int flags),
1264 hwloc_bitmap_t restrict_set)
1265 {
1266 struct hwloc_x86_backend_data_s *data = backend->private_data;
1267 struct hwloc_topology *topology = backend->topology;
1268 unsigned nbprocs = data->nbprocs;
1269 hwloc_bitmap_t orig_cpuset = NULL;
1270 hwloc_bitmap_t set = NULL;
1271 unsigned i;
1272
1273 if (!data->src_cpuiddump_path) {
1274 orig_cpuset = hwloc_bitmap_alloc();
1275 if (get_cpubind(topology, orig_cpuset, HWLOC_CPUBIND_STRICT)) {
1276 hwloc_bitmap_free(orig_cpuset);
1277 return -1;
1278 }
1279 set = hwloc_bitmap_alloc();
1280 }
1281
1282 for (i = 0; i < nbprocs; i++) {
1283 struct cpuiddump *src_cpuiddump = NULL;
1284
1285 if (restrict_set && !hwloc_bitmap_isset(restrict_set, i)) {
1286 /* skip this CPU outside of the binding mask */
1287 continue;
1288 }
1289
1290 if (data->src_cpuiddump_path) {
1291 src_cpuiddump = cpuiddump_read(data->src_cpuiddump_path, i);
1292 if (!src_cpuiddump)
1293 continue;
1294 } else {
1295 hwloc_bitmap_only(set, i);
1296 hwloc_debug("binding to CPU%u\n", i);
1297 if (set_cpubind(topology, set, HWLOC_CPUBIND_STRICT)) {
1298 hwloc_debug("could not bind to CPU%u: %s\n", i, strerror(errno));
1299 continue;
1300 }
1301 }
1302
1303 look_proc(backend, &infos[i], flags, highest_cpuid, highest_ext_cpuid, features, cpuid_type, src_cpuiddump);
1304
1305 if (data->src_cpuiddump_path) {
1306 cpuiddump_free(src_cpuiddump);
1307 }
1308 }
1309
1310 if (!data->src_cpuiddump_path) {
1311 set_cpubind(topology, orig_cpuset, 0);
1312 hwloc_bitmap_free(set);
1313 hwloc_bitmap_free(orig_cpuset);
1314 }
1315
1316 if (data->apicid_unique) {
1317 summarize(backend, infos, flags);
1318
1319 if (has_hybrid(features)) {
1320 /* use hybrid info for cpukinds */
1321 hwloc_bitmap_t atomset = hwloc_bitmap_alloc();
1322 hwloc_bitmap_t coreset = hwloc_bitmap_alloc();
1323 for(i=0; i<nbprocs; i++) {
1324 if (infos[i].hybridcoretype == 0x20)
1325 hwloc_bitmap_set(atomset, i);
1326 else if (infos[i].hybridcoretype == 0x40)
1327 hwloc_bitmap_set(coreset, i);
1328 }
1329 /* register IntelAtom set if any */
1330 if (!hwloc_bitmap_iszero(atomset)) {
1331 struct hwloc_info_s infoattr;
1332 infoattr.name = (char *) "CoreType";
1333 infoattr.value = (char *) "IntelAtom";
1334 hwloc_internal_cpukinds_register(topology, atomset, HWLOC_CPUKIND_EFFICIENCY_UNKNOWN, &infoattr, 1, 0);
1335 /* the cpuset is given to the callee */
1336 } else {
1337 hwloc_bitmap_free(atomset);
1338 }
1339 /* register IntelCore set if any */
1340 if (!hwloc_bitmap_iszero(coreset)) {
1341 struct hwloc_info_s infoattr;
1342 infoattr.name = (char *) "CoreType";
1343 infoattr.value = (char *) "IntelCore";
1344 hwloc_internal_cpukinds_register(topology, coreset, HWLOC_CPUKIND_EFFICIENCY_UNKNOWN, &infoattr, 1, 0);
1345 /* the cpuset is given to the callee */
1346 } else {
1347 hwloc_bitmap_free(coreset);
1348 }
1349 }
1350 }
1351 /* if !data->apicid_unique, do nothing and return success, so that the caller does nothing either */
1352
1353 return 0;
1354 }
1355
1356 #if defined HWLOC_FREEBSD_SYS && defined HAVE_CPUSET_SETID
1357 #include <sys/param.h>
1358 #include <sys/cpuset.h>
1359 typedef cpusetid_t hwloc_x86_os_state_t;
hwloc_x86_os_state_save(hwloc_x86_os_state_t * state,struct cpuiddump * src_cpuiddump)1360 static void hwloc_x86_os_state_save(hwloc_x86_os_state_t *state, struct cpuiddump *src_cpuiddump)
1361 {
1362 if (!src_cpuiddump) {
1363 /* temporary make all cpus available during discovery */
1364 cpuset_getid(CPU_LEVEL_CPUSET, CPU_WHICH_PID, -1, state);
1365 cpuset_setid(CPU_WHICH_PID, -1, 0);
1366 }
1367 }
hwloc_x86_os_state_restore(hwloc_x86_os_state_t * state,struct cpuiddump * src_cpuiddump)1368 static void hwloc_x86_os_state_restore(hwloc_x86_os_state_t *state, struct cpuiddump *src_cpuiddump)
1369 {
1370 if (!src_cpuiddump) {
1371 /* restore initial cpuset */
1372 cpuset_setid(CPU_WHICH_PID, -1, *state);
1373 }
1374 }
1375 #else /* !defined HWLOC_FREEBSD_SYS || !defined HAVE_CPUSET_SETID */
1376 typedef void * hwloc_x86_os_state_t;
hwloc_x86_os_state_save(hwloc_x86_os_state_t * state __hwloc_attribute_unused,struct cpuiddump * src_cpuiddump __hwloc_attribute_unused)1377 static void hwloc_x86_os_state_save(hwloc_x86_os_state_t *state __hwloc_attribute_unused, struct cpuiddump *src_cpuiddump __hwloc_attribute_unused) { }
hwloc_x86_os_state_restore(hwloc_x86_os_state_t * state __hwloc_attribute_unused,struct cpuiddump * src_cpuiddump __hwloc_attribute_unused)1378 static void hwloc_x86_os_state_restore(hwloc_x86_os_state_t *state __hwloc_attribute_unused, struct cpuiddump *src_cpuiddump __hwloc_attribute_unused) { }
1379 #endif /* !defined HWLOC_FREEBSD_SYS || !defined HAVE_CPUSET_SETID */
1380
1381 /* GenuineIntel */
1382 #define INTEL_EBX ('G' | ('e'<<8) | ('n'<<16) | ('u'<<24))
1383 #define INTEL_EDX ('i' | ('n'<<8) | ('e'<<16) | ('I'<<24))
1384 #define INTEL_ECX ('n' | ('t'<<8) | ('e'<<16) | ('l'<<24))
1385
1386 /* AuthenticAMD */
1387 #define AMD_EBX ('A' | ('u'<<8) | ('t'<<16) | ('h'<<24))
1388 #define AMD_EDX ('e' | ('n'<<8) | ('t'<<16) | ('i'<<24))
1389 #define AMD_ECX ('c' | ('A'<<8) | ('M'<<16) | ('D'<<24))
1390
1391 /* HYGON "HygonGenuine" */
1392 #define HYGON_EBX ('H' | ('y'<<8) | ('g'<<16) | ('o'<<24))
1393 #define HYGON_EDX ('n' | ('G'<<8) | ('e'<<16) | ('n'<<24))
1394 #define HYGON_ECX ('u' | ('i'<<8) | ('n'<<16) | ('e'<<24))
1395
1396 /* (Zhaoxin) CentaurHauls */
1397 #define ZX_EBX ('C' | ('e'<<8) | ('n'<<16) | ('t'<<24))
1398 #define ZX_EDX ('a' | ('u'<<8) | ('r'<<16) | ('H'<<24))
1399 #define ZX_ECX ('a' | ('u'<<8) | ('l'<<16) | ('s'<<24))
1400 /* (Zhaoxin) Shanghai */
1401 #define SH_EBX (' ' | (' '<<8) | ('S'<<16) | ('h'<<24))
1402 #define SH_EDX ('a' | ('n'<<8) | ('g'<<16) | ('h'<<24))
1403 #define SH_ECX ('a' | ('i'<<8) | (' '<<16) | (' '<<24))
1404
1405 /* fake cpubind for when nbprocs=1 and no binding support */
fake_get_cpubind(hwloc_topology_t topology __hwloc_attribute_unused,hwloc_cpuset_t set __hwloc_attribute_unused,int flags __hwloc_attribute_unused)1406 static int fake_get_cpubind(hwloc_topology_t topology __hwloc_attribute_unused,
1407 hwloc_cpuset_t set __hwloc_attribute_unused,
1408 int flags __hwloc_attribute_unused)
1409 {
1410 return 0;
1411 }
fake_set_cpubind(hwloc_topology_t topology __hwloc_attribute_unused,hwloc_const_cpuset_t set __hwloc_attribute_unused,int flags __hwloc_attribute_unused)1412 static int fake_set_cpubind(hwloc_topology_t topology __hwloc_attribute_unused,
1413 hwloc_const_cpuset_t set __hwloc_attribute_unused,
1414 int flags __hwloc_attribute_unused)
1415 {
1416 return 0;
1417 }
1418
1419 static
hwloc_look_x86(struct hwloc_backend * backend,unsigned long flags)1420 int hwloc_look_x86(struct hwloc_backend *backend, unsigned long flags)
1421 {
1422 struct hwloc_x86_backend_data_s *data = backend->private_data;
1423 struct hwloc_topology *topology = backend->topology;
1424 unsigned nbprocs = data->nbprocs;
1425 unsigned eax, ebx, ecx = 0, edx;
1426 unsigned i;
1427 unsigned highest_cpuid;
1428 unsigned highest_ext_cpuid;
1429 /* This stores cpuid features with the same indexing as Linux */
1430 unsigned features[19] = { 0 };
1431 struct procinfo *infos = NULL;
1432 enum cpuid_type cpuid_type = unknown;
1433 hwloc_x86_os_state_t os_state;
1434 struct hwloc_binding_hooks hooks;
1435 struct hwloc_topology_support support;
1436 struct hwloc_topology_membind_support memsupport __hwloc_attribute_unused;
1437 int (*get_cpubind)(hwloc_topology_t topology, hwloc_cpuset_t set, int flags) = NULL;
1438 int (*set_cpubind)(hwloc_topology_t topology, hwloc_const_cpuset_t set, int flags) = NULL;
1439 hwloc_bitmap_t restrict_set = NULL;
1440 struct cpuiddump *src_cpuiddump = NULL;
1441 int ret = -1;
1442
1443 /* check if binding works */
1444 memset(&hooks, 0, sizeof(hooks));
1445 support.membind = &memsupport;
1446 /* We could just copy the main hooks (except in some corner cases),
1447 * but the current overhead is negligible, so just always reget them.
1448 */
1449 hwloc_set_native_binding_hooks(&hooks, &support);
1450 /* in theory, those are only needed if !data->src_cpuiddump_path || HWLOC_TOPOLOGY_FLAG_RESTRICT_TO_BINDING
1451 * but that's the vast majority of cases anyway, and the overhead is very small.
1452 */
1453
1454 if (data->src_cpuiddump_path) {
1455 /* Just read cpuid from the dump (implies !topology->is_thissystem by default) */
1456 src_cpuiddump = cpuiddump_read(data->src_cpuiddump_path, 0);
1457 if (!src_cpuiddump)
1458 goto out;
1459
1460 } else {
1461 /* Using real hardware.
1462 * However we don't enforce topology->is_thissystem so that
1463 * we may still force use this backend when debugging with !thissystem.
1464 */
1465
1466 if (hooks.get_thisthread_cpubind && hooks.set_thisthread_cpubind) {
1467 get_cpubind = hooks.get_thisthread_cpubind;
1468 set_cpubind = hooks.set_thisthread_cpubind;
1469 } else if (hooks.get_thisproc_cpubind && hooks.set_thisproc_cpubind) {
1470 /* FIXME: if called by a multithreaded program, we will restore the original process binding
1471 * for each thread instead of their own original thread binding.
1472 * See issue #158.
1473 */
1474 get_cpubind = hooks.get_thisproc_cpubind;
1475 set_cpubind = hooks.set_thisproc_cpubind;
1476 } else {
1477 /* we need binding support if there are multiple PUs */
1478 if (nbprocs > 1)
1479 goto out;
1480 get_cpubind = fake_get_cpubind;
1481 set_cpubind = fake_set_cpubind;
1482 }
1483 }
1484
1485 if (topology->flags & HWLOC_TOPOLOGY_FLAG_RESTRICT_TO_CPUBINDING) {
1486 restrict_set = hwloc_bitmap_alloc();
1487 if (!restrict_set)
1488 goto out;
1489 if (hooks.get_thisproc_cpubind)
1490 hooks.get_thisproc_cpubind(topology, restrict_set, 0);
1491 else if (hooks.get_thisthread_cpubind)
1492 hooks.get_thisthread_cpubind(topology, restrict_set, 0);
1493 if (hwloc_bitmap_iszero(restrict_set)) {
1494 hwloc_bitmap_free(restrict_set);
1495 restrict_set = NULL;
1496 }
1497 }
1498
1499 if (!src_cpuiddump && !hwloc_have_x86_cpuid())
1500 goto out;
1501
1502 infos = calloc(nbprocs, sizeof(struct procinfo));
1503 if (NULL == infos)
1504 goto out;
1505 for (i = 0; i < nbprocs; i++) {
1506 infos[i].ids[PKG] = (unsigned) -1;
1507 infos[i].ids[CORE] = (unsigned) -1;
1508 infos[i].ids[NODE] = (unsigned) -1;
1509 infos[i].ids[UNIT] = (unsigned) -1;
1510 infos[i].ids[TILE] = (unsigned) -1;
1511 infos[i].ids[MODULE] = (unsigned) -1;
1512 infos[i].ids[DIE] = (unsigned) -1;
1513 }
1514
1515 eax = 0x00;
1516 cpuid_or_from_dump(&eax, &ebx, &ecx, &edx, src_cpuiddump);
1517 highest_cpuid = eax;
1518 if (ebx == INTEL_EBX && ecx == INTEL_ECX && edx == INTEL_EDX)
1519 cpuid_type = intel;
1520 else if (ebx == AMD_EBX && ecx == AMD_ECX && edx == AMD_EDX)
1521 cpuid_type = amd;
1522 else if ((ebx == ZX_EBX && ecx == ZX_ECX && edx == ZX_EDX)
1523 || (ebx == SH_EBX && ecx == SH_ECX && edx == SH_EDX))
1524 cpuid_type = zhaoxin;
1525 else if (ebx == HYGON_EBX && ecx == HYGON_ECX && edx == HYGON_EDX)
1526 cpuid_type = hygon;
1527
1528 hwloc_debug("highest cpuid %x, cpuid type %u\n", highest_cpuid, cpuid_type);
1529 if (highest_cpuid < 0x01) {
1530 goto out_with_infos;
1531 }
1532
1533 eax = 0x01;
1534 cpuid_or_from_dump(&eax, &ebx, &ecx, &edx, src_cpuiddump);
1535 features[0] = edx;
1536 features[4] = ecx;
1537
1538 eax = 0x80000000;
1539 cpuid_or_from_dump(&eax, &ebx, &ecx, &edx, src_cpuiddump);
1540 highest_ext_cpuid = eax;
1541
1542 hwloc_debug("highest extended cpuid %x\n", highest_ext_cpuid);
1543
1544 if (highest_cpuid >= 0x7) {
1545 eax = 0x7;
1546 ecx = 0;
1547 cpuid_or_from_dump(&eax, &ebx, &ecx, &edx, src_cpuiddump);
1548 features[9] = ebx;
1549 features[18] = edx;
1550 }
1551
1552 if (cpuid_type != intel && highest_ext_cpuid >= 0x80000001) {
1553 eax = 0x80000001;
1554 cpuid_or_from_dump(&eax, &ebx, &ecx, &edx, src_cpuiddump);
1555 features[1] = edx;
1556 features[6] = ecx;
1557 }
1558
1559 hwloc_x86_os_state_save(&os_state, src_cpuiddump);
1560
1561 ret = look_procs(backend, infos, flags,
1562 highest_cpuid, highest_ext_cpuid, features, cpuid_type,
1563 get_cpubind, set_cpubind, restrict_set);
1564 if (!ret)
1565 /* success, we're done */
1566 goto out_with_os_state;
1567
1568 if (nbprocs == 1) {
1569 /* only one processor, no need to bind */
1570 look_proc(backend, &infos[0], flags, highest_cpuid, highest_ext_cpuid, features, cpuid_type, src_cpuiddump);
1571 summarize(backend, infos, flags);
1572 ret = 0;
1573 }
1574
1575 out_with_os_state:
1576 hwloc_x86_os_state_restore(&os_state, src_cpuiddump);
1577
1578 out_with_infos:
1579 if (NULL != infos) {
1580 for (i = 0; i < nbprocs; i++) {
1581 free(infos[i].cache);
1582 free(infos[i].otherids);
1583 }
1584 free(infos);
1585 }
1586
1587 out:
1588 hwloc_bitmap_free(restrict_set);
1589 if (src_cpuiddump)
1590 cpuiddump_free(src_cpuiddump);
1591 return ret;
1592 }
1593
1594 static int
hwloc_x86_discover(struct hwloc_backend * backend,struct hwloc_disc_status * dstatus)1595 hwloc_x86_discover(struct hwloc_backend *backend, struct hwloc_disc_status *dstatus)
1596 {
1597 struct hwloc_x86_backend_data_s *data = backend->private_data;
1598 struct hwloc_topology *topology = backend->topology;
1599 unsigned long flags = 0;
1600 int alreadypus = 0;
1601 int ret;
1602
1603 assert(dstatus->phase == HWLOC_DISC_PHASE_CPU);
1604
1605 if (topology->flags & HWLOC_TOPOLOGY_FLAG_DONT_CHANGE_BINDING) {
1606 /* TODO: Things would work if there's a single PU, no need to rebind */
1607 return 0;
1608 }
1609
1610 if (getenv("HWLOC_X86_TOPOEXT_NUMANODES")) {
1611 flags |= HWLOC_X86_DISC_FLAG_TOPOEXT_NUMANODES;
1612 }
1613
1614 #if HAVE_DECL_RUNNING_ON_VALGRIND
1615 if (RUNNING_ON_VALGRIND && !data->src_cpuiddump_path) {
1616 fprintf(stderr, "hwloc x86 backend cannot work under Valgrind, disabling.\n"
1617 "May be reenabled by dumping CPUIDs with hwloc-gather-cpuid\n"
1618 "and reloading them under Valgrind with HWLOC_CPUID_PATH.\n");
1619 return 0;
1620 }
1621 #endif
1622
1623 if (data->src_cpuiddump_path) {
1624 assert(data->nbprocs > 0); /* enforced by hwloc_x86_component_instantiate() */
1625 topology->support.discovery->pu = 1;
1626 } else {
1627 int nbprocs = hwloc_fallback_nbprocessors(HWLOC_FALLBACK_NBPROCESSORS_INCLUDE_OFFLINE);
1628 if (nbprocs >= 1)
1629 topology->support.discovery->pu = 1;
1630 else
1631 nbprocs = 1;
1632 data->nbprocs = (unsigned) nbprocs;
1633 }
1634
1635 if (topology->levels[0][0]->cpuset) {
1636 /* somebody else discovered things, reconnect levels so that we can look at them */
1637 hwloc_topology_reconnect(topology, 0);
1638 if (topology->nb_levels == 2 && topology->level_nbobjects[1] == data->nbprocs) {
1639 /* only PUs were discovered, as much as we would, complete the topology with everything else */
1640 alreadypus = 1;
1641 goto fulldiscovery;
1642 }
1643
1644 /* several object types were added, we can't easily complete, just do partial discovery */
1645 ret = hwloc_look_x86(backend, flags);
1646 if (ret)
1647 hwloc_obj_add_info(topology->levels[0][0], "Backend", "x86");
1648 return 0;
1649 } else {
1650 /* topology is empty, initialize it */
1651 hwloc_alloc_root_sets(topology->levels[0][0]);
1652 }
1653
1654 fulldiscovery:
1655 if (hwloc_look_x86(backend, flags | HWLOC_X86_DISC_FLAG_FULL) < 0) {
1656 /* if failed, create PUs */
1657 if (!alreadypus)
1658 hwloc_setup_pu_level(topology, data->nbprocs);
1659 }
1660
1661 hwloc_obj_add_info(topology->levels[0][0], "Backend", "x86");
1662
1663 if (!data->src_cpuiddump_path) { /* CPUID dump works for both x86 and x86_64 */
1664 #ifdef HAVE_UNAME
1665 hwloc_add_uname_info(topology, NULL); /* we already know is_thissystem() is true */
1666 #else
1667 /* uname isn't available, manually setup the "Architecture" info */
1668 #ifdef HWLOC_X86_64_ARCH
1669 hwloc_obj_add_info(topology->levels[0][0], "Architecture", "x86_64");
1670 #else
1671 hwloc_obj_add_info(topology->levels[0][0], "Architecture", "x86");
1672 #endif
1673 #endif
1674 }
1675
1676 return 1;
1677 }
1678
1679 static int
hwloc_x86_check_cpuiddump_input(const char * src_cpuiddump_path,hwloc_bitmap_t set)1680 hwloc_x86_check_cpuiddump_input(const char *src_cpuiddump_path, hwloc_bitmap_t set)
1681 {
1682
1683 #if !(defined HWLOC_WIN_SYS && !defined __MINGW32__ && !defined __CYGWIN__) /* needs a lot of work */
1684 struct dirent *dirent;
1685 DIR *dir;
1686 char *path;
1687 FILE *file;
1688 char line [32];
1689
1690 dir = opendir(src_cpuiddump_path);
1691 if (!dir)
1692 return -1;
1693
1694 path = malloc(strlen(src_cpuiddump_path) + strlen("/hwloc-cpuid-info") + 1);
1695 if (!path)
1696 goto out_with_dir;
1697 sprintf(path, "%s/hwloc-cpuid-info", src_cpuiddump_path);
1698 file = fopen(path, "r");
1699 if (!file) {
1700 fprintf(stderr, "Couldn't open dumped cpuid summary %s\n", path);
1701 goto out_with_path;
1702 }
1703 if (!fgets(line, sizeof(line), file)) {
1704 fprintf(stderr, "Found read dumped cpuid summary in %s\n", path);
1705 fclose(file);
1706 goto out_with_path;
1707 }
1708 fclose(file);
1709 if (strcmp(line, "Architecture: x86\n")) {
1710 fprintf(stderr, "Found non-x86 dumped cpuid summary in %s: %s\n", path, line);
1711 goto out_with_path;
1712 }
1713 free(path);
1714
1715 while ((dirent = readdir(dir)) != NULL) {
1716 if (!strncmp(dirent->d_name, "pu", 2)) {
1717 char *end;
1718 unsigned long idx = strtoul(dirent->d_name+2, &end, 10);
1719 if (!*end)
1720 hwloc_bitmap_set(set, idx);
1721 else
1722 fprintf(stderr, "Ignoring invalid dirent `%s' in dumped cpuid directory `%s'\n",
1723 dirent->d_name, src_cpuiddump_path);
1724 }
1725 }
1726 closedir(dir);
1727
1728 if (hwloc_bitmap_iszero(set)) {
1729 fprintf(stderr, "Did not find any valid pu%%u entry in dumped cpuid directory `%s'\n",
1730 src_cpuiddump_path);
1731 return -1;
1732 } else if (hwloc_bitmap_last(set) != hwloc_bitmap_weight(set) - 1) {
1733 /* The x86 backends enforces contigous set of PUs starting at 0 so far */
1734 fprintf(stderr, "Found non-contigous pu%%u range in dumped cpuid directory `%s'\n",
1735 src_cpuiddump_path);
1736 return -1;
1737 }
1738
1739 return 0;
1740
1741 out_with_path:
1742 free(path);
1743 out_with_dir:
1744 closedir(dir);
1745 #endif /* HWLOC_WIN_SYS & !__MINGW32__ needs a lot of work */
1746 return -1;
1747 }
1748
1749 static void
hwloc_x86_backend_disable(struct hwloc_backend * backend)1750 hwloc_x86_backend_disable(struct hwloc_backend *backend)
1751 {
1752 struct hwloc_x86_backend_data_s *data = backend->private_data;
1753 hwloc_bitmap_free(data->apicid_set);
1754 free(data->src_cpuiddump_path);
1755 free(data);
1756 }
1757
1758 static struct hwloc_backend *
hwloc_x86_component_instantiate(struct hwloc_topology * topology,struct hwloc_disc_component * component,unsigned excluded_phases __hwloc_attribute_unused,const void * _data1 __hwloc_attribute_unused,const void * _data2 __hwloc_attribute_unused,const void * _data3 __hwloc_attribute_unused)1759 hwloc_x86_component_instantiate(struct hwloc_topology *topology,
1760 struct hwloc_disc_component *component,
1761 unsigned excluded_phases __hwloc_attribute_unused,
1762 const void *_data1 __hwloc_attribute_unused,
1763 const void *_data2 __hwloc_attribute_unused,
1764 const void *_data3 __hwloc_attribute_unused)
1765 {
1766 struct hwloc_backend *backend;
1767 struct hwloc_x86_backend_data_s *data;
1768 const char *src_cpuiddump_path;
1769
1770 backend = hwloc_backend_alloc(topology, component);
1771 if (!backend)
1772 goto out;
1773
1774 data = malloc(sizeof(*data));
1775 if (!data) {
1776 errno = ENOMEM;
1777 goto out_with_backend;
1778 }
1779
1780 backend->private_data = data;
1781 backend->discover = hwloc_x86_discover;
1782 backend->disable = hwloc_x86_backend_disable;
1783
1784 /* default values */
1785 data->is_knl = 0;
1786 data->apicid_set = hwloc_bitmap_alloc();
1787 data->apicid_unique = 1;
1788 data->src_cpuiddump_path = NULL;
1789
1790 src_cpuiddump_path = getenv("HWLOC_CPUID_PATH");
1791 if (src_cpuiddump_path) {
1792 hwloc_bitmap_t set = hwloc_bitmap_alloc();
1793 if (!hwloc_x86_check_cpuiddump_input(src_cpuiddump_path, set)) {
1794 backend->is_thissystem = 0;
1795 data->src_cpuiddump_path = strdup(src_cpuiddump_path);
1796 assert(!hwloc_bitmap_iszero(set)); /* enforced by hwloc_x86_check_cpuiddump_input() */
1797 data->nbprocs = hwloc_bitmap_weight(set);
1798 } else {
1799 fprintf(stderr, "Ignoring dumped cpuid directory.\n");
1800 }
1801 hwloc_bitmap_free(set);
1802 }
1803
1804 return backend;
1805
1806 out_with_backend:
1807 free(backend);
1808 out:
1809 return NULL;
1810 }
1811
1812 static struct hwloc_disc_component hwloc_x86_disc_component = {
1813 "x86",
1814 HWLOC_DISC_PHASE_CPU,
1815 HWLOC_DISC_PHASE_GLOBAL,
1816 hwloc_x86_component_instantiate,
1817 45, /* between native and no_os */
1818 1,
1819 NULL
1820 };
1821
1822 const struct hwloc_component hwloc_x86_component = {
1823 HWLOC_COMPONENT_ABI,
1824 NULL, NULL,
1825 HWLOC_COMPONENT_TYPE_DISC,
1826 0,
1827 &hwloc_x86_disc_component
1828 };
1829