2 * Copyright (c) 2012 The DragonFly Project. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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
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.
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
33 #include <sys/param.h>
34 #include <sys/systm.h>
35 #include <sys/kernel.h>
36 #include <sys/sysctl.h>
38 #include <sys/cpu_topology.h>
40 #include <machine/smp.h>
48 #define INDENT_BUF_SIZE LEVEL_NO*3
51 /* Per-cpu sysctl nodes and info */
52 struct per_cpu_sysctl_info {
53 struct sysctl_ctx_list sysctl_ctx;
54 struct sysctl_oid *sysctl_tree;
58 char physical_siblings[8*MAXCPU];
59 char core_siblings[8*MAXCPU];
61 typedef struct per_cpu_sysctl_info per_cpu_sysctl_info_t;
63 static cpu_node_t cpu_topology_nodes[MAXCPU]; /* Memory for topology */
64 static cpu_node_t *cpu_root_node; /* Root node pointer */
66 static struct sysctl_ctx_list cpu_topology_sysctl_ctx;
67 static struct sysctl_oid *cpu_topology_sysctl_tree;
68 static char cpu_topology_members[8*MAXCPU];
69 static per_cpu_sysctl_info_t pcpu_sysctl[MAXCPU];
71 int cpu_topology_levels_number = 1;
72 cpu_node_t *root_cpu_node;
74 /* Get the next valid apicid starting
75 * from current apicid (curr_apicid
78 get_next_valid_apicid(int curr_apicid)
80 int next_apicid = curr_apicid;
84 while(get_cpuid_from_apicid(next_apicid) == -1 &&
85 next_apicid < NAPICID);
86 if (next_apicid == NAPICID) {
87 kprintf("Warning: No next valid APICID found. Returning -1\n");
93 /* Generic topology tree. The parameters have the following meaning:
94 * - children_no_per_level : the number of children on each level
95 * - level_types : the type of the level (THREAD, CORE, CHIP, etc)
96 * - cur_level : the current level of the tree
97 * - node : the current node
98 * - last_free_node : the last free node in the global array.
99 * - cpuid : basicly this are the ids of the leafs
102 build_topology_tree(int *children_no_per_level,
103 uint8_t *level_types,
106 cpu_node_t **last_free_node,
111 node->child_no = children_no_per_level[cur_level];
112 node->type = level_types[cur_level];
115 if (node->child_no == 0) {
116 node->child_node = NULL;
117 *apicid = get_next_valid_apicid(*apicid);
118 node->members = CPUMASK(get_cpuid_from_apicid(*apicid));
122 node->child_node = *last_free_node;
123 (*last_free_node) += node->child_no;
124 if (node->parent_node == NULL)
125 root_cpu_node = node;
127 for (i = 0; i < node->child_no; i++) {
128 node->child_node[i].parent_node = node;
130 build_topology_tree(children_no_per_level,
133 &(node->child_node[i]),
137 node->members |= node->child_node[i].members;
141 /* Build CPU topology. The detection is made by comparing the
142 * chip, core and logical IDs of each CPU with the IDs of the
143 * BSP. When we found a match, at that level the CPUs are siblings.
146 build_cpu_topology(void)
148 detect_cpu_topology();
151 int threads_per_core = 0;
152 int cores_per_chip = 0;
153 int chips_per_package = 0;
154 int children_no_per_level[LEVEL_NO];
155 uint8_t level_types[LEVEL_NO];
158 cpu_node_t *root = &cpu_topology_nodes[0];
159 cpu_node_t *last_free_node = root + 1;
161 /* Assume that the topology is uniform.
162 * Find the number of siblings within chip
163 * and witin core to build up the topology
165 for (i = 0; i < ncpus; i++) {
167 cpumask_t mask = CPUMASK(i);
169 if ((mask & smp_active_mask) == 0)
172 if (get_chip_ID(BSPID) == get_chip_ID(i))
177 if (get_core_number_within_chip(BSPID) ==
178 get_core_number_within_chip(i))
182 cores_per_chip /= threads_per_core;
183 chips_per_package = ncpus / (cores_per_chip * threads_per_core);
186 kprintf("CPU Topology: cores_per_chip: %d; threads_per_core: %d; chips_per_package: %d;\n",
187 cores_per_chip, threads_per_core, chips_per_package);
189 if (threads_per_core > 1) { /* HT available - 4 levels */
191 children_no_per_level[0] = chips_per_package;
192 children_no_per_level[1] = cores_per_chip;
193 children_no_per_level[2] = threads_per_core;
194 children_no_per_level[3] = 0;
196 level_types[0] = PACKAGE_LEVEL;
197 level_types[1] = CHIP_LEVEL;
198 level_types[2] = CORE_LEVEL;
199 level_types[3] = THREAD_LEVEL;
201 build_topology_tree(children_no_per_level,
208 cpu_topology_levels_number = 4;
210 } else if (cores_per_chip > 1) { /* No HT available - 3 levels */
212 children_no_per_level[0] = chips_per_package;
213 children_no_per_level[1] = cores_per_chip;
214 children_no_per_level[2] = 0;
216 level_types[0] = PACKAGE_LEVEL;
217 level_types[1] = CHIP_LEVEL;
218 level_types[2] = CORE_LEVEL;
220 build_topology_tree(children_no_per_level,
227 cpu_topology_levels_number = 3;
229 } else { /* No HT and no Multi-Core - 2 levels */
231 children_no_per_level[0] = chips_per_package;
232 children_no_per_level[1] = 0;
234 level_types[0] = PACKAGE_LEVEL;
235 level_types[1] = CHIP_LEVEL;
237 build_topology_tree(children_no_per_level,
244 cpu_topology_levels_number = 2;
251 /* Recursive function helper to print the CPU topology tree */
253 print_cpu_topology_tree_sysctl_helper(cpu_node_t *node,
262 sbuf_bcat(sb, buf, buf_len);
264 sbuf_printf(sb, "\\-");
265 buf[buf_len] = ' ';buf_len++;
266 buf[buf_len] = ' ';buf_len++;
268 sbuf_printf(sb, "|-");
269 buf[buf_len] = '|';buf_len++;
270 buf[buf_len] = ' ';buf_len++;
273 bsr_member = BSRCPUMASK(node->members);
275 if (node->type == PACKAGE_LEVEL) {
276 sbuf_printf(sb,"PACKAGE MEMBERS: ");
277 } else if (node->type == CHIP_LEVEL) {
278 sbuf_printf(sb,"CHIP ID %d: ",
279 get_chip_ID(bsr_member));
280 } else if (node->type == CORE_LEVEL) {
281 sbuf_printf(sb,"CORE ID %d: ",
282 get_core_number_within_chip(bsr_member));
283 } else if (node->type == THREAD_LEVEL) {
284 sbuf_printf(sb,"THREAD ID %d: ",
285 get_logical_CPU_number_within_core(bsr_member));
287 sbuf_printf(sb,"UNKNOWN: ");
289 CPUSET_FOREACH(i, node->members) {
290 sbuf_printf(sb,"cpu%d ", i);
293 sbuf_printf(sb,"\n");
295 for (i = 0; i < node->child_no; i++) {
296 print_cpu_topology_tree_sysctl_helper(&(node->child_node[i]),
297 sb, buf, buf_len, i == (node->child_no -1));
301 /* SYSCTL PROCEDURE for printing the CPU Topology tree */
303 print_cpu_topology_tree_sysctl(SYSCTL_HANDLER_ARGS)
307 char buf[INDENT_BUF_SIZE];
309 KASSERT(cpu_root_node != NULL, ("cpu_root_node isn't initialized"));
311 sb = sbuf_new(NULL, NULL, 500, SBUF_AUTOEXTEND);
315 sbuf_printf(sb,"\n");
316 print_cpu_topology_tree_sysctl_helper(cpu_root_node, sb, buf, 0, 1);
320 ret = SYSCTL_OUT(req, sbuf_data(sb), sbuf_len(sb));
327 /* SYSCTL PROCEDURE for printing the CPU Topology level description */
329 print_cpu_topology_level_description_sysctl(SYSCTL_HANDLER_ARGS)
334 sb = sbuf_new(NULL, NULL, 500, SBUF_AUTOEXTEND);
338 if (cpu_topology_levels_number == 4) /* HT available */
339 sbuf_printf(sb, "0 - thread; 1 - core; 2 - socket; 3 - anything");
340 else if (cpu_topology_levels_number == 3) /* No HT available */
341 sbuf_printf(sb, "0 - core; 1 - socket; 2 - anything");
342 else if (cpu_topology_levels_number == 2) /* No HT and no Multi-Core */
343 sbuf_printf(sb, "0 - socket; 1 - anything");
345 sbuf_printf(sb, "Unknown");
349 ret = SYSCTL_OUT(req, sbuf_data(sb), sbuf_len(sb));
356 /* Find a cpu_node_t by a mask */
358 get_cpu_node_by_cpumask(cpu_node_t * node,
361 cpu_node_t * found = NULL;
364 if (node->members == mask) {
368 for (i = 0; i < node->child_no; i++) {
369 found = get_cpu_node_by_cpumask(&(node->child_node[i]), mask);
378 get_cpu_node_by_cpuid(int cpuid) {
379 cpumask_t mask = CPUMASK(cpuid);
381 KASSERT(cpu_root_node != NULL, ("cpu_root_node isn't initialized"));
383 return get_cpu_node_by_cpumask(cpu_root_node, mask);
386 /* Get the mask of siblings for level_type of a cpuid */
388 get_cpumask_from_level(int cpuid,
392 cpumask_t mask = CPUMASK(cpuid);
394 KASSERT(cpu_root_node != NULL, ("cpu_root_node isn't initialized"));
396 node = get_cpu_node_by_cpumask(cpu_root_node, mask);
402 while (node != NULL) {
403 if (node->type == level_type) {
404 return node->members;
406 node = node->parent_node;
412 /* init pcpu_sysctl structure info */
414 init_pcpu_topology_sysctl(void)
421 for (i = 0; i < ncpus; i++) {
423 sbuf_new(&sb, pcpu_sysctl[i].cpu_name,
424 sizeof(pcpu_sysctl[i].cpu_name), SBUF_FIXEDLEN);
425 sbuf_printf(&sb,"cpu%d", i);
429 /* Get physical siblings */
430 mask = get_cpumask_from_level(i, CHIP_LEVEL);
432 pcpu_sysctl[i].physical_id = INVALID_ID;
436 sbuf_new(&sb, pcpu_sysctl[i].physical_siblings,
437 sizeof(pcpu_sysctl[i].physical_siblings), SBUF_FIXEDLEN);
438 CPUSET_FOREACH(cpu, mask) {
439 sbuf_printf(&sb,"cpu%d ", cpu);
444 pcpu_sysctl[i].physical_id = get_chip_ID(i);
446 /* Get core siblings */
447 mask = get_cpumask_from_level(i, CORE_LEVEL);
449 pcpu_sysctl[i].core_id = INVALID_ID;
453 sbuf_new(&sb, pcpu_sysctl[i].core_siblings,
454 sizeof(pcpu_sysctl[i].core_siblings), SBUF_FIXEDLEN);
455 CPUSET_FOREACH(cpu, mask) {
456 sbuf_printf(&sb,"cpu%d ", cpu);
461 pcpu_sysctl[i].core_id = get_core_number_within_chip(i);
466 /* Build SYSCTL structure for revealing
467 * the CPU Topology to user-space.
470 build_sysctl_cpu_topology(void)
475 /* SYSCTL new leaf for "cpu_topology" */
476 sysctl_ctx_init(&cpu_topology_sysctl_ctx);
477 cpu_topology_sysctl_tree = SYSCTL_ADD_NODE(&cpu_topology_sysctl_ctx,
478 SYSCTL_STATIC_CHILDREN(_hw),
483 /* SYSCTL cpu_topology "tree" entry */
484 SYSCTL_ADD_PROC(&cpu_topology_sysctl_ctx,
485 SYSCTL_CHILDREN(cpu_topology_sysctl_tree),
486 OID_AUTO, "tree", CTLTYPE_STRING | CTLFLAG_RD,
487 NULL, 0, print_cpu_topology_tree_sysctl, "A",
488 "Tree print of CPU topology");
490 /* SYSCTL cpu_topology "level_description" entry */
491 SYSCTL_ADD_PROC(&cpu_topology_sysctl_ctx,
492 SYSCTL_CHILDREN(cpu_topology_sysctl_tree),
493 OID_AUTO, "level_description", CTLTYPE_STRING | CTLFLAG_RD,
494 NULL, 0, print_cpu_topology_level_description_sysctl, "A",
495 "Level description of CPU topology");
497 /* SYSCTL cpu_topology "members" entry */
498 sbuf_new(&sb, cpu_topology_members,
499 sizeof(cpu_topology_members), SBUF_FIXEDLEN);
500 CPUSET_FOREACH(i, cpu_root_node->members) {
501 sbuf_printf(&sb,"cpu%d ", i);
505 SYSCTL_ADD_STRING(&cpu_topology_sysctl_ctx,
506 SYSCTL_CHILDREN(cpu_topology_sysctl_tree),
507 OID_AUTO, "members", CTLFLAG_RD,
508 cpu_topology_members, 0,
509 "Members of the CPU Topology");
511 /* SYSCTL per_cpu info */
512 for (i = 0; i < ncpus; i++) {
513 /* New leaf : hw.cpu_topology.cpux */
514 sysctl_ctx_init(&pcpu_sysctl[i].sysctl_ctx);
515 pcpu_sysctl[i].sysctl_tree = SYSCTL_ADD_NODE(&pcpu_sysctl[i].sysctl_ctx,
516 SYSCTL_CHILDREN(cpu_topology_sysctl_tree),
518 pcpu_sysctl[i].cpu_name,
521 /* Check if the physical_id found is valid */
522 if (pcpu_sysctl[i].physical_id == INVALID_ID) {
526 /* Add physical id info */
527 SYSCTL_ADD_INT(&pcpu_sysctl[i].sysctl_ctx,
528 SYSCTL_CHILDREN(pcpu_sysctl[i].sysctl_tree),
529 OID_AUTO, "physical_id", CTLFLAG_RD,
530 &pcpu_sysctl[i].physical_id, 0,
533 /* Add physical siblings */
534 SYSCTL_ADD_STRING(&pcpu_sysctl[i].sysctl_ctx,
535 SYSCTL_CHILDREN(pcpu_sysctl[i].sysctl_tree),
536 OID_AUTO, "physical_siblings", CTLFLAG_RD,
537 pcpu_sysctl[i].physical_siblings, 0,
538 "Physical siblings");
540 /* Check if the core_id found is valid */
541 if (pcpu_sysctl[i].core_id == INVALID_ID) {
545 /* Add core id info */
546 SYSCTL_ADD_INT(&pcpu_sysctl[i].sysctl_ctx,
547 SYSCTL_CHILDREN(pcpu_sysctl[i].sysctl_tree),
548 OID_AUTO, "core_id", CTLFLAG_RD,
549 &pcpu_sysctl[i].core_id, 0,
552 /*Add core siblings */
553 SYSCTL_ADD_STRING(&pcpu_sysctl[i].sysctl_ctx,
554 SYSCTL_CHILDREN(pcpu_sysctl[i].sysctl_tree),
555 OID_AUTO, "core_siblings", CTLFLAG_RD,
556 pcpu_sysctl[i].core_siblings, 0,
561 /* Build the CPU Topology and SYSCTL Topology tree */
563 init_cpu_topology(void)
565 cpu_root_node = build_cpu_topology();
567 init_pcpu_topology_sysctl();
568 build_sysctl_cpu_topology();
570 SYSINIT(cpu_topology, SI_BOOT2_CPU_TOPOLOGY, SI_ORDER_FIRST,
571 init_cpu_topology, NULL)