xref: /dflybsd-src/sys/kern/subr_cpu_topology.c (revision 53a91b8ec44c30ce6024f2460eaa892bd21e15f1)
1f77c018aSMihai Carabas /*
2f77c018aSMihai Carabas  * Copyright (c) 2012 The DragonFly Project.  All rights reserved.
3f77c018aSMihai Carabas  *
4f77c018aSMihai Carabas  * Redistribution and use in source and binary forms, with or without
5f77c018aSMihai Carabas  * modification, are permitted provided that the following conditions
6f77c018aSMihai Carabas  * are met:
7f77c018aSMihai Carabas  *
8f77c018aSMihai Carabas  * 1. Redistributions of source code must retain the above copyright
9f77c018aSMihai Carabas  *    notice, this list of conditions and the following disclaimer.
10f77c018aSMihai Carabas  * 2. Redistributions in binary form must reproduce the above copyright
11f77c018aSMihai Carabas  *    notice, this list of conditions and the following disclaimer in
12f77c018aSMihai Carabas  *    the documentation and/or other materials provided with the
13f77c018aSMihai Carabas  *    distribution.
14f77c018aSMihai Carabas  * 3. Neither the name of The DragonFly Project nor the names of its
15f77c018aSMihai Carabas  *    contributors may be used to endorse or promote products derived
16f77c018aSMihai Carabas  *    from this software without specific, prior written permission.
17f77c018aSMihai Carabas  *
18f77c018aSMihai Carabas  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19f77c018aSMihai Carabas  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20f77c018aSMihai Carabas  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
21f77c018aSMihai Carabas  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
22f77c018aSMihai Carabas  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
23f77c018aSMihai Carabas  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
24f77c018aSMihai Carabas  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25f77c018aSMihai Carabas  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
26f77c018aSMihai Carabas  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27f77c018aSMihai Carabas  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
28f77c018aSMihai Carabas  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29f77c018aSMihai Carabas  * SUCH DAMAGE.
30f77c018aSMihai Carabas  */
31f77c018aSMihai Carabas 
32f77c018aSMihai Carabas #include <sys/param.h>
33f77c018aSMihai Carabas #include <sys/systm.h>
34f77c018aSMihai Carabas #include <sys/kernel.h>
35e2164e29Szrj #include <sys/malloc.h>
36f77c018aSMihai Carabas #include <sys/sysctl.h>
37f77c018aSMihai Carabas #include <sys/sbuf.h>
38f77c018aSMihai Carabas #include <sys/cpu_topology.h>
39f77c018aSMihai Carabas 
40f77c018aSMihai Carabas #include <machine/smp.h>
41f77c018aSMihai Carabas 
42f77c018aSMihai Carabas #ifndef NAPICID
43f77c018aSMihai Carabas #define NAPICID 256
44f77c018aSMihai Carabas #endif
45f77c018aSMihai Carabas 
46f77c018aSMihai Carabas #define INDENT_BUF_SIZE LEVEL_NO*3
47f77c018aSMihai Carabas #define INVALID_ID -1
48f77c018aSMihai Carabas 
49f77c018aSMihai Carabas /* Per-cpu sysctl nodes and info */
50f77c018aSMihai Carabas struct per_cpu_sysctl_info {
51f77c018aSMihai Carabas 	struct sysctl_ctx_list sysctl_ctx;
52f77c018aSMihai Carabas 	struct sysctl_oid *sysctl_tree;
53f77c018aSMihai Carabas 	char cpu_name[32];
54f77c018aSMihai Carabas 	int physical_id;
55f77c018aSMihai Carabas 	int core_id;
568e5d7c42SMatthew Dillon 	int ht_id;				/* thread id within core */
57f77c018aSMihai Carabas 	char physical_siblings[8*MAXCPU];
58f77c018aSMihai Carabas 	char core_siblings[8*MAXCPU];
59f77c018aSMihai Carabas };
60f77c018aSMihai Carabas typedef struct per_cpu_sysctl_info per_cpu_sysctl_info_t;
61f77c018aSMihai Carabas 
62*53a91b8eSMatthew Dillon /* Memory for topology */
63*53a91b8eSMatthew Dillon __read_frequently static cpu_node_t cpu_topology_nodes[MAXCPU];
64*53a91b8eSMatthew Dillon /* Root node pointer */
65*53a91b8eSMatthew Dillon __read_frequently static cpu_node_t *cpu_root_node;
66f77c018aSMihai Carabas 
67f77c018aSMihai Carabas static struct sysctl_ctx_list cpu_topology_sysctl_ctx;
68f77c018aSMihai Carabas static struct sysctl_oid *cpu_topology_sysctl_tree;
69f77c018aSMihai Carabas static char cpu_topology_members[8*MAXCPU];
70d8f4ebf4SCharlie Root static per_cpu_sysctl_info_t *pcpu_sysctl;
71399efd7fSMatthew Dillon static void sbuf_print_cpuset(struct sbuf *sb, cpumask_t *mask);
72f77c018aSMihai Carabas 
73*53a91b8eSMatthew Dillon __read_frequently int cpu_topology_levels_number = 1;
74*53a91b8eSMatthew Dillon __read_frequently int cpu_topology_ht_ids;
75*53a91b8eSMatthew Dillon __read_frequently int cpu_topology_core_ids;
76*53a91b8eSMatthew Dillon __read_frequently int cpu_topology_phys_ids;
77*53a91b8eSMatthew Dillon __read_frequently cpu_node_t *root_cpu_node;
78f77c018aSMihai Carabas 
79d8f4ebf4SCharlie Root MALLOC_DEFINE(M_PCPUSYS, "pcpusys", "pcpu sysctl topology");
80d8f4ebf4SCharlie Root 
818e5d7c42SMatthew Dillon SYSCTL_INT(_hw, OID_AUTO, cpu_topology_ht_ids, CTLFLAG_RW,
828e5d7c42SMatthew Dillon 	   &cpu_topology_ht_ids, 0, "# of logical cores per real core");
8333ee48c4SMatthew Dillon SYSCTL_INT(_hw, OID_AUTO, cpu_topology_core_ids, CTLFLAG_RW,
8433ee48c4SMatthew Dillon 	   &cpu_topology_core_ids, 0, "# of real cores per package");
8533ee48c4SMatthew Dillon SYSCTL_INT(_hw, OID_AUTO, cpu_topology_phys_ids, CTLFLAG_RW,
8633ee48c4SMatthew Dillon 	   &cpu_topology_phys_ids, 0, "# of physical packages");
87d8f4ebf4SCharlie Root 
88f77c018aSMihai Carabas /* Get the next valid apicid starting
89f77c018aSMihai Carabas  * from current apicid (curr_apicid
90f77c018aSMihai Carabas  */
91f77c018aSMihai Carabas static int
get_next_valid_apicid(int curr_apicid)92f77c018aSMihai Carabas get_next_valid_apicid(int curr_apicid)
93f77c018aSMihai Carabas {
94f77c018aSMihai Carabas 	int next_apicid = curr_apicid;
95f77c018aSMihai Carabas 	do {
96f77c018aSMihai Carabas 		next_apicid++;
97f77c018aSMihai Carabas 	}
98f77c018aSMihai Carabas 	while(get_cpuid_from_apicid(next_apicid) == -1 &&
99f77c018aSMihai Carabas 	   next_apicid < NAPICID);
100f77c018aSMihai Carabas 	if (next_apicid == NAPICID) {
101f77c018aSMihai Carabas 		kprintf("Warning: No next valid APICID found. Returning -1\n");
102f77c018aSMihai Carabas 		return -1;
103f77c018aSMihai Carabas 	}
104f77c018aSMihai Carabas 	return next_apicid;
105f77c018aSMihai Carabas }
106f77c018aSMihai Carabas 
107f77c018aSMihai Carabas /* Generic topology tree. The parameters have the following meaning:
108f77c018aSMihai Carabas  * - children_no_per_level : the number of children on each level
109f77c018aSMihai Carabas  * - level_types : the type of the level (THREAD, CORE, CHIP, etc)
110f77c018aSMihai Carabas  * - cur_level : the current level of the tree
111f77c018aSMihai Carabas  * - node : the current node
112f77c018aSMihai Carabas  * - last_free_node : the last free node in the global array.
113f77c018aSMihai Carabas  * - cpuid : basicly this are the ids of the leafs
114f77c018aSMihai Carabas  */
115f77c018aSMihai Carabas static void
build_topology_tree(int * children_no_per_level,uint8_t * level_types,int cur_level,cpu_node_t * node,cpu_node_t ** last_free_node,int * apicid)116f77c018aSMihai Carabas build_topology_tree(int *children_no_per_level,
117f77c018aSMihai Carabas    uint8_t *level_types,
118f77c018aSMihai Carabas    int cur_level,
119f77c018aSMihai Carabas    cpu_node_t *node,
120f77c018aSMihai Carabas    cpu_node_t **last_free_node,
121f77c018aSMihai Carabas    int *apicid)
122f77c018aSMihai Carabas {
123f77c018aSMihai Carabas 	int i;
124f77c018aSMihai Carabas 
125f77c018aSMihai Carabas 	node->child_no = children_no_per_level[cur_level];
126f77c018aSMihai Carabas 	node->type = level_types[cur_level];
127c07315c4SMatthew Dillon 	CPUMASK_ASSZERO(node->members);
1280e9325d3SMihai Carabas 	node->compute_unit_id = -1;
129f77c018aSMihai Carabas 
130f77c018aSMihai Carabas 	if (node->child_no == 0) {
131f77c018aSMihai Carabas 		*apicid = get_next_valid_apicid(*apicid);
132c07315c4SMatthew Dillon 		CPUMASK_ASSBIT(node->members, get_cpuid_from_apicid(*apicid));
133f77c018aSMihai Carabas 		return;
134f77c018aSMihai Carabas 	}
135f77c018aSMihai Carabas 
136e28d8b15SMatthew Dillon 	if (node->parent_node == NULL)
137e28d8b15SMatthew Dillon 		root_cpu_node = node;
138f77c018aSMihai Carabas 
139f77c018aSMihai Carabas 	for (i = 0; i < node->child_no; i++) {
1400e9325d3SMihai Carabas 		node->child_node[i] = *last_free_node;
1410e9325d3SMihai Carabas 		(*last_free_node)++;
1420e9325d3SMihai Carabas 
1430e9325d3SMihai Carabas 		node->child_node[i]->parent_node = node;
144f77c018aSMihai Carabas 
145f77c018aSMihai Carabas 		build_topology_tree(children_no_per_level,
146f77c018aSMihai Carabas 		    level_types,
147f77c018aSMihai Carabas 		    cur_level + 1,
1480e9325d3SMihai Carabas 		    node->child_node[i],
149f77c018aSMihai Carabas 		    last_free_node,
150f77c018aSMihai Carabas 		    apicid);
151f77c018aSMihai Carabas 
152c07315c4SMatthew Dillon 		CPUMASK_ORMASK(node->members, node->child_node[i]->members);
153f77c018aSMihai Carabas 	}
154f77c018aSMihai Carabas }
155f77c018aSMihai Carabas 
156493a3e86SSascha Wildner #if defined(__x86_64__) && !defined(_KERNEL_VIRTUAL)
157493a3e86SSascha Wildner static void
migrate_elements(cpu_node_t ** a,int n,int pos)158493a3e86SSascha Wildner migrate_elements(cpu_node_t **a, int n, int pos)
159493a3e86SSascha Wildner {
1600e9325d3SMihai Carabas 	int i;
1610e9325d3SMihai Carabas 
1620e9325d3SMihai Carabas 	for (i = pos; i < n - 1 ; i++) {
1630e9325d3SMihai Carabas 		a[i] = a[i+1];
1640e9325d3SMihai Carabas 	}
1650e9325d3SMihai Carabas 	a[i] = NULL;
1660e9325d3SMihai Carabas }
167493a3e86SSascha Wildner #endif
1680e9325d3SMihai Carabas 
169f77c018aSMihai Carabas /* Build CPU topology. The detection is made by comparing the
170f77c018aSMihai Carabas  * chip, core and logical IDs of each CPU with the IDs of the
171f77c018aSMihai Carabas  * BSP. When we found a match, at that level the CPUs are siblings.
172f77c018aSMihai Carabas  */
1730e9325d3SMihai Carabas static void
build_cpu_topology(int assumed_ncpus)174c7f9edd8SMatthew Dillon build_cpu_topology(int assumed_ncpus)
175f77c018aSMihai Carabas {
176f77c018aSMihai Carabas 	int i;
177f77c018aSMihai Carabas 	int BSPID = 0;
178f77c018aSMihai Carabas 	int threads_per_core = 0;
179f77c018aSMihai Carabas 	int cores_per_chip = 0;
180f77c018aSMihai Carabas 	int chips_per_package = 0;
181f77c018aSMihai Carabas 	int children_no_per_level[LEVEL_NO];
182f77c018aSMihai Carabas 	uint8_t level_types[LEVEL_NO];
183f77c018aSMihai Carabas 	int apicid = -1;
184f77c018aSMihai Carabas 	cpu_node_t *root = &cpu_topology_nodes[0];
185f77c018aSMihai Carabas 	cpu_node_t *last_free_node = root + 1;
186f77c018aSMihai Carabas 
1873a3b0c3aSMatthew Dillon 	detect_cpu_topology();
1883a3b0c3aSMatthew Dillon 
1896f2099feSMatthew Dillon 	/*
1906f2099feSMatthew Dillon 	 * Assume that the topology is uniform.
1919cd8f4f8SMatthew Dillon 	 * Find the number of siblings within the chip
1929cd8f4f8SMatthew Dillon 	 * and within the core to build up the topology.
193f77c018aSMihai Carabas 	 */
194c7f9edd8SMatthew Dillon 	for (i = 0; i < assumed_ncpus; i++) {
195c07315c4SMatthew Dillon 		cpumask_t mask;
196f77c018aSMihai Carabas 
197c07315c4SMatthew Dillon 		CPUMASK_ASSBIT(mask, i);
198f77c018aSMihai Carabas 
1996f2099feSMatthew Dillon #if 0
2006f2099feSMatthew Dillon 		/* smp_active_mask has not been initialized yet, ignore */
201c07315c4SMatthew Dillon 		if (CPUMASK_TESTMASK(mask, smp_active_mask) == 0)
202f77c018aSMihai Carabas 			continue;
2036f2099feSMatthew Dillon #endif
204f77c018aSMihai Carabas 
205c70d4562SMatthew Dillon 		if (get_chip_ID(BSPID) != get_chip_ID(i))
206f77c018aSMihai Carabas 			continue;
207c70d4562SMatthew Dillon 		++cores_per_chip;
208f77c018aSMihai Carabas 
209f77c018aSMihai Carabas 		if (get_core_number_within_chip(BSPID) ==
210c70d4562SMatthew Dillon 		    get_core_number_within_chip(i)) {
211c70d4562SMatthew Dillon 			++threads_per_core;
212c70d4562SMatthew Dillon 		}
213f77c018aSMihai Carabas 	}
214f77c018aSMihai Carabas 
215f77c018aSMihai Carabas 	cores_per_chip /= threads_per_core;
216c7f9edd8SMatthew Dillon 	chips_per_package = assumed_ncpus / (cores_per_chip * threads_per_core);
217f77c018aSMihai Carabas 
218c70d4562SMatthew Dillon 	kprintf("CPU Topology: cores_per_chip: %d; threads_per_core: %d; "
219c70d4562SMatthew Dillon 		"chips_per_package: %d;\n",
220f77c018aSMihai Carabas 		cores_per_chip, threads_per_core, chips_per_package);
221f77c018aSMihai Carabas 
222f77c018aSMihai Carabas 	if (threads_per_core > 1) { /* HT available - 4 levels */
223f77c018aSMihai Carabas 
224f77c018aSMihai Carabas 		children_no_per_level[0] = chips_per_package;
225f77c018aSMihai Carabas 		children_no_per_level[1] = cores_per_chip;
226f77c018aSMihai Carabas 		children_no_per_level[2] = threads_per_core;
227f77c018aSMihai Carabas 		children_no_per_level[3] = 0;
228f77c018aSMihai Carabas 
229f77c018aSMihai Carabas 		level_types[0] = PACKAGE_LEVEL;
230f77c018aSMihai Carabas 		level_types[1] = CHIP_LEVEL;
231f77c018aSMihai Carabas 		level_types[2] = CORE_LEVEL;
232f77c018aSMihai Carabas 		level_types[3] = THREAD_LEVEL;
233f77c018aSMihai Carabas 
234f77c018aSMihai Carabas 		build_topology_tree(children_no_per_level,
235f77c018aSMihai Carabas 		    level_types,
236f77c018aSMihai Carabas 		    0,
237f77c018aSMihai Carabas 		    root,
238f77c018aSMihai Carabas 		    &last_free_node,
239f77c018aSMihai Carabas 		    &apicid);
240f77c018aSMihai Carabas 
241f77c018aSMihai Carabas 		cpu_topology_levels_number = 4;
242f77c018aSMihai Carabas 
243f77c018aSMihai Carabas 	} else if (cores_per_chip > 1) { /* No HT available - 3 levels */
244f77c018aSMihai Carabas 
245f77c018aSMihai Carabas 		children_no_per_level[0] = chips_per_package;
246f77c018aSMihai Carabas 		children_no_per_level[1] = cores_per_chip;
247f77c018aSMihai Carabas 		children_no_per_level[2] = 0;
248f77c018aSMihai Carabas 
249f77c018aSMihai Carabas 		level_types[0] = PACKAGE_LEVEL;
250f77c018aSMihai Carabas 		level_types[1] = CHIP_LEVEL;
251f77c018aSMihai Carabas 		level_types[2] = CORE_LEVEL;
252f77c018aSMihai Carabas 
253f77c018aSMihai Carabas 		build_topology_tree(children_no_per_level,
254f77c018aSMihai Carabas 		    level_types,
255f77c018aSMihai Carabas 		    0,
256f77c018aSMihai Carabas 		    root,
257f77c018aSMihai Carabas 		    &last_free_node,
258f77c018aSMihai Carabas 		    &apicid);
259f77c018aSMihai Carabas 
260f77c018aSMihai Carabas 		cpu_topology_levels_number = 3;
261f77c018aSMihai Carabas 
262f77c018aSMihai Carabas 	} else { /* No HT and no Multi-Core - 2 levels */
263f77c018aSMihai Carabas 
264f77c018aSMihai Carabas 		children_no_per_level[0] = chips_per_package;
265f77c018aSMihai Carabas 		children_no_per_level[1] = 0;
266f77c018aSMihai Carabas 
267f77c018aSMihai Carabas 		level_types[0] = PACKAGE_LEVEL;
268f77c018aSMihai Carabas 		level_types[1] = CHIP_LEVEL;
269f77c018aSMihai Carabas 
270f77c018aSMihai Carabas 		build_topology_tree(children_no_per_level,
271f77c018aSMihai Carabas 		    level_types,
272f77c018aSMihai Carabas 		    0,
273f77c018aSMihai Carabas 		    root,
274f77c018aSMihai Carabas 		    &last_free_node,
275f77c018aSMihai Carabas 		    &apicid);
276f77c018aSMihai Carabas 
277f77c018aSMihai Carabas 		cpu_topology_levels_number = 2;
278f77c018aSMihai Carabas 
279f77c018aSMihai Carabas 	}
280f77c018aSMihai Carabas 
2810e9325d3SMihai Carabas 	cpu_root_node = root;
2820e9325d3SMihai Carabas 
2830e9325d3SMihai Carabas 
284493a3e86SSascha Wildner #if defined(__x86_64__) && !defined(_KERNEL_VIRTUAL)
2850e9325d3SMihai Carabas 	if (fix_amd_topology() == 0) {
2860e9325d3SMihai Carabas 		int visited[MAXCPU], i, j, pos, cpuid;
2870e9325d3SMihai Carabas 		cpu_node_t *leaf, *parent;
2880e9325d3SMihai Carabas 
2890e9325d3SMihai Carabas 		bzero(visited, MAXCPU * sizeof(int));
2900e9325d3SMihai Carabas 
291c7f9edd8SMatthew Dillon 		for (i = 0; i < assumed_ncpus; i++) {
2920e9325d3SMihai Carabas 			if (visited[i] == 0) {
2930e9325d3SMihai Carabas 				pos = 0;
2940e9325d3SMihai Carabas 				visited[i] = 1;
2950e9325d3SMihai Carabas 				leaf = get_cpu_node_by_cpuid(i);
2960e9325d3SMihai Carabas 
2979cd8f4f8SMatthew Dillon 				KASSERT(leaf != NULL, ("cpu %d NULL node", i));
2980e9325d3SMihai Carabas 				if (leaf->type == CORE_LEVEL) {
2990e9325d3SMihai Carabas 					parent = leaf->parent_node;
3000e9325d3SMihai Carabas 
3010e9325d3SMihai Carabas 					last_free_node->child_node[0] = leaf;
3020e9325d3SMihai Carabas 					last_free_node->child_no = 1;
3030e9325d3SMihai Carabas 					last_free_node->members = leaf->members;
3040e9325d3SMihai Carabas 					last_free_node->compute_unit_id = leaf->compute_unit_id;
3050e9325d3SMihai Carabas 					last_free_node->parent_node = parent;
3060e9325d3SMihai Carabas 					last_free_node->type = CORE_LEVEL;
3070e9325d3SMihai Carabas 
3080e9325d3SMihai Carabas 
3090e9325d3SMihai Carabas 					for (j = 0; j < parent->child_no; j++) {
3100e9325d3SMihai Carabas 						if (parent->child_node[j] != leaf) {
3110e9325d3SMihai Carabas 
3120e9325d3SMihai Carabas 							cpuid = BSFCPUMASK(parent->child_node[j]->members);
3130e9325d3SMihai Carabas 							if (visited[cpuid] == 0 &&
3140e9325d3SMihai Carabas 							    parent->child_node[j]->compute_unit_id == leaf->compute_unit_id) {
3150e9325d3SMihai Carabas 
3160e9325d3SMihai Carabas 								last_free_node->child_node[last_free_node->child_no] = parent->child_node[j];
3170e9325d3SMihai Carabas 								last_free_node->child_no++;
318c07315c4SMatthew Dillon 								CPUMASK_ORMASK(last_free_node->members, parent->child_node[j]->members);
3190e9325d3SMihai Carabas 
3200e9325d3SMihai Carabas 								parent->child_node[j]->type = THREAD_LEVEL;
3210e9325d3SMihai Carabas 								parent->child_node[j]->parent_node = last_free_node;
3220e9325d3SMihai Carabas 								visited[cpuid] = 1;
3230e9325d3SMihai Carabas 
3240e9325d3SMihai Carabas 								migrate_elements(parent->child_node, parent->child_no, j);
3250e9325d3SMihai Carabas 								parent->child_no--;
3260e9325d3SMihai Carabas 								j--;
3270e9325d3SMihai Carabas 							}
3280e9325d3SMihai Carabas 						} else {
3290e9325d3SMihai Carabas 							pos = j;
3300e9325d3SMihai Carabas 						}
3310e9325d3SMihai Carabas 					}
3320e9325d3SMihai Carabas 					if (last_free_node->child_no > 1) {
3330e9325d3SMihai Carabas 						parent->child_node[pos] = last_free_node;
3340e9325d3SMihai Carabas 						leaf->type = THREAD_LEVEL;
3350e9325d3SMihai Carabas 						leaf->parent_node = last_free_node;
3360e9325d3SMihai Carabas 						last_free_node++;
3370e9325d3SMihai Carabas 					}
3380e9325d3SMihai Carabas 				}
3390e9325d3SMihai Carabas 			}
3400e9325d3SMihai Carabas 		}
3410e9325d3SMihai Carabas 	}
3420e9325d3SMihai Carabas #endif
343f77c018aSMihai Carabas }
344f77c018aSMihai Carabas 
345f77c018aSMihai Carabas /* Recursive function helper to print the CPU topology tree */
346f77c018aSMihai Carabas static void
print_cpu_topology_tree_sysctl_helper(cpu_node_t * node,struct sbuf * sb,char * buf,int buf_len,int last)347f77c018aSMihai Carabas print_cpu_topology_tree_sysctl_helper(cpu_node_t *node,
348f77c018aSMihai Carabas     struct sbuf *sb,
349f77c018aSMihai Carabas     char * buf,
350f77c018aSMihai Carabas     int buf_len,
351f77c018aSMihai Carabas     int last)
352f77c018aSMihai Carabas {
353f77c018aSMihai Carabas 	int i;
354f77c018aSMihai Carabas 	int bsr_member;
355f77c018aSMihai Carabas 
356f77c018aSMihai Carabas 	sbuf_bcat(sb, buf, buf_len);
357f77c018aSMihai Carabas 	if (last) {
358f77c018aSMihai Carabas 		sbuf_printf(sb, "\\-");
359f77c018aSMihai Carabas 		buf[buf_len] = ' ';buf_len++;
360f77c018aSMihai Carabas 		buf[buf_len] = ' ';buf_len++;
361f77c018aSMihai Carabas 	} else {
362f77c018aSMihai Carabas 		sbuf_printf(sb, "|-");
363f77c018aSMihai Carabas 		buf[buf_len] = '|';buf_len++;
364f77c018aSMihai Carabas 		buf[buf_len] = ' ';buf_len++;
365f77c018aSMihai Carabas 	}
366f77c018aSMihai Carabas 
367f77c018aSMihai Carabas 	bsr_member = BSRCPUMASK(node->members);
368f77c018aSMihai Carabas 
369f77c018aSMihai Carabas 	if (node->type == PACKAGE_LEVEL) {
370f77c018aSMihai Carabas 		sbuf_printf(sb,"PACKAGE MEMBERS: ");
371f77c018aSMihai Carabas 	} else if (node->type == CHIP_LEVEL) {
372f77c018aSMihai Carabas 		sbuf_printf(sb,"CHIP ID %d: ",
373f77c018aSMihai Carabas 			get_chip_ID(bsr_member));
374f77c018aSMihai Carabas 	} else if (node->type == CORE_LEVEL) {
3759fc6d334SSascha Wildner 		if (node->compute_unit_id != (uint8_t)-1) {
3760e9325d3SMihai Carabas 			sbuf_printf(sb,"Compute Unit ID %d: ",
3770e9325d3SMihai Carabas 				node->compute_unit_id);
3780e9325d3SMihai Carabas 		} else {
379f77c018aSMihai Carabas 			sbuf_printf(sb,"CORE ID %d: ",
380f77c018aSMihai Carabas 				get_core_number_within_chip(bsr_member));
3810e9325d3SMihai Carabas 		}
382f77c018aSMihai Carabas 	} else if (node->type == THREAD_LEVEL) {
3839fc6d334SSascha Wildner 		if (node->compute_unit_id != (uint8_t)-1) {
384c70d4562SMatthew Dillon 			sbuf_printf(sb,"THREAD ID %d: ",
3850e9325d3SMihai Carabas 				get_core_number_within_chip(bsr_member));
3860e9325d3SMihai Carabas 		} else {
387f77c018aSMihai Carabas 			sbuf_printf(sb,"THREAD ID %d: ",
388f77c018aSMihai Carabas 				get_logical_CPU_number_within_core(bsr_member));
3890e9325d3SMihai Carabas 		}
390f77c018aSMihai Carabas 	} else {
391f77c018aSMihai Carabas 		sbuf_printf(sb,"UNKNOWN: ");
392f77c018aSMihai Carabas 	}
393399efd7fSMatthew Dillon 	sbuf_print_cpuset(sb, &node->members);
394f77c018aSMihai Carabas 	sbuf_printf(sb,"\n");
395f77c018aSMihai Carabas 
396f77c018aSMihai Carabas 	for (i = 0; i < node->child_no; i++) {
3970e9325d3SMihai Carabas 		print_cpu_topology_tree_sysctl_helper(node->child_node[i],
398f77c018aSMihai Carabas 		    sb, buf, buf_len, i == (node->child_no -1));
399f77c018aSMihai Carabas 	}
400f77c018aSMihai Carabas }
401f77c018aSMihai Carabas 
402f77c018aSMihai Carabas /* SYSCTL PROCEDURE for printing the CPU Topology tree */
403f77c018aSMihai Carabas static int
print_cpu_topology_tree_sysctl(SYSCTL_HANDLER_ARGS)404f77c018aSMihai Carabas print_cpu_topology_tree_sysctl(SYSCTL_HANDLER_ARGS)
405f77c018aSMihai Carabas {
406f77c018aSMihai Carabas 	struct sbuf *sb;
407f77c018aSMihai Carabas 	int ret;
408f77c018aSMihai Carabas 	char buf[INDENT_BUF_SIZE];
409f77c018aSMihai Carabas 
410f77c018aSMihai Carabas 	KASSERT(cpu_root_node != NULL, ("cpu_root_node isn't initialized"));
411f77c018aSMihai Carabas 
412f77c018aSMihai Carabas 	sb = sbuf_new(NULL, NULL, 500, SBUF_AUTOEXTEND);
413f77c018aSMihai Carabas 	if (sb == NULL) {
414f77c018aSMihai Carabas 		return (ENOMEM);
415f77c018aSMihai Carabas 	}
416f77c018aSMihai Carabas 	sbuf_printf(sb,"\n");
417f77c018aSMihai Carabas 	print_cpu_topology_tree_sysctl_helper(cpu_root_node, sb, buf, 0, 1);
418f77c018aSMihai Carabas 
419f77c018aSMihai Carabas 	sbuf_finish(sb);
420f77c018aSMihai Carabas 
421f77c018aSMihai Carabas 	ret = SYSCTL_OUT(req, sbuf_data(sb), sbuf_len(sb));
422f77c018aSMihai Carabas 
423f77c018aSMihai Carabas 	sbuf_delete(sb);
424f77c018aSMihai Carabas 
425f77c018aSMihai Carabas 	return ret;
426f77c018aSMihai Carabas }
427f77c018aSMihai Carabas 
428f77c018aSMihai Carabas /* SYSCTL PROCEDURE for printing the CPU Topology level description */
429f77c018aSMihai Carabas static int
print_cpu_topology_level_description_sysctl(SYSCTL_HANDLER_ARGS)430f77c018aSMihai Carabas print_cpu_topology_level_description_sysctl(SYSCTL_HANDLER_ARGS)
431f77c018aSMihai Carabas {
432f77c018aSMihai Carabas 	struct sbuf *sb;
433f77c018aSMihai Carabas 	int ret;
434f77c018aSMihai Carabas 
435f77c018aSMihai Carabas 	sb = sbuf_new(NULL, NULL, 500, SBUF_AUTOEXTEND);
436f77c018aSMihai Carabas 	if (sb == NULL)
437f77c018aSMihai Carabas 		return (ENOMEM);
438f77c018aSMihai Carabas 
439f77c018aSMihai Carabas 	if (cpu_topology_levels_number == 4) /* HT available */
440f77c018aSMihai Carabas 		sbuf_printf(sb, "0 - thread; 1 - core; 2 - socket; 3 - anything");
441f77c018aSMihai Carabas 	else if (cpu_topology_levels_number == 3) /* No HT available */
442f77c018aSMihai Carabas 		sbuf_printf(sb, "0 - core; 1 - socket; 2 - anything");
443f77c018aSMihai Carabas 	else if (cpu_topology_levels_number == 2) /* No HT and no Multi-Core */
444f77c018aSMihai Carabas 		sbuf_printf(sb, "0 - socket; 1 - anything");
445f77c018aSMihai Carabas 	else
446f77c018aSMihai Carabas 		sbuf_printf(sb, "Unknown");
447f77c018aSMihai Carabas 
448f77c018aSMihai Carabas 	sbuf_finish(sb);
449f77c018aSMihai Carabas 
450f77c018aSMihai Carabas 	ret = SYSCTL_OUT(req, sbuf_data(sb), sbuf_len(sb));
451f77c018aSMihai Carabas 
452f77c018aSMihai Carabas 	sbuf_delete(sb);
453f77c018aSMihai Carabas 
454f77c018aSMihai Carabas 	return ret;
455f77c018aSMihai Carabas }
456f77c018aSMihai Carabas 
457f77c018aSMihai Carabas /* Find a cpu_node_t by a mask */
458f77c018aSMihai Carabas static cpu_node_t *
get_cpu_node_by_cpumask(cpu_node_t * node,cpumask_t mask)459f77c018aSMihai Carabas get_cpu_node_by_cpumask(cpu_node_t * node,
460f77c018aSMihai Carabas 			cpumask_t mask) {
461f77c018aSMihai Carabas 
462f77c018aSMihai Carabas 	cpu_node_t * found = NULL;
463f77c018aSMihai Carabas 	int i;
464f77c018aSMihai Carabas 
465c07315c4SMatthew Dillon 	if (CPUMASK_CMPMASKEQ(node->members, mask))
466f77c018aSMihai Carabas 		return node;
467f77c018aSMihai Carabas 
468f77c018aSMihai Carabas 	for (i = 0; i < node->child_no; i++) {
4690e9325d3SMihai Carabas 		found = get_cpu_node_by_cpumask(node->child_node[i], mask);
470f77c018aSMihai Carabas 		if (found != NULL) {
471f77c018aSMihai Carabas 			return found;
472f77c018aSMihai Carabas 		}
473f77c018aSMihai Carabas 	}
474f77c018aSMihai Carabas 	return NULL;
475f77c018aSMihai Carabas }
476f77c018aSMihai Carabas 
477f77c018aSMihai Carabas cpu_node_t *
get_cpu_node_by_cpuid(int cpuid)478f77c018aSMihai Carabas get_cpu_node_by_cpuid(int cpuid) {
479c07315c4SMatthew Dillon 	cpumask_t mask;
480c07315c4SMatthew Dillon 
481c07315c4SMatthew Dillon 	CPUMASK_ASSBIT(mask, cpuid);
482f77c018aSMihai Carabas 
483f77c018aSMihai Carabas 	KASSERT(cpu_root_node != NULL, ("cpu_root_node isn't initialized"));
484f77c018aSMihai Carabas 
485f77c018aSMihai Carabas 	return get_cpu_node_by_cpumask(cpu_root_node, mask);
486f77c018aSMihai Carabas }
487f77c018aSMihai Carabas 
488f77c018aSMihai Carabas /* Get the mask of siblings for level_type of a cpuid */
489f77c018aSMihai Carabas cpumask_t
get_cpumask_from_level(int cpuid,uint8_t level_type)490f77c018aSMihai Carabas get_cpumask_from_level(int cpuid,
491f77c018aSMihai Carabas 			uint8_t level_type)
492f77c018aSMihai Carabas {
493f77c018aSMihai Carabas 	cpu_node_t * node;
494c07315c4SMatthew Dillon 	cpumask_t mask;
495c07315c4SMatthew Dillon 
496c07315c4SMatthew Dillon 	CPUMASK_ASSBIT(mask, cpuid);
497f77c018aSMihai Carabas 
498f77c018aSMihai Carabas 	KASSERT(cpu_root_node != NULL, ("cpu_root_node isn't initialized"));
499f77c018aSMihai Carabas 
500f77c018aSMihai Carabas 	node = get_cpu_node_by_cpumask(cpu_root_node, mask);
501f77c018aSMihai Carabas 
502f77c018aSMihai Carabas 	if (node == NULL) {
503c07315c4SMatthew Dillon 		CPUMASK_ASSZERO(mask);
504c07315c4SMatthew Dillon 		return mask;
505f77c018aSMihai Carabas 	}
506f77c018aSMihai Carabas 
507f77c018aSMihai Carabas 	while (node != NULL) {
508f77c018aSMihai Carabas 		if (node->type == level_type) {
509f77c018aSMihai Carabas 			return node->members;
510f77c018aSMihai Carabas 		}
511f77c018aSMihai Carabas 		node = node->parent_node;
512f77c018aSMihai Carabas 	}
513c07315c4SMatthew Dillon 	CPUMASK_ASSZERO(mask);
514f77c018aSMihai Carabas 
515c07315c4SMatthew Dillon 	return mask;
516f77c018aSMihai Carabas }
517f77c018aSMihai Carabas 
518d452e98bSSepherosa Ziehau static const cpu_node_t *
get_cpu_node_by_chipid2(const cpu_node_t * node,int chip_id)519d452e98bSSepherosa Ziehau get_cpu_node_by_chipid2(const cpu_node_t *node, int chip_id)
520d452e98bSSepherosa Ziehau {
521d452e98bSSepherosa Ziehau 	int cpuid;
522d452e98bSSepherosa Ziehau 
523d452e98bSSepherosa Ziehau 	if (node->type != CHIP_LEVEL) {
524d452e98bSSepherosa Ziehau 		const cpu_node_t *ret = NULL;
525d452e98bSSepherosa Ziehau 		int i;
526d452e98bSSepherosa Ziehau 
527d452e98bSSepherosa Ziehau 		for (i = 0; i < node->child_no; ++i) {
528d452e98bSSepherosa Ziehau 			ret = get_cpu_node_by_chipid2(node->child_node[i],
529d452e98bSSepherosa Ziehau 			    chip_id);
530d452e98bSSepherosa Ziehau 			if (ret != NULL)
531d452e98bSSepherosa Ziehau 				break;
532d452e98bSSepherosa Ziehau 		}
533d452e98bSSepherosa Ziehau 		return ret;
534d452e98bSSepherosa Ziehau 	}
535d452e98bSSepherosa Ziehau 
536d452e98bSSepherosa Ziehau 	cpuid = BSRCPUMASK(node->members);
537d452e98bSSepherosa Ziehau 	if (get_chip_ID(cpuid) == chip_id)
538d452e98bSSepherosa Ziehau 		return node;
539d452e98bSSepherosa Ziehau 	return NULL;
540d452e98bSSepherosa Ziehau }
541d452e98bSSepherosa Ziehau 
542d452e98bSSepherosa Ziehau const cpu_node_t *
get_cpu_node_by_chipid(int chip_id)543d452e98bSSepherosa Ziehau get_cpu_node_by_chipid(int chip_id)
544d452e98bSSepherosa Ziehau {
545d452e98bSSepherosa Ziehau 	KASSERT(cpu_root_node != NULL, ("cpu_root_node isn't initialized"));
546d452e98bSSepherosa Ziehau 	return get_cpu_node_by_chipid2(cpu_root_node, chip_id);
547d452e98bSSepherosa Ziehau }
548d452e98bSSepherosa Ziehau 
549f77c018aSMihai Carabas /* init pcpu_sysctl structure info */
550f77c018aSMihai Carabas static void
init_pcpu_topology_sysctl(int assumed_ncpus)551c7f9edd8SMatthew Dillon init_pcpu_topology_sysctl(int assumed_ncpus)
552f77c018aSMihai Carabas {
553f77c018aSMihai Carabas 	struct sbuf sb;
5549002b0d5SMatthew Dillon 	cpumask_t mask;
5559002b0d5SMatthew Dillon 	int min_id = -1;
5569002b0d5SMatthew Dillon 	int max_id = -1;
5579002b0d5SMatthew Dillon 	int i;
5589002b0d5SMatthew Dillon 	int phys_id;
559f77c018aSMihai Carabas 
560d8f4ebf4SCharlie Root 	pcpu_sysctl = kmalloc(sizeof(*pcpu_sysctl) * MAXCPU, M_PCPUSYS,
561d8f4ebf4SCharlie Root 			      M_INTWAIT | M_ZERO);
562f77c018aSMihai Carabas 
563c7f9edd8SMatthew Dillon 	for (i = 0; i < assumed_ncpus; i++) {
564f77c018aSMihai Carabas 		sbuf_new(&sb, pcpu_sysctl[i].cpu_name,
565f77c018aSMihai Carabas 		    sizeof(pcpu_sysctl[i].cpu_name), SBUF_FIXEDLEN);
566f77c018aSMihai Carabas 		sbuf_printf(&sb,"cpu%d", i);
567f77c018aSMihai Carabas 		sbuf_finish(&sb);
568f77c018aSMihai Carabas 
569f77c018aSMihai Carabas 
570f77c018aSMihai Carabas 		/* Get physical siblings */
571f77c018aSMihai Carabas 		mask = get_cpumask_from_level(i, CHIP_LEVEL);
572c07315c4SMatthew Dillon 		if (CPUMASK_TESTZERO(mask)) {
573f77c018aSMihai Carabas 			pcpu_sysctl[i].physical_id = INVALID_ID;
574f77c018aSMihai Carabas 			continue;
575f77c018aSMihai Carabas 		}
576f77c018aSMihai Carabas 
577f77c018aSMihai Carabas 		sbuf_new(&sb, pcpu_sysctl[i].physical_siblings,
578f77c018aSMihai Carabas 		    sizeof(pcpu_sysctl[i].physical_siblings), SBUF_FIXEDLEN);
579399efd7fSMatthew Dillon 		sbuf_print_cpuset(&sb, &mask);
580f77c018aSMihai Carabas 		sbuf_trim(&sb);
581f77c018aSMihai Carabas 		sbuf_finish(&sb);
582f77c018aSMihai Carabas 
5839002b0d5SMatthew Dillon 		phys_id = get_chip_ID(i);
5849002b0d5SMatthew Dillon 		pcpu_sysctl[i].physical_id = phys_id;
5859002b0d5SMatthew Dillon 		if (min_id < 0 || min_id > phys_id)
5869002b0d5SMatthew Dillon 			min_id = phys_id;
5879002b0d5SMatthew Dillon 		if (max_id < 0 || max_id < phys_id)
5889002b0d5SMatthew Dillon 			max_id = phys_id;
589f77c018aSMihai Carabas 
590f77c018aSMihai Carabas 		/* Get core siblings */
591f77c018aSMihai Carabas 		mask = get_cpumask_from_level(i, CORE_LEVEL);
592c07315c4SMatthew Dillon 		if (CPUMASK_TESTZERO(mask)) {
593f77c018aSMihai Carabas 			pcpu_sysctl[i].core_id = INVALID_ID;
594f77c018aSMihai Carabas 			continue;
595f77c018aSMihai Carabas 		}
596f77c018aSMihai Carabas 
597f77c018aSMihai Carabas 		sbuf_new(&sb, pcpu_sysctl[i].core_siblings,
598f77c018aSMihai Carabas 		    sizeof(pcpu_sysctl[i].core_siblings), SBUF_FIXEDLEN);
599399efd7fSMatthew Dillon 		sbuf_print_cpuset(&sb, &mask);
600f77c018aSMihai Carabas 		sbuf_trim(&sb);
601f77c018aSMihai Carabas 		sbuf_finish(&sb);
602f77c018aSMihai Carabas 
603f77c018aSMihai Carabas 		pcpu_sysctl[i].core_id = get_core_number_within_chip(i);
6048e5d7c42SMatthew Dillon 		if (cpu_topology_core_ids < pcpu_sysctl[i].core_id + 1)
60533ee48c4SMatthew Dillon 			cpu_topology_core_ids = pcpu_sysctl[i].core_id + 1;
606f77c018aSMihai Carabas 
6078e5d7c42SMatthew Dillon 		pcpu_sysctl[i].ht_id = get_logical_CPU_number_within_core(i);
6088e5d7c42SMatthew Dillon 		if (cpu_topology_ht_ids < pcpu_sysctl[i].ht_id + 1)
6098e5d7c42SMatthew Dillon 			cpu_topology_ht_ids = pcpu_sysctl[i].ht_id + 1;
610f77c018aSMihai Carabas 	}
6119002b0d5SMatthew Dillon 
6129002b0d5SMatthew Dillon 	/*
6139002b0d5SMatthew Dillon 	 * Normalize physical ids so they can be used by the VM system.
6149002b0d5SMatthew Dillon 	 * Some systems number starting at 0 others number starting at 1.
6159002b0d5SMatthew Dillon 	 */
6169002b0d5SMatthew Dillon 	cpu_topology_phys_ids = max_id - min_id + 1;
6179002b0d5SMatthew Dillon 	if (cpu_topology_phys_ids <= 0)		/* don't crash */
6189002b0d5SMatthew Dillon 		cpu_topology_phys_ids = 1;
619c7f9edd8SMatthew Dillon 	for (i = 0; i < assumed_ncpus; i++) {
6209002b0d5SMatthew Dillon 		pcpu_sysctl[i].physical_id %= cpu_topology_phys_ids;
6219002b0d5SMatthew Dillon 	}
622f77c018aSMihai Carabas }
623f77c018aSMihai Carabas 
624f77c018aSMihai Carabas /* Build SYSCTL structure for revealing
625f77c018aSMihai Carabas  * the CPU Topology to user-space.
626f77c018aSMihai Carabas  */
627f77c018aSMihai Carabas static void
build_sysctl_cpu_topology(int assumed_ncpus)628c7f9edd8SMatthew Dillon build_sysctl_cpu_topology(int assumed_ncpus)
629f77c018aSMihai Carabas {
630f77c018aSMihai Carabas 	int i;
631f77c018aSMihai Carabas 	struct sbuf sb;
632f77c018aSMihai Carabas 
633f77c018aSMihai Carabas 	/* SYSCTL new leaf for "cpu_topology" */
634f77c018aSMihai Carabas 	sysctl_ctx_init(&cpu_topology_sysctl_ctx);
635f77c018aSMihai Carabas 	cpu_topology_sysctl_tree = SYSCTL_ADD_NODE(&cpu_topology_sysctl_ctx,
636f77c018aSMihai Carabas 	    SYSCTL_STATIC_CHILDREN(_hw),
637f77c018aSMihai Carabas 	    OID_AUTO,
638f77c018aSMihai Carabas 	    "cpu_topology",
639f77c018aSMihai Carabas 	    CTLFLAG_RD, 0, "");
640f77c018aSMihai Carabas 
641f77c018aSMihai Carabas 	/* SYSCTL cpu_topology "tree" entry */
642f77c018aSMihai Carabas 	SYSCTL_ADD_PROC(&cpu_topology_sysctl_ctx,
643f77c018aSMihai Carabas 	    SYSCTL_CHILDREN(cpu_topology_sysctl_tree),
644f77c018aSMihai Carabas 	    OID_AUTO, "tree", CTLTYPE_STRING | CTLFLAG_RD,
645f77c018aSMihai Carabas 	    NULL, 0, print_cpu_topology_tree_sysctl, "A",
646f77c018aSMihai Carabas 	    "Tree print of CPU topology");
647f77c018aSMihai Carabas 
648f77c018aSMihai Carabas 	/* SYSCTL cpu_topology "level_description" entry */
649f77c018aSMihai Carabas 	SYSCTL_ADD_PROC(&cpu_topology_sysctl_ctx,
650f77c018aSMihai Carabas 	    SYSCTL_CHILDREN(cpu_topology_sysctl_tree),
651f77c018aSMihai Carabas 	    OID_AUTO, "level_description", CTLTYPE_STRING | CTLFLAG_RD,
652f77c018aSMihai Carabas 	    NULL, 0, print_cpu_topology_level_description_sysctl, "A",
653f77c018aSMihai Carabas 	    "Level description of CPU topology");
654f77c018aSMihai Carabas 
655f77c018aSMihai Carabas 	/* SYSCTL cpu_topology "members" entry */
656f77c018aSMihai Carabas 	sbuf_new(&sb, cpu_topology_members,
657f77c018aSMihai Carabas 	    sizeof(cpu_topology_members), SBUF_FIXEDLEN);
658399efd7fSMatthew Dillon 	sbuf_print_cpuset(&sb, &cpu_root_node->members);
659f77c018aSMihai Carabas 	sbuf_trim(&sb);
660f77c018aSMihai Carabas 	sbuf_finish(&sb);
661f77c018aSMihai Carabas 	SYSCTL_ADD_STRING(&cpu_topology_sysctl_ctx,
662f77c018aSMihai Carabas 	    SYSCTL_CHILDREN(cpu_topology_sysctl_tree),
663f77c018aSMihai Carabas 	    OID_AUTO, "members", CTLFLAG_RD,
664f77c018aSMihai Carabas 	    cpu_topology_members, 0,
665f77c018aSMihai Carabas 	    "Members of the CPU Topology");
666f77c018aSMihai Carabas 
667f77c018aSMihai Carabas 	/* SYSCTL per_cpu info */
668c7f9edd8SMatthew Dillon 	for (i = 0; i < assumed_ncpus; i++) {
669f77c018aSMihai Carabas 		/* New leaf : hw.cpu_topology.cpux */
670f77c018aSMihai Carabas 		sysctl_ctx_init(&pcpu_sysctl[i].sysctl_ctx);
671f77c018aSMihai Carabas 		pcpu_sysctl[i].sysctl_tree = SYSCTL_ADD_NODE(&pcpu_sysctl[i].sysctl_ctx,
672f77c018aSMihai Carabas 		    SYSCTL_CHILDREN(cpu_topology_sysctl_tree),
673f77c018aSMihai Carabas 		    OID_AUTO,
674f77c018aSMihai Carabas 		    pcpu_sysctl[i].cpu_name,
675f77c018aSMihai Carabas 		    CTLFLAG_RD, 0, "");
676f77c018aSMihai Carabas 
677f77c018aSMihai Carabas 		/* Check if the physical_id found is valid */
678f77c018aSMihai Carabas 		if (pcpu_sysctl[i].physical_id == INVALID_ID) {
679f77c018aSMihai Carabas 			continue;
680f77c018aSMihai Carabas 		}
681f77c018aSMihai Carabas 
682f77c018aSMihai Carabas 		/* Add physical id info */
683f77c018aSMihai Carabas 		SYSCTL_ADD_INT(&pcpu_sysctl[i].sysctl_ctx,
684f77c018aSMihai Carabas 		    SYSCTL_CHILDREN(pcpu_sysctl[i].sysctl_tree),
685f77c018aSMihai Carabas 		    OID_AUTO, "physical_id", CTLFLAG_RD,
686f77c018aSMihai Carabas 		    &pcpu_sysctl[i].physical_id, 0,
687f77c018aSMihai Carabas 		    "Physical ID");
688f77c018aSMihai Carabas 
689f77c018aSMihai Carabas 		/* Add physical siblings */
690f77c018aSMihai Carabas 		SYSCTL_ADD_STRING(&pcpu_sysctl[i].sysctl_ctx,
691f77c018aSMihai Carabas 		    SYSCTL_CHILDREN(pcpu_sysctl[i].sysctl_tree),
692f77c018aSMihai Carabas 		    OID_AUTO, "physical_siblings", CTLFLAG_RD,
693f77c018aSMihai Carabas 		    pcpu_sysctl[i].physical_siblings, 0,
694f77c018aSMihai Carabas 		    "Physical siblings");
695f77c018aSMihai Carabas 
696f77c018aSMihai Carabas 		/* Check if the core_id found is valid */
697f77c018aSMihai Carabas 		if (pcpu_sysctl[i].core_id == INVALID_ID) {
698f77c018aSMihai Carabas 			continue;
699f77c018aSMihai Carabas 		}
700f77c018aSMihai Carabas 
701f77c018aSMihai Carabas 		/* Add core id info */
702f77c018aSMihai Carabas 		SYSCTL_ADD_INT(&pcpu_sysctl[i].sysctl_ctx,
703f77c018aSMihai Carabas 		    SYSCTL_CHILDREN(pcpu_sysctl[i].sysctl_tree),
704f77c018aSMihai Carabas 		    OID_AUTO, "core_id", CTLFLAG_RD,
705f77c018aSMihai Carabas 		    &pcpu_sysctl[i].core_id, 0,
706f77c018aSMihai Carabas 		    "Core ID");
707f77c018aSMihai Carabas 
708f77c018aSMihai Carabas 		/*Add core siblings */
709f77c018aSMihai Carabas 		SYSCTL_ADD_STRING(&pcpu_sysctl[i].sysctl_ctx,
710f77c018aSMihai Carabas 		    SYSCTL_CHILDREN(pcpu_sysctl[i].sysctl_tree),
711f77c018aSMihai Carabas 		    OID_AUTO, "core_siblings", CTLFLAG_RD,
712f77c018aSMihai Carabas 		    pcpu_sysctl[i].core_siblings, 0,
713f77c018aSMihai Carabas 		    "Core siblings");
714f77c018aSMihai Carabas 	}
715f77c018aSMihai Carabas }
716f77c018aSMihai Carabas 
717399efd7fSMatthew Dillon static
718399efd7fSMatthew Dillon void
sbuf_print_cpuset(struct sbuf * sb,cpumask_t * mask)719399efd7fSMatthew Dillon sbuf_print_cpuset(struct sbuf *sb, cpumask_t *mask)
720399efd7fSMatthew Dillon {
721399efd7fSMatthew Dillon 	int i;
722399efd7fSMatthew Dillon 	int b = -1;
723399efd7fSMatthew Dillon 	int e = -1;
724399efd7fSMatthew Dillon 	int more = 0;
725399efd7fSMatthew Dillon 
726399efd7fSMatthew Dillon 	sbuf_printf(sb, "cpus(");
727399efd7fSMatthew Dillon 	CPUSET_FOREACH(i, *mask) {
728399efd7fSMatthew Dillon 		if (b < 0) {
729399efd7fSMatthew Dillon 			b = i;
730399efd7fSMatthew Dillon 			e = b + 1;
731399efd7fSMatthew Dillon 			continue;
732399efd7fSMatthew Dillon 		}
733399efd7fSMatthew Dillon 		if (e == i) {
734399efd7fSMatthew Dillon 			++e;
735399efd7fSMatthew Dillon 			continue;
736399efd7fSMatthew Dillon 		}
737399efd7fSMatthew Dillon 		if (more)
738399efd7fSMatthew Dillon 			sbuf_printf(sb, ", ");
739399efd7fSMatthew Dillon 		if (b == e - 1) {
740399efd7fSMatthew Dillon 			sbuf_printf(sb, "%d", b);
741399efd7fSMatthew Dillon 		} else {
742399efd7fSMatthew Dillon 			sbuf_printf(sb, "%d-%d", b, e - 1);
743399efd7fSMatthew Dillon 		}
744399efd7fSMatthew Dillon 		more = 1;
745399efd7fSMatthew Dillon 		b = i;
746399efd7fSMatthew Dillon 		e = b + 1;
747399efd7fSMatthew Dillon 	}
748399efd7fSMatthew Dillon 	if (more)
749399efd7fSMatthew Dillon 		sbuf_printf(sb, ", ");
750399efd7fSMatthew Dillon 	if (b >= 0) {
75152a4925cSMatthew Dillon 		if (b == e - 1) {
752399efd7fSMatthew Dillon 			sbuf_printf(sb, "%d", b);
753399efd7fSMatthew Dillon 		} else {
754399efd7fSMatthew Dillon 			sbuf_printf(sb, "%d-%d", b, e - 1);
755399efd7fSMatthew Dillon 		}
756399efd7fSMatthew Dillon 	}
757399efd7fSMatthew Dillon 	sbuf_printf(sb, ") ");
758399efd7fSMatthew Dillon }
759399efd7fSMatthew Dillon 
76033ee48c4SMatthew Dillon int
get_cpu_ht_id(int cpuid)7618e5d7c42SMatthew Dillon get_cpu_ht_id(int cpuid)
7628e5d7c42SMatthew Dillon {
7638e5d7c42SMatthew Dillon 	if (pcpu_sysctl)
7648e5d7c42SMatthew Dillon 		return(pcpu_sysctl[cpuid].ht_id);
7658e5d7c42SMatthew Dillon 	return(0);
7668e5d7c42SMatthew Dillon }
7678e5d7c42SMatthew Dillon 
7688e5d7c42SMatthew Dillon int
get_cpu_core_id(int cpuid)76933ee48c4SMatthew Dillon get_cpu_core_id(int cpuid)
77033ee48c4SMatthew Dillon {
77133ee48c4SMatthew Dillon 	if (pcpu_sysctl)
77233ee48c4SMatthew Dillon 		return(pcpu_sysctl[cpuid].core_id);
77333ee48c4SMatthew Dillon 	return(0);
77433ee48c4SMatthew Dillon }
77533ee48c4SMatthew Dillon 
77633ee48c4SMatthew Dillon int
get_cpu_phys_id(int cpuid)77733ee48c4SMatthew Dillon get_cpu_phys_id(int cpuid)
77833ee48c4SMatthew Dillon {
77933ee48c4SMatthew Dillon 	if (pcpu_sysctl)
78033ee48c4SMatthew Dillon 		return(pcpu_sysctl[cpuid].physical_id);
78133ee48c4SMatthew Dillon 	return(0);
78233ee48c4SMatthew Dillon }
78333ee48c4SMatthew Dillon 
784c70d4562SMatthew Dillon /*
785c70d4562SMatthew Dillon  * Returns the highest amount of memory attached to any single node.
786c70d4562SMatthew Dillon  * Returns 0 if the system is not NUMA or only has one node.
787c70d4562SMatthew Dillon  *
788c70d4562SMatthew Dillon  * This function is used by the scheduler.
789c70d4562SMatthew Dillon  */
790c70d4562SMatthew Dillon long
get_highest_node_memory(void)791c70d4562SMatthew Dillon get_highest_node_memory(void)
792c70d4562SMatthew Dillon {
793c70d4562SMatthew Dillon 	long highest = 0;
794c70d4562SMatthew Dillon 
795c70d4562SMatthew Dillon         if (cpu_root_node && cpu_root_node->type == PACKAGE_LEVEL &&
796c70d4562SMatthew Dillon 	    cpu_root_node->child_node[1]) {
797c70d4562SMatthew Dillon                 cpu_node_t *cpup;
798c70d4562SMatthew Dillon                 int i;
799c70d4562SMatthew Dillon 
800c70d4562SMatthew Dillon                 for (i = 0 ; i < MAXCPU && cpu_root_node->child_node[i]; ++i) {
801c70d4562SMatthew Dillon                         cpup = cpu_root_node->child_node[i];
802c70d4562SMatthew Dillon                         if (highest < cpup->phys_mem)
803c70d4562SMatthew Dillon                                 highest = cpup->phys_mem;
804c70d4562SMatthew Dillon                 }
805c70d4562SMatthew Dillon         }
806c70d4562SMatthew Dillon 	return highest;
807c70d4562SMatthew Dillon }
808c70d4562SMatthew Dillon 
809c7f9edd8SMatthew Dillon extern int naps;
810c7f9edd8SMatthew Dillon 
811f77c018aSMihai Carabas /* Build the CPU Topology and SYSCTL Topology tree */
812f77c018aSMihai Carabas static void
init_cpu_topology(void)813f77c018aSMihai Carabas init_cpu_topology(void)
814f77c018aSMihai Carabas {
815c7f9edd8SMatthew Dillon 	int assumed_ncpus;
816f77c018aSMihai Carabas 
817c7f9edd8SMatthew Dillon 	assumed_ncpus = naps + 1;
818c7f9edd8SMatthew Dillon 
819c7f9edd8SMatthew Dillon 	build_cpu_topology(assumed_ncpus);
820c7f9edd8SMatthew Dillon 	init_pcpu_topology_sysctl(assumed_ncpus);
821c7f9edd8SMatthew Dillon 	build_sysctl_cpu_topology(assumed_ncpus);
822f77c018aSMihai Carabas }
823f77c018aSMihai Carabas SYSINIT(cpu_topology, SI_BOOT2_CPU_TOPOLOGY, SI_ORDER_FIRST,
824f3f3eadbSSascha Wildner     init_cpu_topology, NULL);
825