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