1 /* $OpenBSD: kern_sched.c,v 1.59 2019/10/15 10:05:43 mpi Exp $ */ 2 /* 3 * Copyright (c) 2007, 2008 Artur Grabowski <art@openbsd.org> 4 * 5 * Permission to use, copy, modify, and distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 */ 17 18 #include <sys/param.h> 19 20 #include <sys/sched.h> 21 #include <sys/proc.h> 22 #include <sys/kthread.h> 23 #include <sys/systm.h> 24 #include <sys/resourcevar.h> 25 #include <sys/signalvar.h> 26 #include <sys/mutex.h> 27 #include <sys/task.h> 28 #include <sys/smr.h> 29 30 #include <uvm/uvm_extern.h> 31 32 void sched_kthreads_create(void *); 33 34 int sched_proc_to_cpu_cost(struct cpu_info *ci, struct proc *p); 35 struct proc *sched_steal_proc(struct cpu_info *); 36 37 /* 38 * To help choosing which cpu should run which process we keep track 39 * of cpus which are currently idle and which cpus have processes 40 * queued. 41 */ 42 struct cpuset sched_idle_cpus; 43 struct cpuset sched_queued_cpus; 44 struct cpuset sched_all_cpus; 45 46 /* 47 * Some general scheduler counters. 48 */ 49 uint64_t sched_nmigrations; /* Cpu migration counter */ 50 uint64_t sched_nomigrations; /* Cpu no migration counter */ 51 uint64_t sched_noidle; /* Times we didn't pick the idle task */ 52 uint64_t sched_stolen; /* Times we stole proc from other cpus */ 53 uint64_t sched_choose; /* Times we chose a cpu */ 54 uint64_t sched_wasidle; /* Times we came out of idle */ 55 56 #ifdef MULTIPROCESSOR 57 struct taskq *sbartq; 58 #endif 59 60 int sched_smt; 61 62 /* 63 * A few notes about cpu_switchto that is implemented in MD code. 64 * 65 * cpu_switchto takes two arguments, the old proc and the proc 66 * it should switch to. The new proc will never be NULL, so we always have 67 * a saved state that we need to switch to. The old proc however can 68 * be NULL if the process is exiting. NULL for the old proc simply 69 * means "don't bother saving old state". 70 * 71 * cpu_switchto is supposed to atomically load the new state of the process 72 * including the pcb, pmap and setting curproc, the p_cpu pointer in the 73 * proc and p_stat to SONPROC. Atomically with respect to interrupts, other 74 * cpus in the system must not depend on this state being consistent. 75 * Therefore no locking is necessary in cpu_switchto other than blocking 76 * interrupts during the context switch. 77 */ 78 79 /* 80 * sched_init_cpu is called from main() for the boot cpu, then it's the 81 * responsibility of the MD code to call it for all other cpus. 82 */ 83 void 84 sched_init_cpu(struct cpu_info *ci) 85 { 86 struct schedstate_percpu *spc = &ci->ci_schedstate; 87 int i; 88 89 for (i = 0; i < SCHED_NQS; i++) 90 TAILQ_INIT(&spc->spc_qs[i]); 91 92 spc->spc_idleproc = NULL; 93 94 kthread_create_deferred(sched_kthreads_create, ci); 95 96 LIST_INIT(&spc->spc_deadproc); 97 SIMPLEQ_INIT(&spc->spc_deferred); 98 99 /* 100 * Slight hack here until the cpuset code handles cpu_info 101 * structures. 102 */ 103 cpuset_init_cpu(ci); 104 105 #ifdef __HAVE_CPU_TOPOLOGY 106 if (!sched_smt && ci->ci_smt_id > 0) 107 return; 108 #endif 109 cpuset_add(&sched_all_cpus, ci); 110 } 111 112 void 113 sched_kthreads_create(void *v) 114 { 115 struct cpu_info *ci = v; 116 struct schedstate_percpu *spc = &ci->ci_schedstate; 117 static int num; 118 119 if (fork1(&proc0, FORK_SHAREVM|FORK_SHAREFILES|FORK_NOZOMBIE| 120 FORK_SYSTEM|FORK_SIGHAND|FORK_IDLE, sched_idle, ci, NULL, 121 &spc->spc_idleproc)) 122 panic("fork idle"); 123 124 /* Name it as specified. */ 125 snprintf(spc->spc_idleproc->p_p->ps_comm, 126 sizeof(spc->spc_idleproc->p_p->ps_comm), 127 "idle%d", num); 128 129 num++; 130 } 131 132 void 133 sched_idle(void *v) 134 { 135 struct schedstate_percpu *spc; 136 struct proc *p = curproc; 137 struct cpu_info *ci = v; 138 int s; 139 140 KERNEL_UNLOCK(); 141 142 spc = &ci->ci_schedstate; 143 144 /* 145 * First time we enter here, we're not supposed to idle, 146 * just go away for a while. 147 */ 148 SCHED_LOCK(s); 149 cpuset_add(&sched_idle_cpus, ci); 150 p->p_stat = SSLEEP; 151 p->p_cpu = ci; 152 atomic_setbits_int(&p->p_flag, P_CPUPEG); 153 mi_switch(); 154 cpuset_del(&sched_idle_cpus, ci); 155 SCHED_UNLOCK(s); 156 157 KASSERT(ci == curcpu()); 158 KASSERT(curproc == spc->spc_idleproc); 159 160 while (1) { 161 while (!cpu_is_idle(curcpu())) { 162 struct proc *dead; 163 164 SCHED_LOCK(s); 165 p->p_stat = SSLEEP; 166 mi_switch(); 167 SCHED_UNLOCK(s); 168 169 while ((dead = LIST_FIRST(&spc->spc_deadproc))) { 170 LIST_REMOVE(dead, p_hash); 171 exit2(dead); 172 } 173 } 174 175 splassert(IPL_NONE); 176 177 smr_idle(); 178 179 cpuset_add(&sched_idle_cpus, ci); 180 cpu_idle_enter(); 181 while (spc->spc_whichqs == 0) { 182 #ifdef MULTIPROCESSOR 183 if (spc->spc_schedflags & SPCF_SHOULDHALT && 184 (spc->spc_schedflags & SPCF_HALTED) == 0) { 185 cpuset_del(&sched_idle_cpus, ci); 186 SCHED_LOCK(s); 187 atomic_setbits_int(&spc->spc_schedflags, 188 spc->spc_whichqs ? 0 : SPCF_HALTED); 189 SCHED_UNLOCK(s); 190 wakeup(spc); 191 } 192 #endif 193 cpu_idle_cycle(); 194 } 195 cpu_idle_leave(); 196 cpuset_del(&sched_idle_cpus, ci); 197 } 198 } 199 200 /* 201 * To free our address space we have to jump through a few hoops. 202 * The freeing is done by the reaper, but until we have one reaper 203 * per cpu, we have no way of putting this proc on the deadproc list 204 * and waking up the reaper without risking having our address space and 205 * stack torn from under us before we manage to switch to another proc. 206 * Therefore we have a per-cpu list of dead processes where we put this 207 * proc and have idle clean up that list and move it to the reaper list. 208 * All this will be unnecessary once we can bind the reaper this cpu 209 * and not risk having it switch to another in case it sleeps. 210 */ 211 void 212 sched_exit(struct proc *p) 213 { 214 struct schedstate_percpu *spc = &curcpu()->ci_schedstate; 215 struct timespec ts; 216 struct proc *idle; 217 int s; 218 219 nanouptime(&ts); 220 timespecsub(&ts, &spc->spc_runtime, &ts); 221 timespecadd(&p->p_rtime, &ts, &p->p_rtime); 222 223 LIST_INSERT_HEAD(&spc->spc_deadproc, p, p_hash); 224 225 #ifdef MULTIPROCESSOR 226 /* This process no longer needs to hold the kernel lock. */ 227 KERNEL_ASSERT_LOCKED(); 228 __mp_release_all(&kernel_lock); 229 #endif 230 231 SCHED_LOCK(s); 232 idle = spc->spc_idleproc; 233 idle->p_stat = SRUN; 234 cpu_switchto(NULL, idle); 235 panic("cpu_switchto returned"); 236 } 237 238 /* 239 * Run queue management. 240 */ 241 void 242 sched_init_runqueues(void) 243 { 244 } 245 246 void 247 setrunqueue(struct cpu_info *ci, struct proc *p, uint8_t prio) 248 { 249 struct schedstate_percpu *spc; 250 int queue = prio >> 2; 251 252 if (ci == NULL) 253 ci = sched_choosecpu(p); 254 255 KASSERT(ci != NULL); 256 SCHED_ASSERT_LOCKED(); 257 258 p->p_cpu = ci; 259 p->p_stat = SRUN; 260 p->p_priority = prio; 261 262 spc = &p->p_cpu->ci_schedstate; 263 spc->spc_nrun++; 264 265 TAILQ_INSERT_TAIL(&spc->spc_qs[queue], p, p_runq); 266 spc->spc_whichqs |= (1 << queue); 267 cpuset_add(&sched_queued_cpus, p->p_cpu); 268 269 if (cpuset_isset(&sched_idle_cpus, p->p_cpu)) 270 cpu_unidle(p->p_cpu); 271 } 272 273 void 274 remrunqueue(struct proc *p) 275 { 276 struct schedstate_percpu *spc; 277 int queue = p->p_priority >> 2; 278 279 SCHED_ASSERT_LOCKED(); 280 spc = &p->p_cpu->ci_schedstate; 281 spc->spc_nrun--; 282 283 TAILQ_REMOVE(&spc->spc_qs[queue], p, p_runq); 284 if (TAILQ_EMPTY(&spc->spc_qs[queue])) { 285 spc->spc_whichqs &= ~(1 << queue); 286 if (spc->spc_whichqs == 0) 287 cpuset_del(&sched_queued_cpus, p->p_cpu); 288 } 289 } 290 291 struct proc * 292 sched_chooseproc(void) 293 { 294 struct schedstate_percpu *spc = &curcpu()->ci_schedstate; 295 struct proc *p; 296 int queue; 297 298 SCHED_ASSERT_LOCKED(); 299 300 #ifdef MULTIPROCESSOR 301 if (spc->spc_schedflags & SPCF_SHOULDHALT) { 302 if (spc->spc_whichqs) { 303 for (queue = 0; queue < SCHED_NQS; queue++) { 304 while ((p = TAILQ_FIRST(&spc->spc_qs[queue]))) { 305 remrunqueue(p); 306 setrunqueue(NULL, p, p->p_priority); 307 if (p->p_cpu == curcpu()) { 308 KASSERT(p->p_flag & P_CPUPEG); 309 goto again; 310 } 311 } 312 } 313 } 314 p = spc->spc_idleproc; 315 KASSERT(p); 316 KASSERT(p->p_wchan == NULL); 317 p->p_stat = SRUN; 318 return (p); 319 } 320 #endif 321 322 again: 323 if (spc->spc_whichqs) { 324 queue = ffs(spc->spc_whichqs) - 1; 325 p = TAILQ_FIRST(&spc->spc_qs[queue]); 326 remrunqueue(p); 327 sched_noidle++; 328 if (p->p_stat != SRUN) 329 panic("thread %d not in SRUN: %d", p->p_tid, p->p_stat); 330 } else if ((p = sched_steal_proc(curcpu())) == NULL) { 331 p = spc->spc_idleproc; 332 if (p == NULL) { 333 int s; 334 /* 335 * We get here if someone decides to switch during 336 * boot before forking kthreads, bleh. 337 * This is kind of like a stupid idle loop. 338 */ 339 #ifdef MULTIPROCESSOR 340 __mp_unlock(&sched_lock); 341 #endif 342 spl0(); 343 delay(10); 344 SCHED_LOCK(s); 345 goto again; 346 } 347 KASSERT(p); 348 p->p_stat = SRUN; 349 } 350 351 KASSERT(p->p_wchan == NULL); 352 return (p); 353 } 354 355 struct cpu_info * 356 sched_choosecpu_fork(struct proc *parent, int flags) 357 { 358 #ifdef MULTIPROCESSOR 359 struct cpu_info *choice = NULL; 360 fixpt_t load, best_load = ~0; 361 int run, best_run = INT_MAX; 362 struct cpu_info *ci; 363 struct cpuset set; 364 365 #if 0 366 /* 367 * XXX 368 * Don't do this until we have a painless way to move the cpu in exec. 369 * Preferably when nuking the old pmap and getting a new one on a 370 * new cpu. 371 */ 372 /* 373 * PPWAIT forks are simple. We know that the parent will not 374 * run until we exec and choose another cpu, so we just steal its 375 * cpu. 376 */ 377 if (flags & FORK_PPWAIT) 378 return (parent->p_cpu); 379 #endif 380 381 /* 382 * Look at all cpus that are currently idle and have nothing queued. 383 * If there are none, pick the one with least queued procs first, 384 * then the one with lowest load average. 385 */ 386 cpuset_complement(&set, &sched_queued_cpus, &sched_idle_cpus); 387 cpuset_intersection(&set, &set, &sched_all_cpus); 388 if (cpuset_first(&set) == NULL) 389 cpuset_copy(&set, &sched_all_cpus); 390 391 while ((ci = cpuset_first(&set)) != NULL) { 392 cpuset_del(&set, ci); 393 394 load = ci->ci_schedstate.spc_ldavg; 395 run = ci->ci_schedstate.spc_nrun; 396 397 if (choice == NULL || run < best_run || 398 (run == best_run &&load < best_load)) { 399 choice = ci; 400 best_load = load; 401 best_run = run; 402 } 403 } 404 405 return (choice); 406 #else 407 return (curcpu()); 408 #endif 409 } 410 411 struct cpu_info * 412 sched_choosecpu(struct proc *p) 413 { 414 #ifdef MULTIPROCESSOR 415 struct cpu_info *choice = NULL; 416 int last_cost = INT_MAX; 417 struct cpu_info *ci; 418 struct cpuset set; 419 420 /* 421 * If pegged to a cpu, don't allow it to move. 422 */ 423 if (p->p_flag & P_CPUPEG) 424 return (p->p_cpu); 425 426 sched_choose++; 427 428 /* 429 * Look at all cpus that are currently idle and have nothing queued. 430 * If there are none, pick the cheapest of those. 431 * (idle + queued could mean that the cpu is handling an interrupt 432 * at this moment and haven't had time to leave idle yet). 433 */ 434 cpuset_complement(&set, &sched_queued_cpus, &sched_idle_cpus); 435 cpuset_intersection(&set, &set, &sched_all_cpus); 436 437 /* 438 * First, just check if our current cpu is in that set, if it is, 439 * this is simple. 440 * Also, our cpu might not be idle, but if it's the current cpu 441 * and it has nothing else queued and we're curproc, take it. 442 */ 443 if (cpuset_isset(&set, p->p_cpu) || 444 (p->p_cpu == curcpu() && p->p_cpu->ci_schedstate.spc_nrun == 0 && 445 (p->p_cpu->ci_schedstate.spc_schedflags & SPCF_SHOULDHALT) == 0 && 446 curproc == p)) { 447 sched_wasidle++; 448 return (p->p_cpu); 449 } 450 451 if (cpuset_first(&set) == NULL) 452 cpuset_copy(&set, &sched_all_cpus); 453 454 while ((ci = cpuset_first(&set)) != NULL) { 455 int cost = sched_proc_to_cpu_cost(ci, p); 456 457 if (choice == NULL || cost < last_cost) { 458 choice = ci; 459 last_cost = cost; 460 } 461 cpuset_del(&set, ci); 462 } 463 464 if (p->p_cpu != choice) 465 sched_nmigrations++; 466 else 467 sched_nomigrations++; 468 469 return (choice); 470 #else 471 return (curcpu()); 472 #endif 473 } 474 475 /* 476 * Attempt to steal a proc from some cpu. 477 */ 478 struct proc * 479 sched_steal_proc(struct cpu_info *self) 480 { 481 struct proc *best = NULL; 482 #ifdef MULTIPROCESSOR 483 struct schedstate_percpu *spc; 484 int bestcost = INT_MAX; 485 struct cpu_info *ci; 486 struct cpuset set; 487 488 KASSERT((self->ci_schedstate.spc_schedflags & SPCF_SHOULDHALT) == 0); 489 490 /* Don't steal if we don't want to schedule processes in this CPU. */ 491 if (!cpuset_isset(&sched_all_cpus, self)) 492 return (NULL); 493 494 cpuset_copy(&set, &sched_queued_cpus); 495 496 while ((ci = cpuset_first(&set)) != NULL) { 497 struct proc *p; 498 int queue; 499 int cost; 500 501 cpuset_del(&set, ci); 502 503 spc = &ci->ci_schedstate; 504 505 queue = ffs(spc->spc_whichqs) - 1; 506 TAILQ_FOREACH(p, &spc->spc_qs[queue], p_runq) { 507 if (p->p_flag & P_CPUPEG) 508 continue; 509 510 cost = sched_proc_to_cpu_cost(self, p); 511 512 if (best == NULL || cost < bestcost) { 513 best = p; 514 bestcost = cost; 515 } 516 } 517 } 518 if (best == NULL) 519 return (NULL); 520 521 spc = &best->p_cpu->ci_schedstate; 522 remrunqueue(best); 523 best->p_cpu = self; 524 525 sched_stolen++; 526 #endif 527 return (best); 528 } 529 530 #ifdef MULTIPROCESSOR 531 /* 532 * Base 2 logarithm of an int. returns 0 for 0 (yeye, I know). 533 */ 534 static int 535 log2(unsigned int i) 536 { 537 int ret = 0; 538 539 while (i >>= 1) 540 ret++; 541 542 return (ret); 543 } 544 545 /* 546 * Calculate the cost of moving the proc to this cpu. 547 * 548 * What we want is some guesstimate of how much "performance" it will 549 * cost us to move the proc here. Not just for caches and TLBs and NUMA 550 * memory, but also for the proc itself. A highly loaded cpu might not 551 * be the best candidate for this proc since it won't get run. 552 * 553 * Just total guesstimates for now. 554 */ 555 556 int sched_cost_load = 1; 557 int sched_cost_priority = 1; 558 int sched_cost_runnable = 3; 559 int sched_cost_resident = 1; 560 #endif 561 562 int 563 sched_proc_to_cpu_cost(struct cpu_info *ci, struct proc *p) 564 { 565 int cost = 0; 566 #ifdef MULTIPROCESSOR 567 struct schedstate_percpu *spc; 568 int l2resident = 0; 569 570 spc = &ci->ci_schedstate; 571 572 /* 573 * First, account for the priority of the proc we want to move. 574 * More willing to move, the lower the priority of the destination 575 * and the higher the priority of the proc. 576 */ 577 if (!cpuset_isset(&sched_idle_cpus, ci)) { 578 cost += (p->p_priority - spc->spc_curpriority) * 579 sched_cost_priority; 580 cost += sched_cost_runnable; 581 } 582 if (cpuset_isset(&sched_queued_cpus, ci)) 583 cost += spc->spc_nrun * sched_cost_runnable; 584 585 /* 586 * Try to avoid the primary cpu as it handles hardware interrupts. 587 * 588 * XXX Needs to be revisited when we distribute interrupts 589 * over cpus. 590 */ 591 if (CPU_IS_PRIMARY(ci)) 592 cost += sched_cost_runnable; 593 594 /* 595 * Higher load on the destination means we don't want to go there. 596 */ 597 cost += ((sched_cost_load * spc->spc_ldavg) >> FSHIFT); 598 599 /* 600 * If the proc is on this cpu already, lower the cost by how much 601 * it has been running and an estimate of its footprint. 602 */ 603 if (p->p_cpu == ci && p->p_slptime == 0) { 604 l2resident = 605 log2(pmap_resident_count(p->p_vmspace->vm_map.pmap)); 606 cost -= l2resident * sched_cost_resident; 607 } 608 #endif 609 return (cost); 610 } 611 612 /* 613 * Peg a proc to a cpu. 614 */ 615 void 616 sched_peg_curproc(struct cpu_info *ci) 617 { 618 struct proc *p = curproc; 619 int s; 620 621 SCHED_LOCK(s); 622 atomic_setbits_int(&p->p_flag, P_CPUPEG); 623 setrunqueue(ci, p, p->p_usrpri); 624 p->p_ru.ru_nvcsw++; 625 mi_switch(); 626 SCHED_UNLOCK(s); 627 } 628 629 #ifdef MULTIPROCESSOR 630 631 void 632 sched_start_secondary_cpus(void) 633 { 634 CPU_INFO_ITERATOR cii; 635 struct cpu_info *ci; 636 637 CPU_INFO_FOREACH(cii, ci) { 638 struct schedstate_percpu *spc = &ci->ci_schedstate; 639 640 if (CPU_IS_PRIMARY(ci)) 641 continue; 642 atomic_clearbits_int(&spc->spc_schedflags, 643 SPCF_SHOULDHALT | SPCF_HALTED); 644 #ifdef __HAVE_CPU_TOPOLOGY 645 if (!sched_smt && ci->ci_smt_id > 0) 646 continue; 647 #endif 648 cpuset_add(&sched_all_cpus, ci); 649 } 650 } 651 652 void 653 sched_stop_secondary_cpus(void) 654 { 655 CPU_INFO_ITERATOR cii; 656 struct cpu_info *ci; 657 658 /* 659 * Make sure we stop the secondary CPUs. 660 */ 661 CPU_INFO_FOREACH(cii, ci) { 662 struct schedstate_percpu *spc = &ci->ci_schedstate; 663 664 if (CPU_IS_PRIMARY(ci)) 665 continue; 666 cpuset_del(&sched_all_cpus, ci); 667 atomic_setbits_int(&spc->spc_schedflags, SPCF_SHOULDHALT); 668 } 669 CPU_INFO_FOREACH(cii, ci) { 670 struct schedstate_percpu *spc = &ci->ci_schedstate; 671 struct sleep_state sls; 672 673 if (CPU_IS_PRIMARY(ci)) 674 continue; 675 while ((spc->spc_schedflags & SPCF_HALTED) == 0) { 676 sleep_setup(&sls, spc, PZERO, "schedstate"); 677 sleep_finish(&sls, 678 (spc->spc_schedflags & SPCF_HALTED) == 0); 679 } 680 } 681 } 682 683 struct sched_barrier_state { 684 struct cpu_info *ci; 685 struct cond cond; 686 }; 687 688 void 689 sched_barrier_task(void *arg) 690 { 691 struct sched_barrier_state *sb = arg; 692 struct cpu_info *ci = sb->ci; 693 694 sched_peg_curproc(ci); 695 cond_signal(&sb->cond); 696 atomic_clearbits_int(&curproc->p_flag, P_CPUPEG); 697 } 698 699 void 700 sched_barrier(struct cpu_info *ci) 701 { 702 struct sched_barrier_state sb; 703 struct task task; 704 CPU_INFO_ITERATOR cii; 705 706 if (ci == NULL) { 707 CPU_INFO_FOREACH(cii, ci) { 708 if (CPU_IS_PRIMARY(ci)) 709 break; 710 } 711 } 712 KASSERT(ci != NULL); 713 714 if (ci == curcpu()) 715 return; 716 717 sb.ci = ci; 718 cond_init(&sb.cond); 719 task_set(&task, sched_barrier_task, &sb); 720 721 task_add(systqmp, &task); 722 cond_wait(&sb.cond, "sbar"); 723 } 724 725 #else 726 727 void 728 sched_barrier(struct cpu_info *ci) 729 { 730 } 731 732 #endif 733 734 /* 735 * Functions to manipulate cpu sets. 736 */ 737 struct cpu_info *cpuset_infos[MAXCPUS]; 738 static struct cpuset cpuset_all; 739 740 void 741 cpuset_init_cpu(struct cpu_info *ci) 742 { 743 cpuset_add(&cpuset_all, ci); 744 cpuset_infos[CPU_INFO_UNIT(ci)] = ci; 745 } 746 747 void 748 cpuset_clear(struct cpuset *cs) 749 { 750 memset(cs, 0, sizeof(*cs)); 751 } 752 753 void 754 cpuset_add(struct cpuset *cs, struct cpu_info *ci) 755 { 756 unsigned int num = CPU_INFO_UNIT(ci); 757 atomic_setbits_int(&cs->cs_set[num/32], (1 << (num % 32))); 758 } 759 760 void 761 cpuset_del(struct cpuset *cs, struct cpu_info *ci) 762 { 763 unsigned int num = CPU_INFO_UNIT(ci); 764 atomic_clearbits_int(&cs->cs_set[num/32], (1 << (num % 32))); 765 } 766 767 int 768 cpuset_isset(struct cpuset *cs, struct cpu_info *ci) 769 { 770 unsigned int num = CPU_INFO_UNIT(ci); 771 return (cs->cs_set[num/32] & (1 << (num % 32))); 772 } 773 774 void 775 cpuset_add_all(struct cpuset *cs) 776 { 777 cpuset_copy(cs, &cpuset_all); 778 } 779 780 void 781 cpuset_copy(struct cpuset *to, struct cpuset *from) 782 { 783 memcpy(to, from, sizeof(*to)); 784 } 785 786 struct cpu_info * 787 cpuset_first(struct cpuset *cs) 788 { 789 int i; 790 791 for (i = 0; i < CPUSET_ASIZE(ncpus); i++) 792 if (cs->cs_set[i]) 793 return (cpuset_infos[i * 32 + ffs(cs->cs_set[i]) - 1]); 794 795 return (NULL); 796 } 797 798 void 799 cpuset_union(struct cpuset *to, struct cpuset *a, struct cpuset *b) 800 { 801 int i; 802 803 for (i = 0; i < CPUSET_ASIZE(ncpus); i++) 804 to->cs_set[i] = a->cs_set[i] | b->cs_set[i]; 805 } 806 807 void 808 cpuset_intersection(struct cpuset *to, struct cpuset *a, struct cpuset *b) 809 { 810 int i; 811 812 for (i = 0; i < CPUSET_ASIZE(ncpus); i++) 813 to->cs_set[i] = a->cs_set[i] & b->cs_set[i]; 814 } 815 816 void 817 cpuset_complement(struct cpuset *to, struct cpuset *a, struct cpuset *b) 818 { 819 int i; 820 821 for (i = 0; i < CPUSET_ASIZE(ncpus); i++) 822 to->cs_set[i] = b->cs_set[i] & ~a->cs_set[i]; 823 } 824 825 int 826 cpuset_cardinality(struct cpuset *cs) 827 { 828 int cardinality, i, n; 829 830 cardinality = 0; 831 832 for (i = 0; i < CPUSET_ASIZE(ncpus); i++) 833 for (n = cs->cs_set[i]; n != 0; n &= n - 1) 834 cardinality++; 835 836 return (cardinality); 837 } 838 839 int 840 sysctl_hwncpuonline(void) 841 { 842 return cpuset_cardinality(&sched_all_cpus); 843 } 844 845 int 846 cpu_is_online(struct cpu_info *ci) 847 { 848 return cpuset_isset(&sched_all_cpus, ci); 849 } 850 851 #ifdef __HAVE_CPU_TOPOLOGY 852 853 #include <sys/sysctl.h> 854 855 int 856 sysctl_hwsmt(void *oldp, size_t *oldlenp, void *newp, size_t newlen) 857 { 858 CPU_INFO_ITERATOR cii; 859 struct cpu_info *ci; 860 int err, newsmt; 861 862 newsmt = sched_smt; 863 err = sysctl_int(oldp, oldlenp, newp, newlen, &newsmt); 864 if (err) 865 return err; 866 if (newsmt > 1) 867 newsmt = 1; 868 if (newsmt < 0) 869 newsmt = 0; 870 if (newsmt == sched_smt) 871 return 0; 872 873 sched_smt = newsmt; 874 CPU_INFO_FOREACH(cii, ci) { 875 if (CPU_IS_PRIMARY(ci)) 876 continue; 877 if (ci->ci_smt_id == 0) 878 continue; 879 if (sched_smt) 880 cpuset_add(&sched_all_cpus, ci); 881 else 882 cpuset_del(&sched_all_cpus, ci); 883 } 884 885 return 0; 886 } 887 888 #endif 889