CPU Topology: Refactoring (split machdep and general code). Fix Intel detection bug.
[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 static cpu_node_t cpu_topology_nodes[MAXCPU];
70 static cpu_node_t *cpu_root_node;
71
72 static int print_cpu_topology_tree_sysctl(SYSCTL_HANDLER_ARGS);
73
74 /************************************/
75 /* CPU TOPOLOGY BUILDING  FUNCTIONS */
76 /************************************/
77
78
79 /* Generic topology tree.
80  * @param children_no_per_level : the number of children on each level
81  * @param level_types : the type of the level (THREAD, CORE, CHIP, etc)
82  * @param cur_level : the current level of the tree
83  * @param node : the current node
84  * @param last_free_node : the last free node in the global array.
85  * @param cpuid : basicly this are the ids of the leafs
86  */ 
87 static void
88 build_topology_tree(int *children_no_per_level,
89                 uint8_t *level_types,
90                 int cur_level,
91                 cpu_node_t *node,
92                 cpu_node_t **last_free_node,
93                 int *cpuid)
94 {
95         int i;
96
97         node->child_no = children_no_per_level[cur_level];
98         node->type = level_types[cur_level];
99         node->members = 0;
100
101         if (node->child_no == 0) {
102                 node->child_node = NULL;
103                 node->members = CPUMASK(*cpuid);
104                 (*cpuid)++;
105                 return;
106         }
107
108         node->child_node = *last_free_node;
109         (*last_free_node) += node->child_no;
110         
111         for (i = 0; i< node->child_no; i++) {
112
113                 node->child_node[i].parent_node = node;
114
115                 build_topology_tree(children_no_per_level,
116                                 level_types,
117                                 cur_level + 1,
118                                 &(node->child_node[i]),
119                                 last_free_node,
120                                 cpuid);
121
122                 node->members |= node->child_node[i].members;
123         }
124 }
125
126 /* Build CPU topology. The detection is made by comparing the
127  * chip,core and logical IDs of each CPU with the IDs of the 
128  * BSP. When we found a match, at that level the CPUs are siblings.
129  */
130 cpu_node_t *
131 build_cpu_topology(void)
132 {
133         detect_cpu_topology();
134         int i;
135         int BSPID = 0;
136         int LEVEL_NO = 4;
137         int threads_per_core = 0;
138         int cores_per_chip = 0;
139         int chips_per_package = 0;
140         int children_no_per_level[LEVEL_NO];
141         uint8_t level_types[LEVEL_NO];
142         int cpuid = 0;
143
144         cpu_node_t *root = &cpu_topology_nodes[0];
145         cpu_node_t *last_free_node = root + 1;
146
147         /* Assume that the topology is uniform.
148          * Find the number of siblings within chip
149          * and witin core to build up the topology
150          */
151         for (i = 0; i < ncpus; i++) {
152                 
153                 if (get_chip_ID(BSPID) == get_chip_ID(i)) {
154                         cores_per_chip++;
155                 } else {
156                         continue;
157                 }
158
159                 if (get_core_number_within_chip(BSPID)
160                         == get_core_number_within_chip(i)) {
161                         threads_per_core++;
162                 }
163                 
164         }
165         cores_per_chip /= threads_per_core;
166         chips_per_package = ncpus / (cores_per_chip * threads_per_core);
167
168         /* Init topo info.
169          * For now we assume that we have a four level topology
170          */
171         children_no_per_level[0] = chips_per_package;
172         children_no_per_level[1] = cores_per_chip;
173         children_no_per_level[2] = threads_per_core;
174         children_no_per_level[3] = 0;
175
176         level_types[0] = PACKAGE_LEVEL;
177         level_types[1] = CHIP_LEVEL;
178         level_types[2] = CORE_LEVEL;
179         level_types[3] = THREAD_LEVEL;
180
181         build_topology_tree(children_no_per_level,
182                                 level_types,
183                                 0,
184                                 root,
185                                 &last_free_node,
186                                 &cpuid);
187                 
188         return root;
189 }
190
191 /* Find a cpu_node_t by a mask */
192 static cpu_node_t *
193 get_cpu_node_by_cpumask(cpu_node_t * node,
194                         cpumask_t mask) {
195
196         cpu_node_t * found = NULL;
197         int i;
198
199         if (node->members == mask) {
200                 return node;
201         }
202
203         for (i = 0; i < node->child_no; i++) {
204                 found = get_cpu_node_by_cpumask(&(node->child_node[i]), mask);
205                 if (found != NULL) {
206                         return found;
207                 }
208         }
209         return NULL;
210 }
211
212 /* Get the mask of siblings for level_type of a cpuid */
213 cpumask_t
214 get_cpumask_from_level(cpu_node_t * root,
215                         int cpuid,
216                         uint8_t level_type)
217 {
218         cpu_node_t * node;
219         cpumask_t mask = CPUMASK(cpuid);
220         node = get_cpu_node_by_cpumask(root, mask);
221         if (node == NULL) {
222                 return 0;
223         }
224         while (node != NULL) {
225                 if (node->type == level_type) {
226                         return node->members;
227                 }
228                 node = node->parent_node;
229         }
230         return 0;
231 }
232 static struct sysctl_ctx_list cpu_topology_sysctl_ctx;
233 static struct sysctl_oid *cpu_topology_sysctl_tree;
234
235 void
236 init_cpu_topology(void)
237 {
238         cpu_root_node = build_cpu_topology();
239
240         sysctl_ctx_init(&topology_sysctl_ctx); 
241
242         cpu_topology_sysctl_tree = SYSCTL_ADD_NODE(&sc->acpi_sysctl_ctx,
243                                         SYSCTL_STATIC_CHILDREN(_hw),
244                                         OID_AUTO,
245                                         "cpu_topology",
246                                         CTLFLAG_RD, 0, "");
247
248         SYSCTL_ADD_PROC(&topology_sysctl_ctx, SYSCTL_CHILDREN(cpu_topology_sysctl_tree),
249                         OID_AUTO, "tree", CTLTYPE_STRING | CTLFLAG_RD,
250                         NULL, 0, print_cpu_topology_tree_sysctl, "A", "Tree print of CPU topology");
251
252 }
253
254 static void
255 print_cpu_topology_tree_sysctl_helper(cpu_node_t *node, struct sbuf *sb, char * buf, int buf_len, int last)
256 {
257         sbuf_bcat(sb, buf, buf_len);
258         if (last) {
259                 sbuf_printf(sb, "\\-");
260                 buf[buf_len] = ' ';buf_len++;
261                 buf[buf_len] = ' ';buf_len++;
262         } else {
263                 sbuf_printf(sb, "\-");
264                 buf[buf_len] = '|';buf_len++;
265                 buf[buf_len] = ' ';buf_len++;
266         }
267         sbuf_printf(sb,"%d",node->members);
268         for (i = 0; i < node->child_no; i++) {
269                 print_cpu_topology_tree_sysctl_helper(node->child_node[i], sb, buf, buf_len, i == (node->childno -1));
270         }
271 }
272 static int
273 print_cpu_topology_tree_sysctl(SYSCTL_HANDLER_ARGS)
274 {
275         struct sbuf *sb;
276         int ret;
277         int indent = 60;
278         char buf[400];
279
280         KASSERT(cpu_root_node != NULL, ("cpu_root_node isn't initialized"));
281
282         sb = sbuf_new(NULL, NULL, 500, SBUF_AUTOEXTEND);
283         if (sb == NULL) {
284                 return (ENOMEM);
285         }
286         print_cpu_topology_tree_sysctl_helper(cpu_root_node, sb, buf, 0, 0);
287
288         sbuf_finish(sb);
289
290         ret = SYSCTL_OUT(req, sbuf_data(sb), sbuf_len(sb));
291
292         sbuf_delete(sb);
293
294         return ret;     
295 }