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 /* 23 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 /* 28 * Solaris x86 ACPI ThermalZone Monitor 29 */ 30 31 32 #include <sys/errno.h> 33 #include <sys/conf.h> 34 #include <sys/modctl.h> 35 #include <sys/open.h> 36 #include <sys/stat.h> 37 #include <sys/ddi.h> 38 #include <sys/sunddi.h> 39 #include <sys/ksynch.h> 40 #include <sys/uadmin.h> 41 #include <sys/acpi/acpi.h> 42 #include <sys/acpica.h> 43 #include <sys/sdt.h> 44 45 #include "tzmon.h" 46 47 48 #define TZMON_ENUM_TRIP_POINTS 1 49 #define TZMON_ENUM_DEV_LISTS 2 50 #define TZMON_ENUM_ALL (TZMON_ENUM_TRIP_POINTS | TZMON_ENUM_DEV_LISTS) 51 52 /* 53 * TZ_TASKQ_NAME_LEN is precisely the length of the string "AcpiThermalMonitor" 54 * plus a two-digit instance number plus a NULL. If the taskq name is changed 55 * (particularly if it is lengthened), then this value needs to change. 56 */ 57 #define TZ_TASKQ_NAME_LEN 21 58 59 /* 60 * Kelvin to Celsius conversion 61 * The formula for converting degrees Kelvin to degrees Celsius is 62 * C = K - 273.15 (we round to 273.2). The unit for thermal zone 63 * temperatures is tenths of a degree Kelvin. Use tenth of a degree 64 * to convert, then make a whole number out of it. 65 */ 66 #define K_TO_C(temp) (((temp) - 2732) / 10) 67 68 69 /* cb_ops or dev_ops forward declarations */ 70 static int tzmon_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, 71 void *arg, void **result); 72 static int tzmon_attach(dev_info_t *dip, ddi_attach_cmd_t cmd); 73 static int tzmon_detach(dev_info_t *dip, ddi_detach_cmd_t cmd); 74 75 /* other forward declarations */ 76 static void tzmon_notify_zone(ACPI_HANDLE obj, UINT32 val, void *ctx); 77 static void tzmon_eval_int(ACPI_HANDLE obj, char *method, int *rv); 78 static thermal_zone_t *tzmon_alloc_zone(); 79 static void tzmon_free_zone_list(); 80 static void tzmon_discard_buffers(thermal_zone_t *tzp); 81 static void tzmon_enumerate_zone(ACPI_HANDLE obj, thermal_zone_t *tzp, 82 int enum_flag); 83 static ACPI_STATUS tzmon_zone_callback(ACPI_HANDLE obj, UINT32 nest, 84 void *ctx, void **rv); 85 static void tzmon_find_zones(void); 86 static void tzmon_monitor(void *ctx); 87 static void tzmon_set_power_device(ACPI_HANDLE dev, int on_off, char *tz_name); 88 static void tzmon_set_power(ACPI_BUFFER devlist, int on_off, char *tz_name); 89 static void tzmon_eval_zone(thermal_zone_t *tzp); 90 static void tzmon_do_shutdown(void); 91 92 extern void halt(char *); 93 94 static struct cb_ops tzmon_cb_ops = { 95 nodev, /* no open routine */ 96 nodev, /* no close routine */ 97 nodev, /* not a block driver */ 98 nodev, /* no print routine */ 99 nodev, /* no dump routine */ 100 nodev, /* no read routine */ 101 nodev, /* no write routine */ 102 nodev, /* no ioctl routine */ 103 nodev, /* no devmap routine */ 104 nodev, /* no mmap routine */ 105 nodev, /* no segmap routine */ 106 nochpoll, /* no chpoll routine */ 107 ddi_prop_op, 108 0, /* not a STREAMS driver */ 109 D_NEW | D_MP, /* safe for multi-thread/multi-processor */ 110 }; 111 112 static struct dev_ops tzmon_ops = { 113 DEVO_REV, /* devo_rev */ 114 0, /* devo_refcnt */ 115 tzmon_getinfo, /* devo_getinfo */ 116 nulldev, /* devo_identify */ 117 nulldev, /* devo_probe */ 118 tzmon_attach, /* devo_attach */ 119 tzmon_detach, /* devo_detach */ 120 nodev, /* devo_reset */ 121 &tzmon_cb_ops, /* devo_cb_ops */ 122 (struct bus_ops *)0, /* devo_bus_ops */ 123 NULL, /* devo_power */ 124 ddi_quiesce_not_needed, /* devo_quiesce */ 125 }; 126 127 extern struct mod_ops mod_driverops; 128 129 static struct modldrv modldrv = { 130 &mod_driverops, 131 "ACPI Thermal Zone Monitor", 132 &tzmon_ops, 133 }; 134 135 static struct modlinkage modlinkage = { 136 MODREV_1, /* MODREV_1 indicated by manual */ 137 { (void *)&modldrv, NULL } 138 }; 139 140 /* globals for this module */ 141 static dev_info_t *tzmon_dip; 142 static thermal_zone_t *zone_list; 143 static int zone_count; 144 static kmutex_t zone_list_lock; 145 static kcondvar_t zone_list_condvar; 146 147 148 /* 149 * _init, _info, and _fini support loading and unloading the driver. 150 */ 151 int 152 _init(void) 153 { 154 return (mod_install(&modlinkage)); 155 } 156 157 158 int 159 _info(struct modinfo *modinfop) 160 { 161 return (mod_info(&modlinkage, modinfop)); 162 } 163 164 165 int 166 _fini(void) 167 { 168 return (mod_remove(&modlinkage)); 169 } 170 171 172 static int 173 tzmon_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 174 { 175 if (cmd != DDI_ATTACH) 176 return (DDI_FAILURE); 177 178 if (tzmon_dip != NULL) 179 return (DDI_FAILURE); 180 181 /* 182 * Check to see if ACPI CA services are available 183 */ 184 if (AcpiSubsystemStatus() != AE_OK) 185 return (DDI_FAILURE); 186 187 mutex_init(&zone_list_lock, NULL, MUTEX_DRIVER, NULL); 188 cv_init(&zone_list_condvar, NULL, CV_DRIVER, NULL); 189 190 tzmon_find_zones(); 191 mutex_enter(&zone_list_lock); 192 if (zone_count < 1) { 193 mutex_exit(&zone_list_lock); 194 mutex_destroy(&zone_list_lock); 195 cv_destroy(&zone_list_condvar); 196 return (DDI_FAILURE); 197 } 198 mutex_exit(&zone_list_lock); 199 200 if (ddi_create_minor_node(dip, ddi_get_name(dip), S_IFCHR, 0, 201 DDI_PSEUDO, 0) == DDI_FAILURE) { 202 tzmon_free_zone_list(); 203 mutex_destroy(&zone_list_lock); 204 cv_destroy(&zone_list_condvar); 205 return (DDI_FAILURE); 206 } 207 208 tzmon_dip = dip; 209 210 ddi_report_dev(dip); 211 212 return (DDI_SUCCESS); 213 } 214 215 216 /*ARGSUSED*/ 217 static int 218 tzmon_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result) 219 { 220 int error; 221 222 switch (infocmd) { 223 case DDI_INFO_DEVT2DEVINFO: 224 *result = tzmon_dip; 225 if (tzmon_dip == NULL) 226 error = DDI_FAILURE; 227 else 228 error = DDI_SUCCESS; 229 break; 230 case DDI_INFO_DEVT2INSTANCE: 231 *result = 0; 232 error = DDI_SUCCESS; 233 break; 234 default: 235 *result = NULL; 236 error = DDI_FAILURE; 237 } 238 239 return (error); 240 } 241 242 243 static int 244 tzmon_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 245 { 246 thermal_zone_t *tzp = zone_list; 247 248 if (cmd != DDI_DETACH) 249 return (DDI_FAILURE); 250 251 /* free allocated thermal zone name(s) */ 252 while (tzp != NULL) { 253 AcpiOsFree(tzp->zone_name); 254 tzp = tzp->next; 255 } 256 257 /* discard zone list assets */ 258 tzmon_free_zone_list(); 259 260 ddi_remove_minor_node(dip, NULL); 261 tzmon_dip = NULL; 262 263 mutex_destroy(&zone_list_lock); 264 cv_destroy(&zone_list_condvar); 265 266 return (DDI_SUCCESS); 267 } 268 269 270 /* 271 * tzmon_notify_zone 272 * Thermal zone notification handler. 273 */ 274 static void 275 tzmon_notify_zone(ACPI_HANDLE obj, UINT32 val, void *ctx) 276 { 277 thermal_zone_t *tzp = (thermal_zone_t *)ctx; 278 279 switch (val) { 280 case 0x80: /* Thermal Zone status changed */ 281 tzmon_eval_zone(tzp); 282 break; 283 case 0x81: /* Thermal Zone trip points changed */ 284 tzmon_enumerate_zone(obj, tzp, TZMON_ENUM_TRIP_POINTS); 285 break; 286 case 0x82: /* Device Lists changed */ 287 tzmon_enumerate_zone(obj, tzp, TZMON_ENUM_DEV_LISTS); 288 break; 289 case 0x83: /* Thermal Relationship Table changed */ 290 /* not handling _TRT objects, so not handling this event */ 291 DTRACE_PROBE1(trt__change, char *, (char *)tzp->zone_name); 292 break; 293 default: 294 break; 295 } 296 } 297 298 299 /* 300 * tzmon_eval_int 301 * Evaluate the object/method as an integer. 302 */ 303 static void 304 tzmon_eval_int(ACPI_HANDLE obj, char *method, int *rv) 305 { 306 307 if (acpica_eval_int(obj, method, rv) != AE_OK) 308 *rv = -1; 309 } 310 311 312 /* 313 * tzmon_alloc_zone 314 * Allocate memory for the zone structure and initialize it lock mutex. 315 */ 316 static thermal_zone_t * 317 tzmon_alloc_zone() 318 { 319 thermal_zone_t *tzp; 320 321 tzp = kmem_zalloc(sizeof (thermal_zone_t), KM_SLEEP); 322 mutex_init(&tzp->lock, NULL, MUTEX_DRIVER, NULL); 323 324 return (tzp); 325 } 326 327 328 /* 329 * tzmon_free_zone_list 330 * Free the zone list, either because attach failed or detach initiated. 331 */ 332 static void 333 tzmon_free_zone_list() 334 { 335 thermal_zone_t *tzp = zone_list; 336 337 while (tzp != NULL) { 338 thermal_zone_t *next; 339 340 mutex_enter(&tzp->lock); 341 342 /* 343 * Remove the notify handler for the zone. Not much to 344 * do if this fails (since we are on our way out), so 345 * just ignore failure. 346 */ 347 (void) AcpiRemoveNotifyHandler(tzp->obj, ACPI_DEVICE_NOTIFY, 348 tzmon_notify_zone); 349 350 /* Shut down monitor thread, if running */ 351 if (tzp->taskq != NULL) { 352 tzp->polling_period = 0; 353 cv_broadcast(&zone_list_condvar); 354 355 /* Drop mutex to allow the thread to run */ 356 mutex_exit(&tzp->lock); 357 ddi_taskq_destroy(tzp->taskq); 358 mutex_enter(&tzp->lock); 359 } 360 361 tzmon_discard_buffers(tzp); 362 mutex_exit(&tzp->lock); 363 mutex_destroy(&tzp->lock); 364 365 next = tzp->next; 366 kmem_free(tzp, sizeof (thermal_zone_t)); 367 tzp = next; 368 } 369 } 370 371 372 static void 373 tzmon_discard_buffers(thermal_zone_t *tzp) 374 { 375 int level; 376 377 for (level = 0; level < TZ_NUM_LEVELS; level++) { 378 if (tzp->al[level].Pointer != NULL) 379 AcpiOsFree(tzp->al[level].Pointer); 380 } 381 382 if (tzp->psl.Pointer != NULL) 383 AcpiOsFree(tzp->psl.Pointer); 384 } 385 386 387 /* 388 * tzmon_enumerate_zone 389 * Enumerates the contents of a thermal zone and updates passed-in 390 * thermal_zone or creates a new one if tzp is NULL. Newly-created 391 * zones are linked into the global zone_list. 392 */ 393 static void 394 tzmon_enumerate_zone(ACPI_HANDLE obj, thermal_zone_t *tzp, int enum_flag) 395 { 396 ACPI_STATUS status; 397 ACPI_BUFFER zone_name; 398 int level; 399 int instance; 400 char abuf[5]; 401 402 /* 403 * Newly-created zones and existing zones both require 404 * some individual attention. 405 */ 406 if (tzp == NULL) { 407 /* New zone required */ 408 tzp = tzmon_alloc_zone(); 409 mutex_enter(&zone_list_lock); 410 tzp->next = zone_list; 411 zone_list = tzp; 412 413 /* 414 * It is exceedingly unlikely that instance will exceed 99. 415 * However, if it does, this will cause problems when 416 * creating the taskq for this thermal zone. 417 */ 418 instance = zone_count; 419 zone_count++; 420 mutex_exit(&zone_list_lock); 421 mutex_enter(&tzp->lock); 422 tzp->obj = obj; 423 424 /* 425 * Set to a low level. Will get set to the actual 426 * current power level when the thread monitor polls 427 * the current temperature. 428 */ 429 tzp->current_level = 0; 430 431 /* Get the zone name in case we need to display it later */ 432 zone_name.Length = ACPI_ALLOCATE_BUFFER; 433 zone_name.Pointer = NULL; 434 435 status = AcpiGetName(obj, ACPI_FULL_PATHNAME, &zone_name); 436 ASSERT(status == AE_OK); 437 438 tzp->zone_name = zone_name.Pointer; 439 440 status = AcpiInstallNotifyHandler(obj, ACPI_DEVICE_NOTIFY, 441 tzmon_notify_zone, (void *)tzp); 442 ASSERT(status == AE_OK); 443 } else { 444 /* Existing zone - toss out allocated items */ 445 mutex_enter(&tzp->lock); 446 ASSERT(tzp->obj == obj); 447 448 if (enum_flag & TZMON_ENUM_DEV_LISTS) 449 tzmon_discard_buffers(tzp); 450 } 451 452 if (enum_flag & TZMON_ENUM_TRIP_POINTS) { 453 for (level = 0; level < TZ_NUM_LEVELS; level++) { 454 (void) snprintf(abuf, 5, "_AC%d", level); 455 tzmon_eval_int(obj, abuf, &tzp->ac[level]); 456 457 } 458 459 tzmon_eval_int(obj, "_CRT", &tzp->crt); 460 tzmon_eval_int(obj, "_HOT", &tzp->hot); 461 tzmon_eval_int(obj, "_PSV", &tzp->psv); 462 } 463 464 if (enum_flag & TZMON_ENUM_DEV_LISTS) { 465 for (level = 0; level < TZ_NUM_LEVELS; level++) { 466 if (tzp->ac[level] == -1) { 467 tzp->al[level].Length = 0; 468 tzp->al[level].Pointer = NULL; 469 } else { 470 (void) snprintf(abuf, 5, "_AL%d", level); 471 tzp->al[level].Length = ACPI_ALLOCATE_BUFFER; 472 tzp->al[level].Pointer = NULL; 473 if (AcpiEvaluateObjectTyped(obj, abuf, NULL, 474 &tzp->al[level], ACPI_TYPE_PACKAGE) != 475 AE_OK) { 476 DTRACE_PROBE2(alx__missing, int, level, 477 char *, (char *)tzp->zone_name); 478 479 tzp->al[level].Length = 0; 480 tzp->al[level].Pointer = NULL; 481 } 482 } 483 } 484 485 tzp->psl.Length = ACPI_ALLOCATE_BUFFER; 486 tzp->psl.Pointer = NULL; 487 (void) AcpiEvaluateObjectTyped(obj, "_PSL", NULL, &tzp->psl, 488 ACPI_TYPE_PACKAGE); 489 } 490 491 tzmon_eval_int(obj, "_TC1", &tzp->tc1); 492 tzmon_eval_int(obj, "_TC2", &tzp->tc2); 493 tzmon_eval_int(obj, "_TSP", &tzp->tsp); 494 tzmon_eval_int(obj, "_TZP", &tzp->tzp); 495 496 if (tzp->tzp == 0) { 497 tzp->polling_period = 0; 498 } else { 499 if (tzp->tzp < 0) 500 tzp->polling_period = TZ_DEFAULT_PERIOD; 501 else 502 tzp->polling_period = tzp->tzp/10; 503 504 /* start monitor thread if needed */ 505 if (tzp->taskq == NULL) { 506 char taskq_name[TZ_TASKQ_NAME_LEN]; 507 508 (void) snprintf(taskq_name, TZ_TASKQ_NAME_LEN, 509 "AcpiThermalMonitor%02d", instance); 510 tzp->taskq = ddi_taskq_create(tzmon_dip, 511 taskq_name, 1, TASKQ_DEFAULTPRI, 0); 512 if (tzp->taskq == NULL) { 513 tzp->polling_period = 0; 514 cmn_err(CE_WARN, "tzmon: could not create " 515 "monitor thread for thermal zone %s - " 516 "monitor by notify only", 517 (char *)tzp->zone_name); 518 } else { 519 (void) ddi_taskq_dispatch(tzp->taskq, 520 tzmon_monitor, tzp, DDI_SLEEP); 521 } 522 } 523 } 524 525 mutex_exit(&tzp->lock); 526 } 527 528 529 /* 530 * tzmon_zone_callback 531 * Enumerate the thermal zone if it has a _TMP (current thermal zone 532 * operating temperature) method. 533 */ 534 /*ARGSUSED*/ 535 static ACPI_STATUS 536 tzmon_zone_callback(ACPI_HANDLE obj, UINT32 nest, void *ctx, void **rv) 537 { 538 ACPI_HANDLE tmpobj; 539 540 /* 541 * We get both ThermalZone() and Scope(\_TZ) objects here; 542 * look for _TMP (without which a zone is invalid) to pick 543 * between them (and ignore invalid zones) 544 */ 545 if (AcpiGetHandle(obj, "_TMP", &tmpobj) == AE_OK) { 546 tzmon_enumerate_zone(obj, NULL, TZMON_ENUM_ALL); 547 } 548 549 return (AE_OK); 550 } 551 552 553 /* 554 * tzmon_find_zones 555 * Find all of the thermal zones by calling a ACPICA function that 556 * walks the ACPI namespace and invokes a callback for each thermal 557 * object found. 558 */ 559 static void 560 tzmon_find_zones() 561 { 562 ACPI_STATUS status; 563 int retval; 564 565 status = AcpiWalkNamespace(ACPI_TYPE_THERMAL, ACPI_ROOT_OBJECT, 566 8, tzmon_zone_callback, NULL, NULL, (void **)&retval); 567 568 ASSERT(status == AE_OK); 569 } 570 571 572 /* 573 * tzmon_monitor 574 * Run as a separate thread, this wakes according to polling period and 575 * checks particular objects in the thermal zone. One instance per 576 * thermal zone. 577 */ 578 static void 579 tzmon_monitor(void *ctx) 580 { 581 thermal_zone_t *tzp = (thermal_zone_t *)ctx; 582 clock_t ticks; 583 584 do { 585 /* Check out the zone */ 586 tzmon_eval_zone(tzp); 587 588 /* Go back to sleep */ 589 mutex_enter(&tzp->lock); 590 ticks = drv_usectohz(tzp->polling_period * 1000000); 591 if (ticks > 0) 592 (void) cv_reltimedwait(&zone_list_condvar, 593 &tzp->lock, ticks, TR_CLOCK_TICK); 594 mutex_exit(&tzp->lock); 595 } while (ticks > 0); 596 } 597 598 599 /* 600 * tzmon_set_power_device 601 */ 602 static void 603 tzmon_set_power_device(ACPI_HANDLE dev, int on_off, char *tz_name) 604 { 605 ACPI_BUFFER rb; 606 ACPI_OBJECT *pr0; 607 ACPI_STATUS status; 608 int i; 609 610 rb.Length = ACPI_ALLOCATE_BUFFER; 611 rb.Pointer = NULL; 612 status = AcpiEvaluateObjectTyped(dev, "_PR0", NULL, &rb, 613 ACPI_TYPE_PACKAGE); 614 if (status != AE_OK) { 615 DTRACE_PROBE2(alx__error, int, 2, char *, tz_name); 616 return; 617 } 618 619 pr0 = ((ACPI_OBJECT *)rb.Pointer); 620 for (i = 0; i < pr0->Package.Count; i++) { 621 status = AcpiEvaluateObject( 622 pr0->Package.Elements[i].Reference.Handle, 623 on_off ? "_ON" : "_OFF", NULL, NULL); 624 if (status != AE_OK) { 625 DTRACE_PROBE2(alx__error, int, 4, char *, tz_name); 626 } 627 } 628 629 AcpiOsFree(rb.Pointer); 630 } 631 632 633 /* 634 * tzmon_set_power 635 * Turn on or turn off all devices in the supplied list. 636 */ 637 static void 638 tzmon_set_power(ACPI_BUFFER devlist, int on_off, char *tz_name) 639 { 640 ACPI_OBJECT *devs; 641 int i; 642 643 devs = ((ACPI_OBJECT *)devlist.Pointer); 644 if (devs->Type != ACPI_TYPE_PACKAGE) { 645 DTRACE_PROBE2(alx__error, int, 1, char *, tz_name); 646 return; 647 } 648 649 for (i = 0; i < devs->Package.Count; i++) 650 tzmon_set_power_device( 651 devs->Package.Elements[i].Reference.Handle, on_off, 652 tz_name); 653 } 654 655 656 /* 657 * tzmon_eval_zone 658 * Evaluate the current conditions within the thermal zone. 659 */ 660 static void 661 tzmon_eval_zone(thermal_zone_t *tzp) 662 { 663 int tmp, new_level, level; 664 665 mutex_enter(&tzp->lock); 666 667 /* get the current temperature from ACPI */ 668 tzmon_eval_int(tzp->obj, "_TMP", &tmp); 669 DTRACE_PROBE4(tz__temp, int, tmp, int, tzp->crt, int, tzp->hot, 670 char *, (char *)tzp->zone_name); 671 672 /* _HOT handling */ 673 if (tzp->hot > 0 && tmp >= tzp->hot) { 674 cmn_err(CE_WARN, 675 "tzmon: Thermal zone (%s) is too hot (%d C); " 676 "initiating shutdown\n", 677 (char *)tzp->zone_name, K_TO_C(tmp)); 678 679 tzmon_do_shutdown(); 680 } 681 682 /* _CRT handling */ 683 if (tzp->crt > 0 && tmp >= tzp->crt) { 684 cmn_err(CE_WARN, 685 "tzmon: Thermal zone (%s) is critically hot (%d C); " 686 "initiating rapid shutdown\n", 687 (char *)tzp->zone_name, K_TO_C(tmp)); 688 689 /* shut down (fairly) immediately */ 690 mdboot(A_REBOOT, AD_HALT, NULL, B_FALSE); 691 } 692 693 /* 694 * use the temperature to determine whether the thermal zone 695 * is at a new active cooling threshold level 696 */ 697 for (level = 0, new_level = -1; level < TZ_NUM_LEVELS; level++) { 698 if (tzp->ac[level] >= 0 && (tmp >= tzp->ac[level])) { 699 new_level = level; 700 break; 701 } 702 } 703 704 /* 705 * if the active cooling threshold has changed, turn off the 706 * devices associated with the old one and turn on the new one 707 */ 708 if (tzp->current_level != new_level) { 709 if ((tzp->current_level >= 0) && 710 (tzp->al[tzp->current_level].Length != 0)) 711 tzmon_set_power(tzp->al[tzp->current_level], 0, 712 (char *)tzp->zone_name); 713 714 if ((new_level >= 0) && 715 (tzp->al[new_level].Length != 0)) 716 tzmon_set_power(tzp->al[new_level], 1, 717 (char *)tzp->zone_name); 718 719 tzp->current_level = new_level; 720 } 721 722 mutex_exit(&tzp->lock); 723 } 724 725 726 /* 727 * tzmon_do_shutdown 728 * Initiates shutdown by sending a SIGPWR signal to init. 729 */ 730 static void 731 tzmon_do_shutdown(void) 732 { 733 proc_t *initpp; 734 735 mutex_enter(&pidlock); 736 initpp = prfind(P_INITPID); 737 mutex_exit(&pidlock); 738 739 /* if we can't find init, just halt */ 740 if (initpp == NULL) { 741 mdboot(A_REBOOT, AD_HALT, NULL, B_FALSE); 742 } 743 744 /* graceful shutdown with inittab and all getting involved */ 745 psignal(initpp, SIGPWR); 746 }