xref: /dragonfly/sys/kern/subr_cpu_topology.c (revision b4f25088)
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 
33 #include <sys/param.h>
34 #include <sys/systm.h>
35 #include <sys/kernel.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 	char physical_siblings[8*MAXCPU];
57 	char core_siblings[8*MAXCPU];
58 };
59 typedef struct per_cpu_sysctl_info per_cpu_sysctl_info_t;
60 
61 static cpu_node_t cpu_topology_nodes[MAXCPU];	/* Memory for topology */
62 static cpu_node_t *cpu_root_node;		/* Root node pointer */
63 
64 static struct sysctl_ctx_list cpu_topology_sysctl_ctx;
65 static struct sysctl_oid *cpu_topology_sysctl_tree;
66 static char cpu_topology_members[8*MAXCPU];
67 static per_cpu_sysctl_info_t pcpu_sysctl[MAXCPU];
68 
69 int cpu_topology_levels_number = 1;
70 cpu_node_t *root_cpu_node;
71 
72 /* Get the next valid apicid starting
73  * from current apicid (curr_apicid
74  */
75 static int
76 get_next_valid_apicid(int curr_apicid)
77 {
78 	int next_apicid = curr_apicid;
79 	do {
80 		next_apicid++;
81 	}
82 	while(get_cpuid_from_apicid(next_apicid) == -1 &&
83 	   next_apicid < NAPICID);
84 	if (next_apicid == NAPICID) {
85 		kprintf("Warning: No next valid APICID found. Returning -1\n");
86 		return -1;
87 	}
88 	return next_apicid;
89 }
90 
91 /* Generic topology tree. The parameters have the following meaning:
92  * - children_no_per_level : the number of children on each level
93  * - level_types : the type of the level (THREAD, CORE, CHIP, etc)
94  * - cur_level : the current level of the tree
95  * - node : the current node
96  * - last_free_node : the last free node in the global array.
97  * - cpuid : basicly this are the ids of the leafs
98  */
99 static void
100 build_topology_tree(int *children_no_per_level,
101    uint8_t *level_types,
102    int cur_level,
103    cpu_node_t *node,
104    cpu_node_t **last_free_node,
105    int *apicid)
106 {
107 	int i;
108 
109 	node->child_no = children_no_per_level[cur_level];
110 	node->type = level_types[cur_level];
111 	node->members = 0;
112 
113 	if (node->child_no == 0) {
114 		node->child_node = NULL;
115 		*apicid = get_next_valid_apicid(*apicid);
116 		node->members = CPUMASK(get_cpuid_from_apicid(*apicid));
117 		return;
118 	}
119 
120 	node->child_node = *last_free_node;
121 	(*last_free_node) += node->child_no;
122 	if (node->parent_node == NULL)
123 		root_cpu_node = node;
124 
125 	for (i = 0; i < node->child_no; i++) {
126 		node->child_node[i].parent_node = node;
127 
128 		build_topology_tree(children_no_per_level,
129 		    level_types,
130 		    cur_level + 1,
131 		    &(node->child_node[i]),
132 		    last_free_node,
133 		    apicid);
134 
135 		node->members |= node->child_node[i].members;
136 	}
137 }
138 
139 /* Build CPU topology. The detection is made by comparing the
140  * chip, core and logical IDs of each CPU with the IDs of the
141  * BSP. When we found a match, at that level the CPUs are siblings.
142  */
143 static cpu_node_t *
144 build_cpu_topology(void)
145 {
146 	detect_cpu_topology();
147 	int i;
148 	int BSPID = 0;
149 	int threads_per_core = 0;
150 	int cores_per_chip = 0;
151 	int chips_per_package = 0;
152 	int children_no_per_level[LEVEL_NO];
153 	uint8_t level_types[LEVEL_NO];
154 	int apicid = -1;
155 
156 	cpu_node_t *root = &cpu_topology_nodes[0];
157 	cpu_node_t *last_free_node = root + 1;
158 
159 	/* Assume that the topology is uniform.
160 	 * Find the number of siblings within chip
161 	 * and witin core to build up the topology
162 	 */
163 	for (i = 0; i < ncpus; i++) {
164 
165 		cpumask_t mask = CPUMASK(i);
166 
167 		if ((mask & smp_active_mask) == 0)
168 			continue;
169 
170 		if (get_chip_ID(BSPID) == get_chip_ID(i))
171 			cores_per_chip++;
172 		else
173 			continue;
174 
175 		if (get_core_number_within_chip(BSPID) ==
176 		    get_core_number_within_chip(i))
177 			threads_per_core++;
178 	}
179 
180 	cores_per_chip /= threads_per_core;
181 	chips_per_package = ncpus / (cores_per_chip * threads_per_core);
182 
183 	if (bootverbose)
184 		kprintf("CPU Topology: cores_per_chip: %d; threads_per_core: %d; chips_per_package: %d;\n",
185 		    cores_per_chip, threads_per_core, chips_per_package);
186 
187 	if (threads_per_core > 1) { /* HT available - 4 levels */
188 
189 		children_no_per_level[0] = chips_per_package;
190 		children_no_per_level[1] = cores_per_chip;
191 		children_no_per_level[2] = threads_per_core;
192 		children_no_per_level[3] = 0;
193 
194 		level_types[0] = PACKAGE_LEVEL;
195 		level_types[1] = CHIP_LEVEL;
196 		level_types[2] = CORE_LEVEL;
197 		level_types[3] = THREAD_LEVEL;
198 
199 		build_topology_tree(children_no_per_level,
200 		    level_types,
201 		    0,
202 		    root,
203 		    &last_free_node,
204 		    &apicid);
205 
206 		cpu_topology_levels_number = 4;
207 
208 	} else if (cores_per_chip > 1) { /* No HT available - 3 levels */
209 
210 		children_no_per_level[0] = chips_per_package;
211 		children_no_per_level[1] = cores_per_chip;
212 		children_no_per_level[2] = 0;
213 
214 		level_types[0] = PACKAGE_LEVEL;
215 		level_types[1] = CHIP_LEVEL;
216 		level_types[2] = CORE_LEVEL;
217 
218 		build_topology_tree(children_no_per_level,
219 		    level_types,
220 		    0,
221 		    root,
222 		    &last_free_node,
223 		    &apicid);
224 
225 		cpu_topology_levels_number = 3;
226 
227 	} else { /* No HT and no Multi-Core - 2 levels */
228 
229 		children_no_per_level[0] = chips_per_package;
230 		children_no_per_level[1] = 0;
231 
232 		level_types[0] = PACKAGE_LEVEL;
233 		level_types[1] = CHIP_LEVEL;
234 
235 		build_topology_tree(children_no_per_level,
236 		    level_types,
237 		    0,
238 		    root,
239 		    &last_free_node,
240 		    &apicid);
241 
242 		cpu_topology_levels_number = 2;
243 
244 	}
245 
246 	return root;
247 }
248 
249 /* Recursive function helper to print the CPU topology tree */
250 static void
251 print_cpu_topology_tree_sysctl_helper(cpu_node_t *node,
252     struct sbuf *sb,
253     char * buf,
254     int buf_len,
255     int last)
256 {
257 	int i;
258 	int bsr_member;
259 
260 	sbuf_bcat(sb, buf, buf_len);
261 	if (last) {
262 		sbuf_printf(sb, "\\-");
263 		buf[buf_len] = ' ';buf_len++;
264 		buf[buf_len] = ' ';buf_len++;
265 	} else {
266 		sbuf_printf(sb, "|-");
267 		buf[buf_len] = '|';buf_len++;
268 		buf[buf_len] = ' ';buf_len++;
269 	}
270 
271 	bsr_member = BSRCPUMASK(node->members);
272 
273 	if (node->type == PACKAGE_LEVEL) {
274 		sbuf_printf(sb,"PACKAGE MEMBERS: ");
275 	} else if (node->type == CHIP_LEVEL) {
276 		sbuf_printf(sb,"CHIP ID %d: ",
277 			get_chip_ID(bsr_member));
278 	} else if (node->type == CORE_LEVEL) {
279 		sbuf_printf(sb,"CORE ID %d: ",
280 			get_core_number_within_chip(bsr_member));
281 	} else if (node->type == THREAD_LEVEL) {
282 		sbuf_printf(sb,"THREAD ID %d: ",
283 			get_logical_CPU_number_within_core(bsr_member));
284 	} else {
285 		sbuf_printf(sb,"UNKNOWN: ");
286 	}
287 	CPUSET_FOREACH(i, node->members) {
288 		sbuf_printf(sb,"cpu%d ", i);
289 	}
290 
291 	sbuf_printf(sb,"\n");
292 
293 	for (i = 0; i < node->child_no; i++) {
294 		print_cpu_topology_tree_sysctl_helper(&(node->child_node[i]),
295 		    sb, buf, buf_len, i == (node->child_no -1));
296 	}
297 }
298 
299 /* SYSCTL PROCEDURE for printing the CPU Topology tree */
300 static int
301 print_cpu_topology_tree_sysctl(SYSCTL_HANDLER_ARGS)
302 {
303 	struct sbuf *sb;
304 	int ret;
305 	char buf[INDENT_BUF_SIZE];
306 
307 	KASSERT(cpu_root_node != NULL, ("cpu_root_node isn't initialized"));
308 
309 	sb = sbuf_new(NULL, NULL, 500, SBUF_AUTOEXTEND);
310 	if (sb == NULL) {
311 		return (ENOMEM);
312 	}
313 	sbuf_printf(sb,"\n");
314 	print_cpu_topology_tree_sysctl_helper(cpu_root_node, sb, buf, 0, 1);
315 
316 	sbuf_finish(sb);
317 
318 	ret = SYSCTL_OUT(req, sbuf_data(sb), sbuf_len(sb));
319 
320 	sbuf_delete(sb);
321 
322 	return ret;
323 }
324 
325 /* SYSCTL PROCEDURE for printing the CPU Topology level description */
326 static int
327 print_cpu_topology_level_description_sysctl(SYSCTL_HANDLER_ARGS)
328 {
329 	struct sbuf *sb;
330 	int ret;
331 
332 	sb = sbuf_new(NULL, NULL, 500, SBUF_AUTOEXTEND);
333 	if (sb == NULL)
334 		return (ENOMEM);
335 
336 	if (cpu_topology_levels_number == 4) /* HT available */
337 		sbuf_printf(sb, "0 - thread; 1 - core; 2 - socket; 3 - anything");
338 	else if (cpu_topology_levels_number == 3) /* No HT available */
339 		sbuf_printf(sb, "0 - core; 1 - socket; 2 - anything");
340 	else if (cpu_topology_levels_number == 2) /* No HT and no Multi-Core */
341 		sbuf_printf(sb, "0 - socket; 1 - anything");
342 	else
343 		sbuf_printf(sb, "Unknown");
344 
345 	sbuf_finish(sb);
346 
347 	ret = SYSCTL_OUT(req, sbuf_data(sb), sbuf_len(sb));
348 
349 	sbuf_delete(sb);
350 
351 	return ret;
352 }
353 
354 /* Find a cpu_node_t by a mask */
355 static cpu_node_t *
356 get_cpu_node_by_cpumask(cpu_node_t * node,
357 			cpumask_t mask) {
358 
359 	cpu_node_t * found = NULL;
360 	int i;
361 
362 	if (node->members == mask) {
363 		return node;
364 	}
365 
366 	for (i = 0; i < node->child_no; i++) {
367 		found = get_cpu_node_by_cpumask(&(node->child_node[i]), mask);
368 		if (found != NULL) {
369 			return found;
370 		}
371 	}
372 	return NULL;
373 }
374 
375 cpu_node_t *
376 get_cpu_node_by_cpuid(int cpuid) {
377 	cpumask_t mask = CPUMASK(cpuid);
378 
379 	KASSERT(cpu_root_node != NULL, ("cpu_root_node isn't initialized"));
380 
381 	return get_cpu_node_by_cpumask(cpu_root_node, mask);
382 }
383 
384 /* Get the mask of siblings for level_type of a cpuid */
385 cpumask_t
386 get_cpumask_from_level(int cpuid,
387 			uint8_t level_type)
388 {
389 	cpu_node_t * node;
390 	cpumask_t mask = CPUMASK(cpuid);
391 
392 	KASSERT(cpu_root_node != NULL, ("cpu_root_node isn't initialized"));
393 
394 	node = get_cpu_node_by_cpumask(cpu_root_node, mask);
395 
396 	if (node == NULL) {
397 		return 0;
398 	}
399 
400 	while (node != NULL) {
401 		if (node->type == level_type) {
402 			return node->members;
403 		}
404 		node = node->parent_node;
405 	}
406 
407 	return 0;
408 }
409 
410 /* init pcpu_sysctl structure info */
411 static void
412 init_pcpu_topology_sysctl(void)
413 {
414 	int cpu;
415 	int i;
416 	cpumask_t mask;
417 	struct sbuf sb;
418 
419 	for (i = 0; i < ncpus; i++) {
420 
421 		sbuf_new(&sb, pcpu_sysctl[i].cpu_name,
422 		    sizeof(pcpu_sysctl[i].cpu_name), SBUF_FIXEDLEN);
423 		sbuf_printf(&sb,"cpu%d", i);
424 		sbuf_finish(&sb);
425 
426 
427 		/* Get physical siblings */
428 		mask = get_cpumask_from_level(i, CHIP_LEVEL);
429 		if (mask == 0) {
430 			pcpu_sysctl[i].physical_id = INVALID_ID;
431 			continue;
432 		}
433 
434 		sbuf_new(&sb, pcpu_sysctl[i].physical_siblings,
435 		    sizeof(pcpu_sysctl[i].physical_siblings), SBUF_FIXEDLEN);
436 		CPUSET_FOREACH(cpu, mask) {
437 			sbuf_printf(&sb,"cpu%d ", cpu);
438 		}
439 		sbuf_trim(&sb);
440 		sbuf_finish(&sb);
441 
442 		pcpu_sysctl[i].physical_id = get_chip_ID(i);
443 
444 		/* Get core siblings */
445 		mask = get_cpumask_from_level(i, CORE_LEVEL);
446 		if (mask == 0) {
447 			pcpu_sysctl[i].core_id = INVALID_ID;
448 			continue;
449 		}
450 
451 		sbuf_new(&sb, pcpu_sysctl[i].core_siblings,
452 		    sizeof(pcpu_sysctl[i].core_siblings), SBUF_FIXEDLEN);
453 		CPUSET_FOREACH(cpu, mask) {
454 			sbuf_printf(&sb,"cpu%d ", cpu);
455 		}
456 		sbuf_trim(&sb);
457 		sbuf_finish(&sb);
458 
459 		pcpu_sysctl[i].core_id = get_core_number_within_chip(i);
460 
461 	}
462 }
463 
464 /* Build SYSCTL structure for revealing
465  * the CPU Topology to user-space.
466  */
467 static void
468 build_sysctl_cpu_topology(void)
469 {
470 	int i;
471 	struct sbuf sb;
472 
473 	/* SYSCTL new leaf for "cpu_topology" */
474 	sysctl_ctx_init(&cpu_topology_sysctl_ctx);
475 	cpu_topology_sysctl_tree = SYSCTL_ADD_NODE(&cpu_topology_sysctl_ctx,
476 	    SYSCTL_STATIC_CHILDREN(_hw),
477 	    OID_AUTO,
478 	    "cpu_topology",
479 	    CTLFLAG_RD, 0, "");
480 
481 	/* SYSCTL cpu_topology "tree" entry */
482 	SYSCTL_ADD_PROC(&cpu_topology_sysctl_ctx,
483 	    SYSCTL_CHILDREN(cpu_topology_sysctl_tree),
484 	    OID_AUTO, "tree", CTLTYPE_STRING | CTLFLAG_RD,
485 	    NULL, 0, print_cpu_topology_tree_sysctl, "A",
486 	    "Tree print of CPU topology");
487 
488 	/* SYSCTL cpu_topology "level_description" entry */
489 	SYSCTL_ADD_PROC(&cpu_topology_sysctl_ctx,
490 	    SYSCTL_CHILDREN(cpu_topology_sysctl_tree),
491 	    OID_AUTO, "level_description", CTLTYPE_STRING | CTLFLAG_RD,
492 	    NULL, 0, print_cpu_topology_level_description_sysctl, "A",
493 	    "Level description of CPU topology");
494 
495 	/* SYSCTL cpu_topology "members" entry */
496 	sbuf_new(&sb, cpu_topology_members,
497 	    sizeof(cpu_topology_members), SBUF_FIXEDLEN);
498 	CPUSET_FOREACH(i, cpu_root_node->members) {
499 		sbuf_printf(&sb,"cpu%d ", i);
500 	}
501 	sbuf_trim(&sb);
502 	sbuf_finish(&sb);
503 	SYSCTL_ADD_STRING(&cpu_topology_sysctl_ctx,
504 	    SYSCTL_CHILDREN(cpu_topology_sysctl_tree),
505 	    OID_AUTO, "members", CTLFLAG_RD,
506 	    cpu_topology_members, 0,
507 	    "Members of the CPU Topology");
508 
509 	/* SYSCTL per_cpu info */
510 	for (i = 0; i < ncpus; i++) {
511 		/* New leaf : hw.cpu_topology.cpux */
512 		sysctl_ctx_init(&pcpu_sysctl[i].sysctl_ctx);
513 		pcpu_sysctl[i].sysctl_tree = SYSCTL_ADD_NODE(&pcpu_sysctl[i].sysctl_ctx,
514 		    SYSCTL_CHILDREN(cpu_topology_sysctl_tree),
515 		    OID_AUTO,
516 		    pcpu_sysctl[i].cpu_name,
517 		    CTLFLAG_RD, 0, "");
518 
519 		/* Check if the physical_id found is valid */
520 		if (pcpu_sysctl[i].physical_id == INVALID_ID) {
521 			continue;
522 		}
523 
524 		/* Add physical id info */
525 		SYSCTL_ADD_INT(&pcpu_sysctl[i].sysctl_ctx,
526 		    SYSCTL_CHILDREN(pcpu_sysctl[i].sysctl_tree),
527 		    OID_AUTO, "physical_id", CTLFLAG_RD,
528 		    &pcpu_sysctl[i].physical_id, 0,
529 		    "Physical ID");
530 
531 		/* Add physical siblings */
532 		SYSCTL_ADD_STRING(&pcpu_sysctl[i].sysctl_ctx,
533 		    SYSCTL_CHILDREN(pcpu_sysctl[i].sysctl_tree),
534 		    OID_AUTO, "physical_siblings", CTLFLAG_RD,
535 		    pcpu_sysctl[i].physical_siblings, 0,
536 		    "Physical siblings");
537 
538 		/* Check if the core_id found is valid */
539 		if (pcpu_sysctl[i].core_id == INVALID_ID) {
540 			continue;
541 		}
542 
543 		/* Add core id info */
544 		SYSCTL_ADD_INT(&pcpu_sysctl[i].sysctl_ctx,
545 		    SYSCTL_CHILDREN(pcpu_sysctl[i].sysctl_tree),
546 		    OID_AUTO, "core_id", CTLFLAG_RD,
547 		    &pcpu_sysctl[i].core_id, 0,
548 		    "Core ID");
549 
550 		/*Add core siblings */
551 		SYSCTL_ADD_STRING(&pcpu_sysctl[i].sysctl_ctx,
552 		    SYSCTL_CHILDREN(pcpu_sysctl[i].sysctl_tree),
553 		    OID_AUTO, "core_siblings", CTLFLAG_RD,
554 		    pcpu_sysctl[i].core_siblings, 0,
555 		    "Core siblings");
556 	}
557 }
558 
559 /* Build the CPU Topology and SYSCTL Topology tree */
560 static void
561 init_cpu_topology(void)
562 {
563 	cpu_root_node = build_cpu_topology();
564 
565 	init_pcpu_topology_sysctl();
566 	build_sysctl_cpu_topology();
567 }
568 SYSINIT(cpu_topology, SI_BOOT2_CPU_TOPOLOGY, SI_ORDER_FIRST,
569     init_cpu_topology, NULL)
570