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 }