1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #include <sys/cpu_pm.h> 27 #include <sys/cmn_err.h> 28 #include <sys/time.h> 29 #include <sys/sdt.h> 30 31 /* 32 * Solaris Event Based CPU Power Manager 33 * 34 * This file implements platform independent event based CPU power management. 35 * When CPUs are configured into the system, the CMT scheduling subsystem will 36 * query the platform to determine if the CPU belongs to any power management 37 * domains. That is, sets of CPUs that share power management states. 38 * 39 * Active Power Management domains represent a group of CPUs across which the 40 * Operating System can request speed changes (which may in turn result 41 * in voltage changes). This allows the operating system to trade off 42 * performance for power savings. 43 * 44 * Idle Power Management domains can enter power savings states when they are 45 * unutilized. These states allow the Operating System to trade off power 46 * for performance (in the form of latency to transition from the idle state 47 * to an active one). 48 * 49 * For each active and idle power domain the CMT subsystem instantiates, a 50 * cpupm_domain_t structure is created. As the dispatcher schedules threads 51 * to run on the system's CPUs, it will also track the utilization of the 52 * enumerated power domains. Significant changes in utilization will result 53 * in the dispatcher sending the power manager events that relate to the 54 * utilization of the power domain. The power manager recieves the events, 55 * and in the context of the policy objectives in force, may decide to request 56 * the domain's power/performance state be changed. 57 * 58 * Under the "elastic" CPUPM policy, when the utilization rises, the CPU power 59 * manager will request the CPUs in the domain run at their fastest (and most 60 * power consuming) state. When the domain becomes idle (utilization at zero), 61 * the power manager will request that the CPUs run at a speed that saves the 62 * most power. 63 * 64 * The advantage of this scheme, is that the CPU power manager working with the 65 * dispatcher can be extremely responsive to changes in utilization. Optimizing 66 * for performance in the presence of utilization, and power savings in the 67 * presence of idleness. Such close collaboration with the dispatcher has other 68 * benefits that will play out in the form of more sophisticated power / 69 * performance policy in the near future. 70 * 71 * Avoiding state thrashing in the presence of transient periods of utilization 72 * and idleness while still being responsive to non-transient periods is key. 73 * The power manager implements a "governor" that is used to throttle 74 * state transitions when a significant amount of transient idle or transient 75 * work is detected. 76 * 77 * Kernel background activity (e.g. taskq threads) are by far the most common 78 * form of transient utilization. Ungoverned in the face of this utililzation, 79 * hundreds of state transitions per second would result on an idle system. 80 * 81 * Transient idleness is common when a thread briefly yields the CPU to 82 * wait for an event elsewhere in the system. Where the idle period is short 83 * enough, the overhead associated with making the state transition doesn't 84 * justify the power savings. 85 * 86 * The following is the state machine for the governor implemented by 87 * cpupm_utilization_event(): 88 * 89 * ----->---tw---->----- 90 * / \ 91 * (I)-<-ti-<- -<-ntw-<(W) 92 * | \ / | 93 * \ \ / / 94 * >-nti/rm->(D)--->-tw->- 95 * Key: 96 * 97 * States 98 * - (D): Default (ungoverned) 99 * - (W): Transient work governed 100 * - (I): Transient idle governed 101 * State Transitions 102 * - tw: transient work 103 * - ti: transient idleness 104 * - ntw: non-transient work 105 * - nti: non-transient idleness 106 * - rm: thread remain event 107 */ 108 109 static cpupm_domain_t *cpupm_domains = NULL; 110 111 /* 112 * Uninitialized state of CPU power management is disabled 113 */ 114 cpupm_policy_t cpupm_policy = CPUPM_POLICY_DISABLED; 115 116 /* 117 * Periods of utilization lasting less than this time interval are characterized 118 * as transient. State changes associated with transient work are considered 119 * to be mispredicted. That is, it's not worth raising and lower power states 120 * where the utilization lasts for less than this interval. 121 */ 122 hrtime_t cpupm_tw_predict_interval; 123 124 /* 125 * Periods of idleness lasting less than this time interval are characterized 126 * as transient. State changes associated with transient idle are considered 127 * to be mispredicted. That is, it's not worth lowering and raising power 128 * states where the idleness lasts for less than this interval. 129 */ 130 hrtime_t cpupm_ti_predict_interval; 131 132 /* 133 * Number of mispredictions after which future transitions will be governed. 134 */ 135 int cpupm_mispredict_thresh = 4; 136 137 /* 138 * Likewise, the number of mispredicted governed transitions after which the 139 * governor will be removed. 140 */ 141 int cpupm_mispredict_gov_thresh = 4; 142 143 /* 144 * The transient work and transient idle prediction intervals are specified 145 * here. Tuning them higher will result in the transient work, and transient 146 * idle governors being used more aggresively, which limits the frequency of 147 * state transitions at the expense of performance and power savings, 148 * respectively. The intervals are specified in nanoseconds. 149 */ 150 /* 151 * 400 usec 152 */ 153 #define CPUPM_DEFAULT_TI_INTERVAL 400000 154 /* 155 * 400 usec 156 */ 157 #define CPUPM_DEFAULT_TW_INTERVAL 400000 158 159 hrtime_t cpupm_ti_gov_interval = CPUPM_DEFAULT_TI_INTERVAL; 160 hrtime_t cpupm_tw_gov_interval = CPUPM_DEFAULT_TW_INTERVAL; 161 162 163 static void cpupm_governor_initialize(void); 164 static void cpupm_state_change_global(cpupm_dtype_t, cpupm_state_name_t); 165 166 cpupm_policy_t 167 cpupm_get_policy(void) 168 { 169 return (cpupm_policy); 170 } 171 172 int 173 cpupm_set_policy(cpupm_policy_t new_policy) 174 { 175 static int gov_init = 0; 176 int result = 0; 177 178 mutex_enter(&cpu_lock); 179 if (new_policy == cpupm_policy) { 180 mutex_exit(&cpu_lock); 181 return (result); 182 } 183 184 /* 185 * Pausing CPUs causes a high priority thread to be scheduled 186 * on all other CPUs (besides the current one). This locks out 187 * other CPUs from making CPUPM state transitions. 188 */ 189 switch (new_policy) { 190 case CPUPM_POLICY_DISABLED: 191 pause_cpus(NULL, NULL); 192 cpupm_policy = CPUPM_POLICY_DISABLED; 193 start_cpus(); 194 195 result = cmt_pad_disable(PGHW_POW_ACTIVE); 196 197 /* 198 * Once PAD has been enabled, it should always be possible 199 * to disable it. 200 */ 201 ASSERT(result == 0); 202 203 /* 204 * Bring all the active power domains to the maximum 205 * performance state. 206 */ 207 cpupm_state_change_global(CPUPM_DTYPE_ACTIVE, 208 CPUPM_STATE_MAX_PERF); 209 210 break; 211 case CPUPM_POLICY_ELASTIC: 212 213 result = cmt_pad_enable(PGHW_POW_ACTIVE); 214 if (result < 0) { 215 /* 216 * Failed to enable PAD across the active power 217 * domains, which may well be because none were 218 * enumerated. 219 */ 220 break; 221 } 222 223 /* 224 * Initialize the governor parameters the first time through. 225 */ 226 if (gov_init == 0) { 227 cpupm_governor_initialize(); 228 gov_init = 1; 229 } 230 231 pause_cpus(NULL, NULL); 232 cpupm_policy = CPUPM_POLICY_ELASTIC; 233 start_cpus(); 234 235 break; 236 default: 237 cmn_err(CE_WARN, "Attempt to set unknown CPUPM policy %d\n", 238 new_policy); 239 ASSERT(0); 240 break; 241 } 242 mutex_exit(&cpu_lock); 243 244 return (result); 245 } 246 247 /* 248 * Look for an existing power domain 249 */ 250 static cpupm_domain_t * 251 cpupm_domain_find(id_t id, cpupm_dtype_t type) 252 { 253 ASSERT(MUTEX_HELD(&cpu_lock)); 254 255 cpupm_domain_t *dom; 256 257 dom = cpupm_domains; 258 while (dom != NULL) { 259 if (id == dom->cpd_id && type == dom->cpd_type) 260 return (dom); 261 dom = dom->cpd_next; 262 } 263 return (NULL); 264 } 265 266 /* 267 * Create a new domain 268 */ 269 static cpupm_domain_t * 270 cpupm_domain_create(id_t id, cpupm_dtype_t type) 271 { 272 cpupm_domain_t *dom; 273 274 ASSERT(MUTEX_HELD(&cpu_lock)); 275 276 dom = kmem_zalloc(sizeof (cpupm_domain_t), KM_SLEEP); 277 dom->cpd_id = id; 278 dom->cpd_type = type; 279 280 /* Link into the known domain list */ 281 dom->cpd_next = cpupm_domains; 282 cpupm_domains = dom; 283 284 return (dom); 285 } 286 287 static void 288 cpupm_domain_state_enum(struct cpu *cp, cpupm_domain_t *dom) 289 { 290 /* 291 * In the envent we're enumerating because the domain's state 292 * configuration has changed, toss any existing states. 293 */ 294 if (dom->cpd_nstates > 0) { 295 kmem_free(dom->cpd_states, 296 sizeof (cpupm_state_t) * dom->cpd_nstates); 297 dom->cpd_nstates = 0; 298 } 299 300 /* 301 * Query to determine the number of states, allocate storage 302 * large enough to hold the state information, and pass it back 303 * to the platform driver to complete the enumeration. 304 */ 305 dom->cpd_nstates = cpupm_plat_state_enumerate(cp, dom->cpd_type, NULL); 306 307 if (dom->cpd_nstates == 0) 308 return; 309 310 dom->cpd_states = 311 kmem_zalloc(dom->cpd_nstates * sizeof (cpupm_state_t), KM_SLEEP); 312 (void) cpupm_plat_state_enumerate(cp, dom->cpd_type, dom->cpd_states); 313 } 314 315 /* 316 * Initialize the specified type of power domain on behalf of the CPU 317 */ 318 cpupm_domain_t * 319 cpupm_domain_init(struct cpu *cp, cpupm_dtype_t type) 320 { 321 cpupm_domain_t *dom; 322 id_t did; 323 324 ASSERT(MUTEX_HELD(&cpu_lock)); 325 326 /* 327 * Instantiate the domain if it doesn't already exist 328 * and enumerate its power states. 329 */ 330 did = cpupm_domain_id(cp, type); 331 dom = cpupm_domain_find(did, type); 332 if (dom == NULL) { 333 dom = cpupm_domain_create(did, type); 334 cpupm_domain_state_enum(cp, dom); 335 } 336 337 /* 338 * Named state initialization 339 */ 340 if (type == CPUPM_DTYPE_ACTIVE) { 341 /* 342 * For active power domains, the highest performance 343 * state is defined as first state returned from 344 * the domain enumeration. 345 */ 346 dom->cpd_named_states[CPUPM_STATE_MAX_PERF] = 347 &dom->cpd_states[0]; 348 dom->cpd_named_states[CPUPM_STATE_LOW_POWER] = 349 &dom->cpd_states[dom->cpd_nstates - 1]; 350 351 /* 352 * Begin by assuming CPU is running at the max perf state. 353 */ 354 dom->cpd_state = dom->cpd_named_states[CPUPM_STATE_MAX_PERF]; 355 } 356 357 return (dom); 358 } 359 360 /* 361 * Return the id associated with the given type of domain 362 * to which cp belongs 363 */ 364 id_t 365 cpupm_domain_id(struct cpu *cp, cpupm_dtype_t type) 366 { 367 return (cpupm_plat_domain_id(cp, type)); 368 } 369 370 /* 371 * Initiate a state change for the specified domain on behalf of cp 372 */ 373 int 374 cpupm_change_state(struct cpu *cp, cpupm_domain_t *dom, cpupm_state_t *state) 375 { 376 if (cpupm_plat_change_state(cp, state) < 0) 377 return (-1); 378 379 DTRACE_PROBE2(cpupm__change__state, 380 cpupm_domain_t *, dom, 381 cpupm_state_t *, state); 382 383 dom->cpd_state = state; 384 return (0); 385 } 386 387 /* 388 * Interface into the CPU power manager to indicate a significant change 389 * in utilization of the specified active power domain 390 */ 391 void 392 cpupm_utilization_event(struct cpu *cp, hrtime_t now, cpupm_domain_t *dom, 393 cpupm_util_event_t event) 394 { 395 cpupm_state_t *new_state = NULL; 396 hrtime_t last; 397 398 if (cpupm_policy == CPUPM_POLICY_DISABLED) { 399 return; 400 } 401 402 /* 403 * What follows is a simple elastic power state management policy. 404 * 405 * If the utilization has become non-zero, and the domain was 406 * previously at it's lowest power state, then transition it 407 * to the highest state in the spirit of "race to idle". 408 * 409 * If the utilization has dropped to zero, then transition the 410 * domain to its lowest power state. 411 * 412 * Statistics are maintained to implement a governor to reduce state 413 * transitions resulting from either transient work, or periods of 414 * transient idleness on the domain. 415 */ 416 switch (event) { 417 case CPUPM_DOM_REMAIN_BUSY: 418 419 /* 420 * We've received an event that the domain is running a thread 421 * that's made it to the end of it's time slice. If we are at 422 * low power, then raise it. If the transient work governor 423 * is engaged, then remove it. 424 */ 425 if (dom->cpd_state == 426 dom->cpd_named_states[CPUPM_STATE_LOW_POWER]) { 427 new_state = 428 dom->cpd_named_states[CPUPM_STATE_MAX_PERF]; 429 if (dom->cpd_governor == CPUPM_GOV_TRANS_WORK) { 430 dom->cpd_governor = CPUPM_GOV_DISENGAGED; 431 dom->cpd_tw = 0; 432 } 433 } 434 break; 435 436 case CPUPM_DOM_BUSY_FROM_IDLE: 437 last = dom->cpd_last_lower; 438 dom->cpd_last_raise = now; 439 440 DTRACE_PROBE3(cpupm__raise__req, 441 cpupm_domain_t *, dom, 442 hrtime_t, last, 443 hrtime_t, now); 444 445 if (dom->cpd_state == 446 dom->cpd_named_states[CPUPM_STATE_LOW_POWER]) { 447 448 /* 449 * There's non-zero utilization, and the domain is 450 * running in the lower power state. Before we 451 * consider raising power, check if the preceeding 452 * idle period was transient in duration. 453 * 454 * If the domain is already transient work governed, 455 * then we don't bother maintaining transient idle 456 * statistics, as the presence of enough transient work 457 * can also make the domain frequently transiently idle. 458 * In this case, we still want to remain transient work 459 * governed. 460 */ 461 if (dom->cpd_governor == CPUPM_GOV_DISENGAGED) { 462 if ((now - last) < cpupm_ti_predict_interval) { 463 /* 464 * We're raising the domain power and 465 * we *just* lowered it. Consider 466 * this a mispredicted power state 467 * transition due to a transient 468 * idle period. 469 */ 470 if (++dom->cpd_ti >= 471 cpupm_mispredict_thresh) { 472 /* 473 * There's enough transient 474 * idle transitions to 475 * justify governing future 476 * lowering requests. 477 */ 478 dom->cpd_governor = 479 CPUPM_GOV_TRANS_IDLE; 480 dom->cpd_ti = 0; 481 DTRACE_PROBE1( 482 cpupm__ti__governed, 483 cpupm_domain_t *, dom); 484 } 485 } else { 486 /* 487 * We correctly predicted the last 488 * lowering. 489 */ 490 dom->cpd_ti = 0; 491 } 492 } 493 if (dom->cpd_governor == CPUPM_GOV_TRANS_WORK) { 494 /* 495 * Raise requests are governed due to 496 * transient work. 497 */ 498 DTRACE_PROBE1(cpupm__raise__governed, 499 cpupm_domain_t *, dom); 500 501 return; 502 } 503 /* 504 * Prepare to transition to the higher power state 505 */ 506 new_state = dom->cpd_named_states[CPUPM_STATE_MAX_PERF]; 507 508 } else if (dom->cpd_state == 509 dom->cpd_named_states[CPUPM_STATE_MAX_PERF]) { 510 511 /* 512 * Utilization is non-zero, and we're already running 513 * in the higher power state. Take this opportunity to 514 * perform some book keeping if the last lowering 515 * request was governed. 516 */ 517 if (dom->cpd_governor == CPUPM_GOV_TRANS_IDLE) { 518 519 if ((now - last) >= cpupm_ti_predict_interval) { 520 /* 521 * The domain is transient idle 522 * governed, and we mispredicted 523 * governing the last lowering request. 524 */ 525 if (++dom->cpd_ti >= 526 cpupm_mispredict_gov_thresh) { 527 /* 528 * There's enough non-transient 529 * idle periods to justify 530 * removing the governor. 531 */ 532 dom->cpd_governor = 533 CPUPM_GOV_DISENGAGED; 534 dom->cpd_ti = 0; 535 DTRACE_PROBE1( 536 cpupm__ti__ungoverned, 537 cpupm_domain_t *, dom); 538 } 539 } else { 540 /* 541 * Correctly predicted governing the 542 * last lowering request. 543 */ 544 dom->cpd_ti = 0; 545 } 546 } 547 } 548 break; 549 550 case CPUPM_DOM_IDLE_FROM_BUSY: 551 last = dom->cpd_last_raise; 552 dom->cpd_last_lower = now; 553 554 DTRACE_PROBE3(cpupm__lower__req, 555 cpupm_domain_t *, dom, 556 hrtime_t, last, 557 hrtime_t, now); 558 559 if (dom->cpd_state == 560 dom->cpd_named_states[CPUPM_STATE_MAX_PERF]) { 561 562 /* 563 * The domain is idle, and is running in the highest 564 * performance state. Before we consider lowering power, 565 * perform some book keeping for the transient work 566 * governor. 567 */ 568 if (dom->cpd_governor == CPUPM_GOV_DISENGAGED) { 569 if ((now - last) < cpupm_tw_predict_interval) { 570 /* 571 * We're lowering the domain power and 572 * we *just* raised it. Consider the 573 * last raise mispredicted due to 574 * transient work. 575 */ 576 if (++dom->cpd_tw >= 577 cpupm_mispredict_thresh) { 578 /* 579 * There's enough transient work 580 * transitions to justify 581 * governing future raise 582 * requests. 583 */ 584 dom->cpd_governor = 585 CPUPM_GOV_TRANS_WORK; 586 dom->cpd_tw = 0; 587 DTRACE_PROBE1( 588 cpupm__tw__governed, 589 cpupm_domain_t *, dom); 590 } 591 } else { 592 /* 593 * We correctly predicted during the 594 * last raise. 595 */ 596 dom->cpd_tw = 0; 597 } 598 } 599 if (dom->cpd_governor == CPUPM_GOV_TRANS_IDLE) { 600 /* 601 * Lowering requests are governed due to 602 * transient idleness. 603 */ 604 DTRACE_PROBE1(cpupm__lowering__governed, 605 cpupm_domain_t *, dom); 606 607 return; 608 } 609 610 /* 611 * Prepare to transition to a lower power state. 612 */ 613 new_state = 614 dom->cpd_named_states[CPUPM_STATE_LOW_POWER]; 615 616 } else if (dom->cpd_state == 617 dom->cpd_named_states[CPUPM_STATE_LOW_POWER]) { 618 619 /* 620 * The domain is idle, and we're already running in 621 * the lower power state. Take this opportunity to 622 * perform some book keeping if the last raising 623 * request was governed. 624 */ 625 if (dom->cpd_governor == CPUPM_GOV_TRANS_WORK) { 626 if ((now - last) >= cpupm_tw_predict_interval) { 627 /* 628 * The domain is transient work 629 * governed, and we mispredicted 630 * governing the last raising request. 631 */ 632 if (++dom->cpd_tw >= 633 cpupm_mispredict_gov_thresh) { 634 /* 635 * There's enough non-transient 636 * work to justify removing 637 * the governor. 638 */ 639 dom->cpd_governor = 640 CPUPM_GOV_DISENGAGED; 641 dom->cpd_tw = 0; 642 DTRACE_PROBE1( 643 cpupm__tw__ungoverned, 644 cpupm_domain_t *, dom); 645 } 646 } else { 647 /* 648 * We correctly predicted governing 649 * the last raise. 650 */ 651 dom->cpd_tw = 0; 652 } 653 } 654 } 655 break; 656 } 657 /* 658 * Change the power state 659 * Not much currently done if this doesn't succeed 660 */ 661 if (new_state) 662 (void) cpupm_change_state(cp, dom, new_state); 663 } 664 665 666 /* 667 * Interface called by platforms to dynamically change the 668 * MAX performance cpupm state 669 */ 670 void 671 cpupm_redefine_max_activepwr_state(struct cpu *cp, int max_perf_level) 672 { 673 cpupm_domain_t *dom; 674 id_t did; 675 cpupm_dtype_t type = CPUPM_DTYPE_ACTIVE; 676 boolean_t change_state = B_FALSE; 677 cpupm_state_t *new_state = NULL; 678 679 did = cpupm_domain_id(cp, type); 680 if (MUTEX_HELD(&cpu_lock)) { 681 dom = cpupm_domain_find(did, type); 682 } else { 683 mutex_enter(&cpu_lock); 684 dom = cpupm_domain_find(did, type); 685 mutex_exit(&cpu_lock); 686 } 687 688 /* 689 * Can use a lock to avoid changing the power state of the cpu when 690 * CPUPM_STATE_MAX_PERF is getting changed. 691 * Since the occurance of events to change MAX_PERF is not frequent, 692 * it may not be a good idea to overburden with locks. In the worst 693 * case, for one cycle the power may not get changed to the required 694 * level 695 */ 696 if (dom != NULL) { 697 if (dom->cpd_state == 698 dom->cpd_named_states[CPUPM_STATE_MAX_PERF]) { 699 change_state = B_TRUE; 700 } 701 702 /* 703 * If an out of range level is passed, use the lowest supported 704 * speed. 705 */ 706 if (max_perf_level >= dom->cpd_nstates && 707 dom->cpd_nstates > 1) { 708 max_perf_level = dom->cpd_nstates - 1; 709 } 710 711 dom->cpd_named_states[CPUPM_STATE_MAX_PERF] = 712 &dom->cpd_states[max_perf_level]; 713 714 /* 715 * If the current state is MAX_PERF, change the current state 716 * to the new MAX_PERF 717 */ 718 if (change_state) { 719 new_state = 720 dom->cpd_named_states[CPUPM_STATE_MAX_PERF]; 721 if (new_state) { 722 (void) cpupm_change_state(cp, dom, new_state); 723 } 724 } 725 } 726 } 727 728 /* 729 * Initialize the parameters for the transience governor state machine 730 */ 731 static void 732 cpupm_governor_initialize(void) 733 { 734 /* 735 * The default prediction intervals are specified in nanoseconds. 736 * Convert these to the equivalent in unscaled hrtime, which is the 737 * format of the timestamps passed to cpupm_utilization_event() 738 */ 739 cpupm_ti_predict_interval = unscalehrtime(cpupm_ti_gov_interval); 740 cpupm_tw_predict_interval = unscalehrtime(cpupm_tw_gov_interval); 741 } 742 743 /* 744 * Initiate a state change in all CPUPM domain instances of the specified type 745 */ 746 static void 747 cpupm_state_change_global(cpupm_dtype_t type, cpupm_state_name_t state) 748 { 749 cpu_t *cp; 750 pg_cmt_t *pwr_pg; 751 cpupm_domain_t *dom; 752 group_t *hwset; 753 group_iter_t giter; 754 pg_cpu_itr_t cpu_iter; 755 pghw_type_t hw; 756 757 ASSERT(MUTEX_HELD(&cpu_lock)); 758 759 switch (type) { 760 case CPUPM_DTYPE_ACTIVE: 761 hw = PGHW_POW_ACTIVE; 762 break; 763 default: 764 /* 765 * Power domain types other than "active" unsupported. 766 */ 767 ASSERT(type == CPUPM_DTYPE_ACTIVE); 768 return; 769 } 770 771 if ((hwset = pghw_set_lookup(hw)) == NULL) 772 return; 773 774 /* 775 * Iterate over the power domains 776 */ 777 group_iter_init(&giter); 778 while ((pwr_pg = group_iterate(hwset, &giter)) != NULL) { 779 780 dom = (cpupm_domain_t *)pwr_pg->cmt_pg.pghw_handle; 781 782 /* 783 * Iterate over the CPUs in each domain 784 */ 785 PG_CPU_ITR_INIT(pwr_pg, cpu_iter); 786 while ((cp = pg_cpu_next(&cpu_iter)) != NULL) { 787 (void) cpupm_change_state(cp, dom, 788 dom->cpd_named_states[state]); 789 } 790 } 791 }