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