2 * Copyright (c) 2012 The DragonFly Project. All rights reserved.
4 * This code is derived from software contributed to The DragonFly Project
5 * by Matthew Dillon <dillon@backplane.com>
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in
15 * the documentation and/or other materials provided with the
17 * 3. Neither the name of The DragonFly Project nor the names of its
18 * contributors may be used to endorse or promote products derived
19 * from this software without specific, prior written permission.
21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26 * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
27 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
31 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
38 #include <sys/param.h>
39 #include <sys/systm.h>
40 #include <sys/kernel.h>
41 #include <sys/sysctl.h>
42 #include <sys/malloc.h>
43 #include <sys/memrange.h>
44 #include <sys/cons.h> /* cngetc() */
45 #include <sys/machintr.h>
47 #include <sys/mplock2.h>
55 #include <machine/smp.h>
56 #include <machine_base/apic/apicreg.h>
57 #include <machine/atomic.h>
58 #include <machine/cpufunc.h>
59 #include <machine/cputypes.h>
60 #include <machine_base/apic/lapic.h>
61 #include <machine_base/apic/ioapic.h>
62 #include <machine/psl.h>
63 #include <machine/segments.h>
64 #include <machine/tss.h>
65 #include <machine/specialreg.h>
66 #include <machine/globaldata.h>
67 #include <machine/pmap_inval.h>
70 #include <sys/cpu_topology.h>
72 #define INDENT_BUF_SIZE LEVEL_NO*3
75 static cpu_node_t cpu_topology_nodes[MAXCPU]; /* Memory for topology */
76 static cpu_node_t *cpu_root_node; /* Root node pointer */
78 static struct sysctl_ctx_list cpu_topology_sysctl_ctx;
79 static struct sysctl_oid *cpu_topology_sysctl_tree;
80 static char cpu_topology_members[8*MAXCPU];
82 struct per_cpu_sysctl_info {
83 struct sysctl_ctx_list sysctl_ctx;
84 struct sysctl_oid *sysctl_tree;
88 char physical_siblings[8*MAXCPU];
89 char core_siblings[8*MAXCPU];
91 typedef struct per_cpu_sysctl_info per_cpu_sysctl_info_t;
93 static per_cpu_sysctl_info_t pcpu_sysctl[MAXCPU];
96 /************************************/
97 /* CPU TOPOLOGY BUILDING FUNCTIONS */
98 /************************************/
101 /* Generic topology tree.
102 * @param children_no_per_level : the number of children on each level
103 * @param level_types : the type of the level (THREAD, CORE, CHIP, etc)
104 * @param cur_level : the current level of the tree
105 * @param node : the current node
106 * @param last_free_node : the last free node in the global array.
107 * @param cpuid : basicly this are the ids of the leafs
110 build_topology_tree(int *children_no_per_level,
111 uint8_t *level_types,
114 cpu_node_t **last_free_node,
119 node->child_no = children_no_per_level[cur_level];
120 node->type = level_types[cur_level];
123 if (node->child_no == 0) {
124 node->child_node = NULL;
125 node->members = CPUMASK(APICID_TO_CPUID(*apicid));
130 node->child_node = *last_free_node;
131 (*last_free_node) += node->child_no;
133 for (i = 0; i< node->child_no; i++) {
135 node->child_node[i].parent_node = node;
137 build_topology_tree(children_no_per_level,
140 &(node->child_node[i]),
144 node->members |= node->child_node[i].members;
148 /* Build CPU topology. The detection is made by comparing the
149 * chip,core and logical IDs of each CPU with the IDs of the
150 * BSP. When we found a match, at that level the CPUs are siblings.
153 build_cpu_topology(void)
155 detect_cpu_topology();
158 int threads_per_core = 0;
159 int cores_per_chip = 0;
160 int chips_per_package = 0;
161 int children_no_per_level[LEVEL_NO];
162 uint8_t level_types[LEVEL_NO];
165 cpu_node_t *root = &cpu_topology_nodes[0];
166 cpu_node_t *last_free_node = root + 1;
168 /* Assume that the topology is uniform.
169 * Find the number of siblings within chip
170 * and witin core to build up the topology
172 for (i = 0; i < ncpus; i++) {
174 cpumask_t mask = CPUMASK(i);
176 if ((mask & smp_active_mask) == 0){
180 if (get_chip_ID(BSPID) == get_chip_ID(i)) {
186 if (get_core_number_within_chip(BSPID)
187 == get_core_number_within_chip(i)) {
192 cores_per_chip /= threads_per_core;
193 chips_per_package = ncpus / (cores_per_chip * threads_per_core);
195 if (threads_per_core > 1) { /* HT available - 4 levels */
197 children_no_per_level[0] = chips_per_package;
198 children_no_per_level[1] = cores_per_chip;
199 children_no_per_level[2] = threads_per_core;
200 children_no_per_level[3] = 0;
202 level_types[0] = PACKAGE_LEVEL;
203 level_types[1] = CHIP_LEVEL;
204 level_types[2] = CORE_LEVEL;
205 level_types[3] = THREAD_LEVEL;
207 build_topology_tree(children_no_per_level,
214 } else if (cores_per_chip > 1) { /* No HT available - 3 levels */
216 children_no_per_level[0] = chips_per_package;
217 children_no_per_level[1] = cores_per_chip;
218 children_no_per_level[2] = 0;
220 level_types[0] = PACKAGE_LEVEL;
221 level_types[1] = CHIP_LEVEL;
222 level_types[2] = CORE_LEVEL;
224 build_topology_tree(children_no_per_level,
230 } else { /* No HT and no Multi-Core - 2 levels */
232 children_no_per_level[0] = chips_per_package;
233 children_no_per_level[1] = 0;
235 level_types[0] = PACKAGE_LEVEL;
236 level_types[1] = CHIP_LEVEL;
238 build_topology_tree(children_no_per_level,
248 /* Recursive function helper to print the CPU topology tree */
250 print_cpu_topology_tree_sysctl_helper(cpu_node_t *node,
259 sbuf_bcat(sb, buf, buf_len);
261 sbuf_printf(sb, "\\-");
262 buf[buf_len] = ' ';buf_len++;
263 buf[buf_len] = ' ';buf_len++;
265 sbuf_printf(sb, "|-");
266 buf[buf_len] = '|';buf_len++;
267 buf[buf_len] = ' ';buf_len++;
270 bsr_member = BSRCPUMASK(node->members);
272 if (node->type == PACKAGE_LEVEL) {
273 sbuf_printf(sb,"PACKAGE MEMBERS: ");
274 } else if (node->type == CHIP_LEVEL) {
275 sbuf_printf(sb,"CHIP ID %d: ",
276 get_chip_ID(bsr_member));
277 } else if (node->type == CORE_LEVEL) {
278 sbuf_printf(sb,"CORE ID %d: ",
279 get_core_number_within_chip(bsr_member));
280 } else if (node->type == THREAD_LEVEL) {
281 sbuf_printf(sb,"THREAD ID %d: ",
282 get_logical_CPU_number_within_core(bsr_member));
284 sbuf_printf(sb,"UNKNOWN: ");
286 CPUSET_FOREACH(i, node->members) {
287 sbuf_printf(sb,"cpu%d ", i);
290 sbuf_printf(sb,"\n");
292 for (i = 0; i < node->child_no; i++) {
293 print_cpu_topology_tree_sysctl_helper(&(node->child_node[i]),
294 sb, buf, buf_len, i == (node->child_no -1));
298 /* SYSCTL PROCEDURE for printing the CPU Topology tree */
300 print_cpu_topology_tree_sysctl(SYSCTL_HANDLER_ARGS)
304 char buf[INDENT_BUF_SIZE];
306 KASSERT(cpu_root_node != NULL, ("cpu_root_node isn't initialized"));
308 sb = sbuf_new(NULL, NULL, 500, SBUF_AUTOEXTEND);
312 sbuf_printf(sb,"\n");
313 print_cpu_topology_tree_sysctl_helper(cpu_root_node, sb, buf, 0, 1);
317 ret = SYSCTL_OUT(req, sbuf_data(sb), sbuf_len(sb));
324 /* Find a cpu_node_t by a mask */
326 get_cpu_node_by_cpumask(cpu_node_t * node,
329 cpu_node_t * found = NULL;
332 if (node->members == mask) {
336 for (i = 0; i < node->child_no; i++) {
337 found = get_cpu_node_by_cpumask(&(node->child_node[i]), mask);
346 get_cpu_node_by_cpuid(int cpuid) {
347 cpumask_t mask = CPUMASK(cpuid);
349 KASSERT(cpu_root_node != NULL, ("cpu_root_node isn't initialized"));
351 return get_cpu_node_by_cpumask(cpu_root_node, mask);
354 /* Get the mask of siblings for level_type of a cpuid */
356 get_cpumask_from_level(int cpuid,
360 cpumask_t mask = CPUMASK(cpuid);
362 KASSERT(cpu_root_node != NULL, ("cpu_root_node isn't initialized"));
364 node = get_cpu_node_by_cpumask(cpu_root_node, mask);
370 while (node != NULL) {
371 if (node->type == level_type) {
372 return node->members;
374 node = node->parent_node;
380 /* init pcpu_sysctl structure info */
382 init_pcpu_topology_sysctl(void)
389 for (i = 0; i < ncpus; i++) {
391 sbuf_new(&sb, pcpu_sysctl[i].cpu_name,
392 sizeof(pcpu_sysctl[i].cpu_name), SBUF_FIXEDLEN);
393 sbuf_printf(&sb,"cpu%d", i);
397 /* Get physical siblings */
398 mask = get_cpumask_from_level(i, CHIP_LEVEL);
400 pcpu_sysctl[i].physical_id = INVALID_ID;
404 sbuf_new(&sb, pcpu_sysctl[i].physical_siblings,
405 sizeof(pcpu_sysctl[i].physical_siblings), SBUF_FIXEDLEN);
406 CPUSET_FOREACH(cpu, mask) {
407 sbuf_printf(&sb,"cpu%d ", cpu);
412 pcpu_sysctl[i].physical_id = get_chip_ID(i);
414 /* Get core siblings */
415 mask = get_cpumask_from_level(i, CORE_LEVEL);
417 pcpu_sysctl[i].core_id = INVALID_ID;
421 sbuf_new(&sb, pcpu_sysctl[i].core_siblings,
422 sizeof(pcpu_sysctl[i].core_siblings), SBUF_FIXEDLEN);
423 CPUSET_FOREACH(cpu, mask) {
424 sbuf_printf(&sb,"cpu%d ", cpu);
429 pcpu_sysctl[i].core_id = get_core_number_within_chip(i);
434 /* Build SYSCTL structure for revealing
435 * the CPU Topology to user-space.
438 build_sysctl_cpu_topology(void)
443 /* SYSCTL new leaf for "cpu_topology" */
444 sysctl_ctx_init(&cpu_topology_sysctl_ctx);
445 cpu_topology_sysctl_tree = SYSCTL_ADD_NODE(&cpu_topology_sysctl_ctx,
446 SYSCTL_STATIC_CHILDREN(_hw),
451 /* SYSCTL cpu_topology "tree" entry */
452 SYSCTL_ADD_PROC(&cpu_topology_sysctl_ctx,
453 SYSCTL_CHILDREN(cpu_topology_sysctl_tree),
454 OID_AUTO, "tree", CTLTYPE_STRING | CTLFLAG_RD,
455 NULL, 0, print_cpu_topology_tree_sysctl, "A",
456 "Tree print of CPU topology");
458 /* SYSCTL cpu_topology "members" entry */
459 sbuf_new(&sb, cpu_topology_members,
460 sizeof(cpu_topology_members), SBUF_FIXEDLEN);
461 CPUSET_FOREACH(i, cpu_root_node->members) {
462 sbuf_printf(&sb,"cpu%d ", i);
466 SYSCTL_ADD_STRING(&cpu_topology_sysctl_ctx,
467 SYSCTL_CHILDREN(cpu_topology_sysctl_tree),
468 OID_AUTO, "members", CTLFLAG_RD,
469 cpu_topology_members, 0,
470 "Members of the CPU Topology");
472 /* SYSCTL per_cpu info */
473 for (i = 0; i < ncpus; i++) {
474 /* New leaf : hw.cpu_topology.cpux */
475 sysctl_ctx_init(&pcpu_sysctl[i].sysctl_ctx);
476 pcpu_sysctl[i].sysctl_tree = SYSCTL_ADD_NODE(&pcpu_sysctl[i].sysctl_ctx,
477 SYSCTL_CHILDREN(cpu_topology_sysctl_tree),
479 pcpu_sysctl[i].cpu_name,
482 /* Check if the physical_id found is valid */
483 if (pcpu_sysctl[i].physical_id == INVALID_ID) {
487 /* Add physical id info */
488 SYSCTL_ADD_INT(&pcpu_sysctl[i].sysctl_ctx,
489 SYSCTL_CHILDREN(pcpu_sysctl[i].sysctl_tree),
490 OID_AUTO, "physical_id", CTLFLAG_RD,
491 &pcpu_sysctl[i].physical_id, 0,
494 /* Add physical siblings */
495 SYSCTL_ADD_STRING(&pcpu_sysctl[i].sysctl_ctx,
496 SYSCTL_CHILDREN(pcpu_sysctl[i].sysctl_tree),
497 OID_AUTO, "physical_sibings", CTLFLAG_RD,
498 pcpu_sysctl[i].physical_siblings, 0,
499 "Physical siblings");
501 /* Check if the core_id found is valid */
502 if (pcpu_sysctl[i].core_id == INVALID_ID) {
506 /* Add core id info */
507 SYSCTL_ADD_INT(&pcpu_sysctl[i].sysctl_ctx,
508 SYSCTL_CHILDREN(pcpu_sysctl[i].sysctl_tree),
509 OID_AUTO, "core_id", CTLFLAG_RD,
510 &pcpu_sysctl[i].core_id, 0,
513 /*Add core siblings */
514 SYSCTL_ADD_STRING(&pcpu_sysctl[i].sysctl_ctx,
515 SYSCTL_CHILDREN(pcpu_sysctl[i].sysctl_tree),
516 OID_AUTO, "core_siblings", CTLFLAG_RD,
517 pcpu_sysctl[i].core_siblings, 0,
522 /* Build the CPU Topology and SYSCTL Topology tree */
524 init_cpu_topology(void)
526 cpu_root_node = build_cpu_topology();
528 init_pcpu_topology_sysctl();
529 build_sysctl_cpu_topology();
531 SYSINIT(cpu_topology, SI_BOOT2_CPU_TOPOLOGY, SI_ORDER_FIRST,
532 init_cpu_topology, NULL)