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