CPU Topology: check if there are any physical siblings before add the sysctl node
[dragonfly.git] / sys / kern / cpu_topology.c
1 /*
2  * Copyright (c) 2012 The DragonFly Project.  All rights reserved.
3  * 
4  * This code is derived from software contributed to The DragonFly Project
5  * by Matthew Dillon <dillon@backplane.com>
6  * 
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 
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
16  *    distribution.
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.
20  * 
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
32  * SUCH DAMAGE.
33  * 
34  */
35
36 #include "opt_cpu.h"
37
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>
46
47 #include <sys/mplock2.h>
48
49 #include <sys/lock.h>
50 #include <sys/user.h>
51 #ifdef GPROF 
52 #include <sys/gmon.h>
53 #endif
54
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>
68
69 #include <sys/sbuf.h>
70 #include <sys/cpu_topology.h>
71
72 #define INDENT_BUF_SIZE LEVEL_NO*3
73 #define INVALID_ID -1
74
75 static cpu_node_t cpu_topology_nodes[MAXCPU]; /* Memory for topology */
76 static cpu_node_t *cpu_root_node; /* Root node pointer */
77
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];
81
82 struct per_cpu_sysctl_info {
83         struct sysctl_ctx_list sysctl_ctx;
84         struct sysctl_oid *sysctl_tree;
85         char cpu_name[32];
86         int physical_id;
87         int core_id;
88         char physical_siblings[8*MAXCPU];
89         char core_siblings[8*MAXCPU];
90 };
91 typedef struct per_cpu_sysctl_info per_cpu_sysctl_info_t;
92
93 static per_cpu_sysctl_info_t pcpu_sysctl[MAXCPU];
94
95
96 /************************************/
97 /* CPU TOPOLOGY BUILDING  FUNCTIONS */
98 /************************************/
99
100
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
108  */ 
109 static void
110 build_topology_tree(int *children_no_per_level,
111                 uint8_t *level_types,
112                 int cur_level,
113                 cpu_node_t *node,
114                 cpu_node_t **last_free_node,
115                 int *apicid)
116 {
117         int i;
118
119         node->child_no = children_no_per_level[cur_level];
120         node->type = level_types[cur_level];
121         node->members = 0;
122
123         if (node->child_no == 0) {
124                 node->child_node = NULL;
125                 node->members = CPUMASK(APICID_TO_CPUID(*apicid));
126                 (*apicid)++;
127                 return;
128         }
129
130         node->child_node = *last_free_node;
131         (*last_free_node) += node->child_no;
132         
133         for (i = 0; i< node->child_no; i++) {
134
135                 node->child_node[i].parent_node = node;
136
137                 build_topology_tree(children_no_per_level,
138                                 level_types,
139                                 cur_level + 1,
140                                 &(node->child_node[i]),
141                                 last_free_node,
142                                 apicid);
143
144                 node->members |= node->child_node[i].members;
145         }
146 }
147
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.
151  */
152 static cpu_node_t *
153 build_cpu_topology(void)
154 {
155         detect_cpu_topology();
156         int i;
157         int BSPID = 0;
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];
163         int apicid = 0;
164
165         cpu_node_t *root = &cpu_topology_nodes[0];
166         cpu_node_t *last_free_node = root + 1;
167
168         /* Assume that the topology is uniform.
169          * Find the number of siblings within chip
170          * and witin core to build up the topology
171          */
172         for (i = 0; i < ncpus; i++) {
173
174                 cpumask_t mask = CPUMASK(i);
175
176                 if ((mask & smp_active_mask) == 0){
177                         continue;
178                 }
179
180                 if (get_chip_ID(BSPID) == get_chip_ID(i)) {
181                         cores_per_chip++;
182                 } else {
183                         continue;
184                 }
185
186                 if (get_core_number_within_chip(BSPID)
187                         == get_core_number_within_chip(i)) {
188                         threads_per_core++;
189                 }
190                 
191         }
192         cores_per_chip /= threads_per_core;
193         chips_per_package = ncpus / (cores_per_chip * threads_per_core);
194         
195         if (threads_per_core > 1) { /* HT available - 4 levels */
196
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;
201
202                 level_types[0] = PACKAGE_LEVEL;
203                 level_types[1] = CHIP_LEVEL;
204                 level_types[2] = CORE_LEVEL;
205                 level_types[3] = THREAD_LEVEL;
206         
207                 build_topology_tree(children_no_per_level,
208                                 level_types,
209                                 0,
210                                 root,
211                                 &last_free_node,
212                                 &apicid);
213         
214         } else if (cores_per_chip > 1) { /* No HT available - 3 levels */
215
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;
219
220                 level_types[0] = PACKAGE_LEVEL;
221                 level_types[1] = CHIP_LEVEL;
222                 level_types[2] = CORE_LEVEL;
223         
224                 build_topology_tree(children_no_per_level,
225                                 level_types,
226                                 0,
227                                 root,
228                                 &last_free_node,
229                                 &apicid);
230         } else { /* No HT and no Multi-Core - 2 levels */
231
232                 children_no_per_level[0] = chips_per_package;
233                 children_no_per_level[1] = 0;
234
235                 level_types[0] = PACKAGE_LEVEL;
236                 level_types[1] = CHIP_LEVEL;
237         
238                 build_topology_tree(children_no_per_level,
239                                 level_types,
240                                 0,
241                                 root,
242                                 &last_free_node,
243                                 &apicid);
244         }
245         return root;
246 }
247
248 /* Recursive function helper to print the CPU topology tree */
249 static void
250 print_cpu_topology_tree_sysctl_helper(cpu_node_t *node,
251                                 struct sbuf *sb,
252                                 char * buf,
253                                 int buf_len,
254                                 int last)
255 {
256         int i;
257         int bsr_member;
258
259         sbuf_bcat(sb, buf, buf_len);
260         if (last) {
261                 sbuf_printf(sb, "\\-");
262                 buf[buf_len] = ' ';buf_len++;
263                 buf[buf_len] = ' ';buf_len++;
264         } else {
265                 sbuf_printf(sb, "|-");
266                 buf[buf_len] = '|';buf_len++;
267                 buf[buf_len] = ' ';buf_len++;
268         }
269         
270         bsr_member = BSRCPUMASK(node->members);
271
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));
283         } else {
284                 sbuf_printf(sb,"UNKNOWN: ");
285         }
286         CPUSET_FOREACH(i, node->members) {
287                 sbuf_printf(sb,"cpu%d ", i);
288         }       
289         
290         sbuf_printf(sb,"\n");
291
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));
295         }
296 }
297
298 /* SYSCTL PROCEDURE for printing the CPU Topology tree */
299 static int
300 print_cpu_topology_tree_sysctl(SYSCTL_HANDLER_ARGS)
301 {
302         struct sbuf *sb;
303         int ret;
304         char buf[INDENT_BUF_SIZE];
305
306         KASSERT(cpu_root_node != NULL, ("cpu_root_node isn't initialized"));
307
308         sb = sbuf_new(NULL, NULL, 500, SBUF_AUTOEXTEND);
309         if (sb == NULL) {
310                 return (ENOMEM);
311         }
312         sbuf_printf(sb,"\n");
313         print_cpu_topology_tree_sysctl_helper(cpu_root_node, sb, buf, 0, 1);
314
315         sbuf_finish(sb);
316
317         ret = SYSCTL_OUT(req, sbuf_data(sb), sbuf_len(sb));
318
319         sbuf_delete(sb);
320
321         return ret;     
322 }
323
324 /* Find a cpu_node_t by a mask */
325 static cpu_node_t *
326 get_cpu_node_by_cpumask(cpu_node_t * node,
327                         cpumask_t mask) {
328
329         cpu_node_t * found = NULL;
330         int i;
331
332         if (node->members == mask) {
333                 return node;
334         }
335
336         for (i = 0; i < node->child_no; i++) {
337                 found = get_cpu_node_by_cpumask(&(node->child_node[i]), mask);
338                 if (found != NULL) {
339                         return found;
340                 }
341         }
342         return NULL;
343 }
344
345 cpu_node_t *
346 get_cpu_node_by_cpuid(int cpuid) {
347         cpumask_t mask = CPUMASK(cpuid);
348
349         KASSERT(cpu_root_node != NULL, ("cpu_root_node isn't initialized"));
350
351         return get_cpu_node_by_cpumask(cpu_root_node, mask);
352 }
353
354 /* Get the mask of siblings for level_type of a cpuid */
355 cpumask_t
356 get_cpumask_from_level(int cpuid,
357                         uint8_t level_type)
358 {
359         cpu_node_t * node;
360         cpumask_t mask = CPUMASK(cpuid);
361
362         KASSERT(cpu_root_node != NULL, ("cpu_root_node isn't initialized"));
363
364         node = get_cpu_node_by_cpumask(cpu_root_node, mask);
365
366         if (node == NULL) {
367                 return 0;
368         }
369
370         while (node != NULL) {
371                 if (node->type == level_type) {
372                         return node->members;
373                 }
374                 node = node->parent_node;
375         }
376
377         return 0;
378 }
379
380 /* init pcpu_sysctl structure info */
381 static void
382 init_pcpu_topology_sysctl(void)
383 {
384         int cpu;
385         int i;
386         cpumask_t mask;
387         struct sbuf sb;
388
389         for (i = 0; i < ncpus; i++) {
390
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);
394                 sbuf_finish(&sb);
395
396
397                 /* Get physical siblings */
398                 mask = get_cpumask_from_level(i, CHIP_LEVEL);
399                 if (mask == 0) {
400                         pcpu_sysctl[i].physical_id = INVALID_ID;
401                         continue;
402                 }
403
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);
408                 }
409                 sbuf_trim(&sb);
410                 sbuf_finish(&sb);
411
412                 pcpu_sysctl[i].physical_id = get_chip_ID(i); 
413
414                 /* Get core siblings */
415                 mask = get_cpumask_from_level(i, CORE_LEVEL);
416                 if (mask == 0) {
417                         pcpu_sysctl[i].core_id = INVALID_ID;
418                         continue;
419                 }
420
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);
425                 }
426                 sbuf_trim(&sb);
427                 sbuf_finish(&sb);
428
429                 pcpu_sysctl[i].core_id = get_core_number_within_chip(i); 
430
431         }
432 }
433
434 /* Build SYSCTL structure for revealing
435  * the CPU Topology to user-space.
436  */
437 static void
438 build_sysctl_cpu_topology(void)
439 {
440         int i;
441         struct sbuf sb;
442         
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),
447                                         OID_AUTO,
448                                         "cpu_topology",
449                                         CTLFLAG_RD, 0, "");
450
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");
457
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);
463         }
464         sbuf_trim(&sb);
465         sbuf_finish(&sb);
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");
471
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),
478                                         OID_AUTO,
479                                         pcpu_sysctl[i].cpu_name,
480                                         CTLFLAG_RD, 0, "");
481
482                 /* Check if the physical_id found is valid */
483                 if (pcpu_sysctl[i].physical_id == INVALID_ID) {
484                         continue;
485                 }
486
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,
492                         "Physical ID");
493
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");
500
501                 /* Check if the core_id found is valid */
502                 if (pcpu_sysctl[i].core_id == INVALID_ID) {
503                         continue;
504                 }
505
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,
511                         "Core ID");
512                 
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,
518                         "Core siblings");
519         }
520 }
521
522 /* Build the CPU Topology and SYSCTL Topology tree */
523 static void
524 init_cpu_topology(void)
525 {
526         cpu_root_node = build_cpu_topology();
527
528         init_pcpu_topology_sysctl();
529         build_sysctl_cpu_topology();
530 }
531 SYSINIT(cpu_topology, SI_BOOT2_CPU_TOPOLOGY, SI_ORDER_FIRST,
532         init_cpu_topology, NULL)