xref: /dragonfly/sys/kern/subr_cpu_topology.c (revision 62dc643e)
1 /*
2  * Copyright (c) 2012 The DragonFly Project.  All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  *
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in
12  *    the documentation and/or other materials provided with the
13  *    distribution.
14  * 3. Neither the name of The DragonFly Project nor the names of its
15  *    contributors may be used to endorse or promote products derived
16  *    from this software without specific, prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
21  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
22  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
23  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
24  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
26  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
28  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  *
31  */
32 
33 #include <sys/param.h>
34 #include <sys/systm.h>
35 #include <sys/kernel.h>
36 #include <sys/sysctl.h>
37 #include <sys/sbuf.h>
38 #include <sys/cpu_topology.h>
39 
40 #include <machine/smp.h>
41 
42 #ifndef NAPICID
43 #define NAPICID 256
44 #endif
45 
46 #define INDENT_BUF_SIZE LEVEL_NO*3
47 #define INVALID_ID -1
48 
49 /* Per-cpu sysctl nodes and info */
50 struct per_cpu_sysctl_info {
51 	struct sysctl_ctx_list sysctl_ctx;
52 	struct sysctl_oid *sysctl_tree;
53 	char cpu_name[32];
54 	int physical_id;
55 	int core_id;
56 	char physical_siblings[8*MAXCPU];
57 	char core_siblings[8*MAXCPU];
58 };
59 typedef struct per_cpu_sysctl_info per_cpu_sysctl_info_t;
60 
61 static cpu_node_t cpu_topology_nodes[MAXCPU];	/* Memory for topology */
62 static cpu_node_t *cpu_root_node;		/* Root node pointer */
63 
64 static struct sysctl_ctx_list cpu_topology_sysctl_ctx;
65 static struct sysctl_oid *cpu_topology_sysctl_tree;
66 static char cpu_topology_members[8*MAXCPU];
67 static per_cpu_sysctl_info_t *pcpu_sysctl;
68 static void sbuf_print_cpuset(struct sbuf *sb, cpumask_t *mask);
69 
70 int cpu_topology_levels_number = 1;
71 int cpu_topology_core_ids;
72 int cpu_topology_phys_ids;
73 cpu_node_t *root_cpu_node;
74 
75 MALLOC_DEFINE(M_PCPUSYS, "pcpusys", "pcpu sysctl topology");
76 
77 SYSCTL_INT(_hw, OID_AUTO, cpu_topology_core_ids, CTLFLAG_RW,
78 	   &cpu_topology_core_ids, 0, "# of real cores per package");
79 SYSCTL_INT(_hw, OID_AUTO, cpu_topology_phys_ids, CTLFLAG_RW,
80 	   &cpu_topology_phys_ids, 0, "# of physical packages");
81 
82 /* Get the next valid apicid starting
83  * from current apicid (curr_apicid
84  */
85 static int
86 get_next_valid_apicid(int curr_apicid)
87 {
88 	int next_apicid = curr_apicid;
89 	do {
90 		next_apicid++;
91 	}
92 	while(get_cpuid_from_apicid(next_apicid) == -1 &&
93 	   next_apicid < NAPICID);
94 	if (next_apicid == NAPICID) {
95 		kprintf("Warning: No next valid APICID found. Returning -1\n");
96 		return -1;
97 	}
98 	return next_apicid;
99 }
100 
101 /* Generic topology tree. The parameters have the following meaning:
102  * - children_no_per_level : the number of children on each level
103  * - level_types : the type of the level (THREAD, CORE, CHIP, etc)
104  * - cur_level : the current level of the tree
105  * - node : the current node
106  * - last_free_node : the last free node in the global array.
107  * - cpuid : basicly this are the ids of the leafs
108  */
109 static void
110 build_topology_tree(int *children_no_per_level,
111    uint8_t *level_types,
112    int cur_level,
113    cpu_node_t *node,
114    cpu_node_t **last_free_node,
115    int *apicid)
116 {
117 	int i;
118 
119 	node->child_no = children_no_per_level[cur_level];
120 	node->type = level_types[cur_level];
121 	CPUMASK_ASSZERO(node->members);
122 	node->compute_unit_id = -1;
123 
124 	if (node->child_no == 0) {
125 		*apicid = get_next_valid_apicid(*apicid);
126 		CPUMASK_ASSBIT(node->members, get_cpuid_from_apicid(*apicid));
127 		return;
128 	}
129 
130 	if (node->parent_node == NULL)
131 		root_cpu_node = node;
132 
133 	for (i = 0; i < node->child_no; i++) {
134 		node->child_node[i] = *last_free_node;
135 		(*last_free_node)++;
136 
137 		node->child_node[i]->parent_node = node;
138 
139 		build_topology_tree(children_no_per_level,
140 		    level_types,
141 		    cur_level + 1,
142 		    node->child_node[i],
143 		    last_free_node,
144 		    apicid);
145 
146 		CPUMASK_ORMASK(node->members, node->child_node[i]->members);
147 	}
148 }
149 
150 #if defined(__x86_64__) && !defined(_KERNEL_VIRTUAL)
151 static void
152 migrate_elements(cpu_node_t **a, int n, int pos)
153 {
154 	int i;
155 
156 	for (i = pos; i < n - 1 ; i++) {
157 		a[i] = a[i+1];
158 	}
159 	a[i] = NULL;
160 }
161 #endif
162 
163 /* Build CPU topology. The detection is made by comparing the
164  * chip, core and logical IDs of each CPU with the IDs of the
165  * BSP. When we found a match, at that level the CPUs are siblings.
166  */
167 static void
168 build_cpu_topology(int assumed_ncpus)
169 {
170 	int i;
171 	int BSPID = 0;
172 	int threads_per_core = 0;
173 	int cores_per_chip = 0;
174 	int chips_per_package = 0;
175 	int children_no_per_level[LEVEL_NO];
176 	uint8_t level_types[LEVEL_NO];
177 	int apicid = -1;
178 	cpu_node_t *root = &cpu_topology_nodes[0];
179 	cpu_node_t *last_free_node = root + 1;
180 
181 	detect_cpu_topology();
182 
183 	/*
184 	 * Assume that the topology is uniform.
185 	 * Find the number of siblings within chip
186 	 * and witin core to build up the topology.
187 	 */
188 	for (i = 0; i < assumed_ncpus; i++) {
189 		cpumask_t mask;
190 
191 		CPUMASK_ASSBIT(mask, i);
192 
193 #if 0
194 		/* smp_active_mask has not been initialized yet, ignore */
195 		if (CPUMASK_TESTMASK(mask, smp_active_mask) == 0)
196 			continue;
197 #endif
198 
199 		if (get_chip_ID(BSPID) == get_chip_ID(i))
200 			cores_per_chip++;
201 		else
202 			continue;
203 
204 		if (get_core_number_within_chip(BSPID) ==
205 		    get_core_number_within_chip(i))
206 			threads_per_core++;
207 	}
208 
209 	cores_per_chip /= threads_per_core;
210 	chips_per_package = assumed_ncpus / (cores_per_chip * threads_per_core);
211 
212 	if (bootverbose)
213 		kprintf("CPU Topology: cores_per_chip: %d; threads_per_core: %d; chips_per_package: %d;\n",
214 		    cores_per_chip, threads_per_core, chips_per_package);
215 
216 	if (threads_per_core > 1) { /* HT available - 4 levels */
217 
218 		children_no_per_level[0] = chips_per_package;
219 		children_no_per_level[1] = cores_per_chip;
220 		children_no_per_level[2] = threads_per_core;
221 		children_no_per_level[3] = 0;
222 
223 		level_types[0] = PACKAGE_LEVEL;
224 		level_types[1] = CHIP_LEVEL;
225 		level_types[2] = CORE_LEVEL;
226 		level_types[3] = THREAD_LEVEL;
227 
228 		build_topology_tree(children_no_per_level,
229 		    level_types,
230 		    0,
231 		    root,
232 		    &last_free_node,
233 		    &apicid);
234 
235 		cpu_topology_levels_number = 4;
236 
237 	} else if (cores_per_chip > 1) { /* No HT available - 3 levels */
238 
239 		children_no_per_level[0] = chips_per_package;
240 		children_no_per_level[1] = cores_per_chip;
241 		children_no_per_level[2] = 0;
242 
243 		level_types[0] = PACKAGE_LEVEL;
244 		level_types[1] = CHIP_LEVEL;
245 		level_types[2] = CORE_LEVEL;
246 
247 		build_topology_tree(children_no_per_level,
248 		    level_types,
249 		    0,
250 		    root,
251 		    &last_free_node,
252 		    &apicid);
253 
254 		cpu_topology_levels_number = 3;
255 
256 	} else { /* No HT and no Multi-Core - 2 levels */
257 
258 		children_no_per_level[0] = chips_per_package;
259 		children_no_per_level[1] = 0;
260 
261 		level_types[0] = PACKAGE_LEVEL;
262 		level_types[1] = CHIP_LEVEL;
263 
264 		build_topology_tree(children_no_per_level,
265 		    level_types,
266 		    0,
267 		    root,
268 		    &last_free_node,
269 		    &apicid);
270 
271 		cpu_topology_levels_number = 2;
272 
273 	}
274 
275 	cpu_root_node = root;
276 
277 
278 #if defined(__x86_64__) && !defined(_KERNEL_VIRTUAL)
279 	if (fix_amd_topology() == 0) {
280 		int visited[MAXCPU], i, j, pos, cpuid;
281 		cpu_node_t *leaf, *parent;
282 
283 		bzero(visited, MAXCPU * sizeof(int));
284 
285 		for (i = 0; i < assumed_ncpus; i++) {
286 			if (visited[i] == 0) {
287 				pos = 0;
288 				visited[i] = 1;
289 				leaf = get_cpu_node_by_cpuid(i);
290 
291 				if (leaf->type == CORE_LEVEL) {
292 					parent = leaf->parent_node;
293 
294 					last_free_node->child_node[0] = leaf;
295 					last_free_node->child_no = 1;
296 					last_free_node->members = leaf->members;
297 					last_free_node->compute_unit_id = leaf->compute_unit_id;
298 					last_free_node->parent_node = parent;
299 					last_free_node->type = CORE_LEVEL;
300 
301 
302 					for (j = 0; j < parent->child_no; j++) {
303 						if (parent->child_node[j] != leaf) {
304 
305 							cpuid = BSFCPUMASK(parent->child_node[j]->members);
306 							if (visited[cpuid] == 0 &&
307 							    parent->child_node[j]->compute_unit_id == leaf->compute_unit_id) {
308 
309 								last_free_node->child_node[last_free_node->child_no] = parent->child_node[j];
310 								last_free_node->child_no++;
311 								CPUMASK_ORMASK(last_free_node->members, parent->child_node[j]->members);
312 
313 								parent->child_node[j]->type = THREAD_LEVEL;
314 								parent->child_node[j]->parent_node = last_free_node;
315 								visited[cpuid] = 1;
316 
317 								migrate_elements(parent->child_node, parent->child_no, j);
318 								parent->child_no--;
319 								j--;
320 							}
321 						} else {
322 							pos = j;
323 						}
324 					}
325 					if (last_free_node->child_no > 1) {
326 						parent->child_node[pos] = last_free_node;
327 						leaf->type = THREAD_LEVEL;
328 						leaf->parent_node = last_free_node;
329 						last_free_node++;
330 					}
331 				}
332 			}
333 		}
334 	}
335 #endif
336 }
337 
338 /* Recursive function helper to print the CPU topology tree */
339 static void
340 print_cpu_topology_tree_sysctl_helper(cpu_node_t *node,
341     struct sbuf *sb,
342     char * buf,
343     int buf_len,
344     int last)
345 {
346 	int i;
347 	int bsr_member;
348 
349 	sbuf_bcat(sb, buf, buf_len);
350 	if (last) {
351 		sbuf_printf(sb, "\\-");
352 		buf[buf_len] = ' ';buf_len++;
353 		buf[buf_len] = ' ';buf_len++;
354 	} else {
355 		sbuf_printf(sb, "|-");
356 		buf[buf_len] = '|';buf_len++;
357 		buf[buf_len] = ' ';buf_len++;
358 	}
359 
360 	bsr_member = BSRCPUMASK(node->members);
361 
362 	if (node->type == PACKAGE_LEVEL) {
363 		sbuf_printf(sb,"PACKAGE MEMBERS: ");
364 	} else if (node->type == CHIP_LEVEL) {
365 		sbuf_printf(sb,"CHIP ID %d: ",
366 			get_chip_ID(bsr_member));
367 	} else if (node->type == CORE_LEVEL) {
368 		if (node->compute_unit_id != (uint8_t)-1) {
369 			sbuf_printf(sb,"Compute Unit ID %d: ",
370 				node->compute_unit_id);
371 		} else {
372 			sbuf_printf(sb,"CORE ID %d: ",
373 				get_core_number_within_chip(bsr_member));
374 		}
375 	} else if (node->type == THREAD_LEVEL) {
376 		if (node->compute_unit_id != (uint8_t)-1) {
377 			sbuf_printf(sb,"CORE ID %d: ",
378 				get_core_number_within_chip(bsr_member));
379 		} else {
380 			sbuf_printf(sb,"THREAD ID %d: ",
381 				get_logical_CPU_number_within_core(bsr_member));
382 		}
383 	} else {
384 		sbuf_printf(sb,"UNKNOWN: ");
385 	}
386 	sbuf_print_cpuset(sb, &node->members);
387 	sbuf_printf(sb,"\n");
388 
389 	for (i = 0; i < node->child_no; i++) {
390 		print_cpu_topology_tree_sysctl_helper(node->child_node[i],
391 		    sb, buf, buf_len, i == (node->child_no -1));
392 	}
393 }
394 
395 /* SYSCTL PROCEDURE for printing the CPU Topology tree */
396 static int
397 print_cpu_topology_tree_sysctl(SYSCTL_HANDLER_ARGS)
398 {
399 	struct sbuf *sb;
400 	int ret;
401 	char buf[INDENT_BUF_SIZE];
402 
403 	KASSERT(cpu_root_node != NULL, ("cpu_root_node isn't initialized"));
404 
405 	sb = sbuf_new(NULL, NULL, 500, SBUF_AUTOEXTEND);
406 	if (sb == NULL) {
407 		return (ENOMEM);
408 	}
409 	sbuf_printf(sb,"\n");
410 	print_cpu_topology_tree_sysctl_helper(cpu_root_node, sb, buf, 0, 1);
411 
412 	sbuf_finish(sb);
413 
414 	ret = SYSCTL_OUT(req, sbuf_data(sb), sbuf_len(sb));
415 
416 	sbuf_delete(sb);
417 
418 	return ret;
419 }
420 
421 /* SYSCTL PROCEDURE for printing the CPU Topology level description */
422 static int
423 print_cpu_topology_level_description_sysctl(SYSCTL_HANDLER_ARGS)
424 {
425 	struct sbuf *sb;
426 	int ret;
427 
428 	sb = sbuf_new(NULL, NULL, 500, SBUF_AUTOEXTEND);
429 	if (sb == NULL)
430 		return (ENOMEM);
431 
432 	if (cpu_topology_levels_number == 4) /* HT available */
433 		sbuf_printf(sb, "0 - thread; 1 - core; 2 - socket; 3 - anything");
434 	else if (cpu_topology_levels_number == 3) /* No HT available */
435 		sbuf_printf(sb, "0 - core; 1 - socket; 2 - anything");
436 	else if (cpu_topology_levels_number == 2) /* No HT and no Multi-Core */
437 		sbuf_printf(sb, "0 - socket; 1 - anything");
438 	else
439 		sbuf_printf(sb, "Unknown");
440 
441 	sbuf_finish(sb);
442 
443 	ret = SYSCTL_OUT(req, sbuf_data(sb), sbuf_len(sb));
444 
445 	sbuf_delete(sb);
446 
447 	return ret;
448 }
449 
450 /* Find a cpu_node_t by a mask */
451 static cpu_node_t *
452 get_cpu_node_by_cpumask(cpu_node_t * node,
453 			cpumask_t mask) {
454 
455 	cpu_node_t * found = NULL;
456 	int i;
457 
458 	if (CPUMASK_CMPMASKEQ(node->members, mask))
459 		return node;
460 
461 	for (i = 0; i < node->child_no; i++) {
462 		found = get_cpu_node_by_cpumask(node->child_node[i], mask);
463 		if (found != NULL) {
464 			return found;
465 		}
466 	}
467 	return NULL;
468 }
469 
470 cpu_node_t *
471 get_cpu_node_by_cpuid(int cpuid) {
472 	cpumask_t mask;
473 
474 	CPUMASK_ASSBIT(mask, cpuid);
475 
476 	KASSERT(cpu_root_node != NULL, ("cpu_root_node isn't initialized"));
477 
478 	return get_cpu_node_by_cpumask(cpu_root_node, mask);
479 }
480 
481 /* Get the mask of siblings for level_type of a cpuid */
482 cpumask_t
483 get_cpumask_from_level(int cpuid,
484 			uint8_t level_type)
485 {
486 	cpu_node_t * node;
487 	cpumask_t mask;
488 
489 	CPUMASK_ASSBIT(mask, cpuid);
490 
491 	KASSERT(cpu_root_node != NULL, ("cpu_root_node isn't initialized"));
492 
493 	node = get_cpu_node_by_cpumask(cpu_root_node, mask);
494 
495 	if (node == NULL) {
496 		CPUMASK_ASSZERO(mask);
497 		return mask;
498 	}
499 
500 	while (node != NULL) {
501 		if (node->type == level_type) {
502 			return node->members;
503 		}
504 		node = node->parent_node;
505 	}
506 	CPUMASK_ASSZERO(mask);
507 
508 	return mask;
509 }
510 
511 static const cpu_node_t *
512 get_cpu_node_by_chipid2(const cpu_node_t *node, int chip_id)
513 {
514 	int cpuid;
515 
516 	if (node->type != CHIP_LEVEL) {
517 		const cpu_node_t *ret = NULL;
518 		int i;
519 
520 		for (i = 0; i < node->child_no; ++i) {
521 			ret = get_cpu_node_by_chipid2(node->child_node[i],
522 			    chip_id);
523 			if (ret != NULL)
524 				break;
525 		}
526 		return ret;
527 	}
528 
529 	cpuid = BSRCPUMASK(node->members);
530 	if (get_chip_ID(cpuid) == chip_id)
531 		return node;
532 	return NULL;
533 }
534 
535 const cpu_node_t *
536 get_cpu_node_by_chipid(int chip_id)
537 {
538 	KASSERT(cpu_root_node != NULL, ("cpu_root_node isn't initialized"));
539 	return get_cpu_node_by_chipid2(cpu_root_node, chip_id);
540 }
541 
542 /* init pcpu_sysctl structure info */
543 static void
544 init_pcpu_topology_sysctl(int assumed_ncpus)
545 {
546 	struct sbuf sb;
547 	cpumask_t mask;
548 	int min_id = -1;
549 	int max_id = -1;
550 	int i;
551 	int phys_id;
552 
553 	pcpu_sysctl = kmalloc(sizeof(*pcpu_sysctl) * MAXCPU, M_PCPUSYS,
554 			      M_INTWAIT | M_ZERO);
555 
556 	for (i = 0; i < assumed_ncpus; i++) {
557 		sbuf_new(&sb, pcpu_sysctl[i].cpu_name,
558 		    sizeof(pcpu_sysctl[i].cpu_name), SBUF_FIXEDLEN);
559 		sbuf_printf(&sb,"cpu%d", i);
560 		sbuf_finish(&sb);
561 
562 
563 		/* Get physical siblings */
564 		mask = get_cpumask_from_level(i, CHIP_LEVEL);
565 		if (CPUMASK_TESTZERO(mask)) {
566 			pcpu_sysctl[i].physical_id = INVALID_ID;
567 			continue;
568 		}
569 
570 		sbuf_new(&sb, pcpu_sysctl[i].physical_siblings,
571 		    sizeof(pcpu_sysctl[i].physical_siblings), SBUF_FIXEDLEN);
572 		sbuf_print_cpuset(&sb, &mask);
573 		sbuf_trim(&sb);
574 		sbuf_finish(&sb);
575 
576 		phys_id = get_chip_ID(i);
577 		pcpu_sysctl[i].physical_id = phys_id;
578 		if (min_id < 0 || min_id > phys_id)
579 			min_id = phys_id;
580 		if (max_id < 0 || max_id < phys_id)
581 			max_id = phys_id;
582 
583 		/* Get core siblings */
584 		mask = get_cpumask_from_level(i, CORE_LEVEL);
585 		if (CPUMASK_TESTZERO(mask)) {
586 			pcpu_sysctl[i].core_id = INVALID_ID;
587 			continue;
588 		}
589 
590 		sbuf_new(&sb, pcpu_sysctl[i].core_siblings,
591 		    sizeof(pcpu_sysctl[i].core_siblings), SBUF_FIXEDLEN);
592 		sbuf_print_cpuset(&sb, &mask);
593 		sbuf_trim(&sb);
594 		sbuf_finish(&sb);
595 
596 		pcpu_sysctl[i].core_id = get_core_number_within_chip(i);
597 		if (cpu_topology_core_ids < pcpu_sysctl[i].core_id)
598 			cpu_topology_core_ids = pcpu_sysctl[i].core_id + 1;
599 
600 	}
601 
602 	/*
603 	 * Normalize physical ids so they can be used by the VM system.
604 	 * Some systems number starting at 0 others number starting at 1.
605 	 */
606 	cpu_topology_phys_ids = max_id - min_id + 1;
607 	if (cpu_topology_phys_ids <= 0)		/* don't crash */
608 		cpu_topology_phys_ids = 1;
609 	for (i = 0; i < assumed_ncpus; i++) {
610 		pcpu_sysctl[i].physical_id %= cpu_topology_phys_ids;
611 	}
612 }
613 
614 /* Build SYSCTL structure for revealing
615  * the CPU Topology to user-space.
616  */
617 static void
618 build_sysctl_cpu_topology(int assumed_ncpus)
619 {
620 	int i;
621 	struct sbuf sb;
622 
623 	/* SYSCTL new leaf for "cpu_topology" */
624 	sysctl_ctx_init(&cpu_topology_sysctl_ctx);
625 	cpu_topology_sysctl_tree = SYSCTL_ADD_NODE(&cpu_topology_sysctl_ctx,
626 	    SYSCTL_STATIC_CHILDREN(_hw),
627 	    OID_AUTO,
628 	    "cpu_topology",
629 	    CTLFLAG_RD, 0, "");
630 
631 	/* SYSCTL cpu_topology "tree" entry */
632 	SYSCTL_ADD_PROC(&cpu_topology_sysctl_ctx,
633 	    SYSCTL_CHILDREN(cpu_topology_sysctl_tree),
634 	    OID_AUTO, "tree", CTLTYPE_STRING | CTLFLAG_RD,
635 	    NULL, 0, print_cpu_topology_tree_sysctl, "A",
636 	    "Tree print of CPU topology");
637 
638 	/* SYSCTL cpu_topology "level_description" entry */
639 	SYSCTL_ADD_PROC(&cpu_topology_sysctl_ctx,
640 	    SYSCTL_CHILDREN(cpu_topology_sysctl_tree),
641 	    OID_AUTO, "level_description", CTLTYPE_STRING | CTLFLAG_RD,
642 	    NULL, 0, print_cpu_topology_level_description_sysctl, "A",
643 	    "Level description of CPU topology");
644 
645 	/* SYSCTL cpu_topology "members" entry */
646 	sbuf_new(&sb, cpu_topology_members,
647 	    sizeof(cpu_topology_members), SBUF_FIXEDLEN);
648 	sbuf_print_cpuset(&sb, &cpu_root_node->members);
649 	sbuf_trim(&sb);
650 	sbuf_finish(&sb);
651 	SYSCTL_ADD_STRING(&cpu_topology_sysctl_ctx,
652 	    SYSCTL_CHILDREN(cpu_topology_sysctl_tree),
653 	    OID_AUTO, "members", CTLFLAG_RD,
654 	    cpu_topology_members, 0,
655 	    "Members of the CPU Topology");
656 
657 	/* SYSCTL per_cpu info */
658 	for (i = 0; i < assumed_ncpus; i++) {
659 		/* New leaf : hw.cpu_topology.cpux */
660 		sysctl_ctx_init(&pcpu_sysctl[i].sysctl_ctx);
661 		pcpu_sysctl[i].sysctl_tree = SYSCTL_ADD_NODE(&pcpu_sysctl[i].sysctl_ctx,
662 		    SYSCTL_CHILDREN(cpu_topology_sysctl_tree),
663 		    OID_AUTO,
664 		    pcpu_sysctl[i].cpu_name,
665 		    CTLFLAG_RD, 0, "");
666 
667 		/* Check if the physical_id found is valid */
668 		if (pcpu_sysctl[i].physical_id == INVALID_ID) {
669 			continue;
670 		}
671 
672 		/* Add physical id info */
673 		SYSCTL_ADD_INT(&pcpu_sysctl[i].sysctl_ctx,
674 		    SYSCTL_CHILDREN(pcpu_sysctl[i].sysctl_tree),
675 		    OID_AUTO, "physical_id", CTLFLAG_RD,
676 		    &pcpu_sysctl[i].physical_id, 0,
677 		    "Physical ID");
678 
679 		/* Add physical siblings */
680 		SYSCTL_ADD_STRING(&pcpu_sysctl[i].sysctl_ctx,
681 		    SYSCTL_CHILDREN(pcpu_sysctl[i].sysctl_tree),
682 		    OID_AUTO, "physical_siblings", CTLFLAG_RD,
683 		    pcpu_sysctl[i].physical_siblings, 0,
684 		    "Physical siblings");
685 
686 		/* Check if the core_id found is valid */
687 		if (pcpu_sysctl[i].core_id == INVALID_ID) {
688 			continue;
689 		}
690 
691 		/* Add core id info */
692 		SYSCTL_ADD_INT(&pcpu_sysctl[i].sysctl_ctx,
693 		    SYSCTL_CHILDREN(pcpu_sysctl[i].sysctl_tree),
694 		    OID_AUTO, "core_id", CTLFLAG_RD,
695 		    &pcpu_sysctl[i].core_id, 0,
696 		    "Core ID");
697 
698 		/*Add core siblings */
699 		SYSCTL_ADD_STRING(&pcpu_sysctl[i].sysctl_ctx,
700 		    SYSCTL_CHILDREN(pcpu_sysctl[i].sysctl_tree),
701 		    OID_AUTO, "core_siblings", CTLFLAG_RD,
702 		    pcpu_sysctl[i].core_siblings, 0,
703 		    "Core siblings");
704 	}
705 }
706 
707 static
708 void
709 sbuf_print_cpuset(struct sbuf *sb, cpumask_t *mask)
710 {
711 	int i;
712 	int b = -1;
713 	int e = -1;
714 	int more = 0;
715 
716 	sbuf_printf(sb, "cpus(");
717 	CPUSET_FOREACH(i, *mask) {
718 		if (b < 0) {
719 			b = i;
720 			e = b + 1;
721 			continue;
722 		}
723 		if (e == i) {
724 			++e;
725 			continue;
726 		}
727 		if (more)
728 			sbuf_printf(sb, ", ");
729 		if (b == e - 1) {
730 			sbuf_printf(sb, "%d", b);
731 		} else {
732 			sbuf_printf(sb, "%d-%d", b, e - 1);
733 		}
734 		more = 1;
735 		b = i;
736 		e = b + 1;
737 	}
738 	if (more)
739 		sbuf_printf(sb, ", ");
740 	if (b >= 0) {
741 		if (b == e - 1) {
742 			sbuf_printf(sb, "%d", b);
743 		} else {
744 			sbuf_printf(sb, "%d-%d", b, e - 1);
745 		}
746 	}
747 	sbuf_printf(sb, ") ");
748 }
749 
750 int
751 get_cpu_core_id(int cpuid)
752 {
753 	if (pcpu_sysctl)
754 		return(pcpu_sysctl[cpuid].core_id);
755 	return(0);
756 }
757 
758 int
759 get_cpu_phys_id(int cpuid)
760 {
761 	if (pcpu_sysctl)
762 		return(pcpu_sysctl[cpuid].physical_id);
763 	return(0);
764 }
765 
766 extern int naps;
767 
768 /* Build the CPU Topology and SYSCTL Topology tree */
769 static void
770 init_cpu_topology(void)
771 {
772 	int assumed_ncpus;
773 
774 	assumed_ncpus = naps + 1;
775 
776 	build_cpu_topology(assumed_ncpus);
777 	init_pcpu_topology_sysctl(assumed_ncpus);
778 	build_sysctl_cpu_topology(assumed_ncpus);
779 }
780 SYSINIT(cpu_topology, SI_BOOT2_CPU_TOPOLOGY, SI_ORDER_FIRST,
781     init_cpu_topology, NULL);
782