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 */
70static	int	tzmon_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd,
71    void *arg, void **result);
72static	int	tzmon_attach(dev_info_t *dip, ddi_attach_cmd_t cmd);
73static	int	tzmon_detach(dev_info_t *dip, ddi_detach_cmd_t cmd);
74
75/* other forward declarations */
76static void tzmon_notify_zone(ACPI_HANDLE obj, UINT32 val, void *ctx);
77static void tzmon_eval_int(ACPI_HANDLE obj, char *method, int *rv);
78static thermal_zone_t *tzmon_alloc_zone();
79static void tzmon_free_zone_list();
80static void tzmon_discard_buffers(thermal_zone_t *tzp);
81static void tzmon_enumerate_zone(ACPI_HANDLE obj, thermal_zone_t *tzp,
82	int enum_flag);
83static ACPI_STATUS tzmon_zone_callback(ACPI_HANDLE obj, UINT32 nest,
84    void *ctx, void **rv);
85static void tzmon_find_zones(void);
86static void tzmon_monitor(void *ctx);
87static void tzmon_set_power_device(ACPI_HANDLE dev, int on_off, char *tz_name);
88static void tzmon_set_power(ACPI_BUFFER devlist, int on_off, char *tz_name);
89static void tzmon_eval_zone(thermal_zone_t *tzp);
90static void tzmon_do_shutdown(void);
91
92extern void halt(char *);
93
94static 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
112static 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
127extern	struct	mod_ops mod_driverops;
128
129static	struct modldrv modldrv = {
130	&mod_driverops,
131	"ACPI Thermal Zone Monitor",
132	&tzmon_ops,
133};
134
135static	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 */
142static dev_info_t	*tzmon_dip;
143static thermal_zone_t	*zone_list;
144static int		zone_count;
145static kmutex_t		zone_list_lock;
146static kcondvar_t	zone_list_condvar;
147
148
149/*
150 * _init, _info, and _fini support loading and unloading the driver.
151 */
152int
153_init(void)
154{
155	return (mod_install(&modlinkage));
156}
157
158
159int
160_info(struct modinfo *modinfop)
161{
162	return (mod_info(&modlinkage, modinfop));
163}
164
165
166int
167_fini(void)
168{
169	return (mod_remove(&modlinkage));
170}
171
172
173static int
174tzmon_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*/
218static int
219tzmon_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
244static int
245tzmon_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 */
275static void
276tzmon_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 */
304static void
305tzmon_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 */
317static thermal_zone_t *
318tzmon_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 */
333static void
334tzmon_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
373static void
374tzmon_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 */
394static void
395tzmon_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 (AcpiEvaluateObjectTyped(obj, abuf, NULL,
475				    &tzp->al[level], ACPI_TYPE_PACKAGE) !=
476				    AE_OK) {
477					DTRACE_PROBE2(alx__missing, int, level,
478					    char *, (char *)tzp->zone_name);
479
480					tzp->al[level].Length = 0;
481					tzp->al[level].Pointer = NULL;
482				}
483			}
484		}
485
486		tzp->psl.Length = ACPI_ALLOCATE_BUFFER;
487		tzp->psl.Pointer = NULL;
488		(void) AcpiEvaluateObjectTyped(obj, "_PSL", NULL, &tzp->psl,
489		    ACPI_TYPE_PACKAGE);
490	}
491
492	tzmon_eval_int(obj, "_TC1", &tzp->tc1);
493	tzmon_eval_int(obj, "_TC2", &tzp->tc2);
494	tzmon_eval_int(obj, "_TSP", &tzp->tsp);
495	tzmon_eval_int(obj, "_TZP", &tzp->tzp);
496
497	if (tzp->tzp == 0) {
498		tzp->polling_period = 0;
499	} else {
500		if (tzp->tzp < 0)
501			tzp->polling_period = TZ_DEFAULT_PERIOD;
502		else
503			tzp->polling_period = tzp->tzp/10;
504
505		/* start monitor thread if needed */
506		if (tzp->taskq == NULL) {
507			char taskq_name[TZ_TASKQ_NAME_LEN];
508
509			(void) snprintf(taskq_name, TZ_TASKQ_NAME_LEN,
510			    "AcpiThermalMonitor%02d", instance);
511			tzp->taskq = ddi_taskq_create(tzmon_dip,
512			    taskq_name, 1, TASKQ_DEFAULTPRI, 0);
513			if (tzp->taskq == NULL) {
514				tzp->polling_period = 0;
515				cmn_err(CE_WARN, "tzmon: could not create "
516				    "monitor thread for thermal zone %s - "
517				    "monitor by notify only",
518				    (char *)tzp->zone_name);
519			} else {
520				(void) ddi_taskq_dispatch(tzp->taskq,
521				    tzmon_monitor, tzp, DDI_SLEEP);
522			}
523		}
524	}
525
526	mutex_exit(&tzp->lock);
527}
528
529
530/*
531 * tzmon_zone_callback
532 * Enumerate the thermal zone if it has a _TMP (current thermal zone
533 * operating temperature) method.
534 */
535/*ARGSUSED*/
536static ACPI_STATUS
537tzmon_zone_callback(ACPI_HANDLE obj, UINT32 nest, void *ctx, void **rv)
538{
539	ACPI_HANDLE tmpobj;
540
541	/*
542	 * We get both ThermalZone() and Scope(\_TZ) objects here;
543	 * look for _TMP (without which a zone is invalid) to pick
544	 * between them (and ignore invalid zones)
545	 */
546	if (AcpiGetHandle(obj, "_TMP", &tmpobj) == AE_OK) {
547		tzmon_enumerate_zone(obj, NULL, TZMON_ENUM_ALL);
548	}
549
550	return (AE_OK);
551}
552
553
554/*
555 * tzmon_find_zones
556 * Find all of the thermal zones by calling a ACPICA function that
557 * walks the ACPI namespace and invokes a callback for each thermal
558 * object found.
559 */
560static void
561tzmon_find_zones()
562{
563	ACPI_STATUS status;
564	int retval;
565
566	status = AcpiWalkNamespace(ACPI_TYPE_THERMAL, ACPI_ROOT_OBJECT,
567	    8, tzmon_zone_callback, NULL, NULL, (void **)&retval);
568
569	ASSERT(status == AE_OK);
570}
571
572
573/*
574 * tzmon_monitor
575 * Run as a separate thread, this wakes according to polling period and
576 * checks particular objects in the thermal zone.  One instance per
577 * thermal zone.
578 */
579static void
580tzmon_monitor(void *ctx)
581{
582	thermal_zone_t *tzp = (thermal_zone_t *)ctx;
583	clock_t ticks;
584
585	do {
586		/* Check out the zone */
587		tzmon_eval_zone(tzp);
588
589		/* Go back to sleep */
590		mutex_enter(&tzp->lock);
591		ticks = drv_usectohz(tzp->polling_period * 1000000);
592		if (ticks > 0)
593			(void) cv_reltimedwait(&zone_list_condvar,
594			    &tzp->lock, ticks, TR_CLOCK_TICK);
595		mutex_exit(&tzp->lock);
596	} while (ticks > 0);
597}
598
599
600/*
601 * tzmon_set_power_device
602 */
603static void
604tzmon_set_power_device(ACPI_HANDLE dev, int on_off, char *tz_name)
605{
606	ACPI_BUFFER rb;
607	ACPI_OBJECT *pr0;
608	ACPI_STATUS status;
609	int i;
610
611	rb.Length = ACPI_ALLOCATE_BUFFER;
612	rb.Pointer = NULL;
613	status = AcpiEvaluateObjectTyped(dev, "_PR0", NULL, &rb,
614	    ACPI_TYPE_PACKAGE);
615	if (status != AE_OK) {
616		DTRACE_PROBE2(alx__error, int, 2, char *, tz_name);
617		return;
618	}
619
620	pr0 = ((ACPI_OBJECT *)rb.Pointer);
621	for (i = 0; i < pr0->Package.Count; i++) {
622		status = AcpiEvaluateObject(
623		    pr0->Package.Elements[i].Reference.Handle,
624		    on_off ? "_ON" : "_OFF", NULL, NULL);
625		if (status != AE_OK) {
626			DTRACE_PROBE2(alx__error, int, 4, char *, tz_name);
627		}
628	}
629
630	AcpiOsFree(rb.Pointer);
631}
632
633
634/*
635 * tzmon_set_power
636 * Turn on or turn off all devices in the supplied list.
637 */
638static void
639tzmon_set_power(ACPI_BUFFER devlist, int on_off, char *tz_name)
640{
641	ACPI_OBJECT *devs;
642	int i;
643
644	devs = ((ACPI_OBJECT *)devlist.Pointer);
645	if (devs->Type != ACPI_TYPE_PACKAGE) {
646		DTRACE_PROBE2(alx__error, int, 1, char *, tz_name);
647		return;
648	}
649
650	for (i = 0; i < devs->Package.Count; i++)
651		tzmon_set_power_device(
652		    devs->Package.Elements[i].Reference.Handle, on_off,
653		    tz_name);
654}
655
656
657/*
658 * tzmon_eval_zone
659 * Evaluate the current conditions within the thermal zone.
660 */
661static void
662tzmon_eval_zone(thermal_zone_t *tzp)
663{
664	int tmp, new_level, level;
665
666	mutex_enter(&tzp->lock);
667
668	/* get the current temperature from ACPI */
669	tzmon_eval_int(tzp->obj, "_TMP", &tmp);
670	DTRACE_PROBE4(tz__temp, int, tmp, int, tzp->crt, int, tzp->hot,
671	    char *, (char *)tzp->zone_name);
672
673	/* _HOT handling */
674	if (tzp->hot > 0 && tmp >= tzp->hot) {
675		cmn_err(CE_WARN,
676		    "tzmon: Thermal zone (%s) is too hot (%d C); "
677		    "initiating shutdown\n",
678		    (char *)tzp->zone_name, K_TO_C(tmp));
679
680		tzmon_do_shutdown();
681	}
682
683	/* _CRT handling */
684	if (tzp->crt > 0 && tmp >= tzp->crt) {
685		cmn_err(CE_WARN,
686		    "tzmon: Thermal zone (%s) is critically hot (%d C); "
687		    "initiating rapid shutdown\n",
688		    (char *)tzp->zone_name, K_TO_C(tmp));
689
690		/* shut down (fairly) immediately */
691		mdboot(A_REBOOT, AD_HALT, NULL, B_FALSE);
692	}
693
694	/*
695	 * use the temperature to determine whether the thermal zone
696	 * is at a new active cooling threshold level
697	 */
698	for (level = 0, new_level = -1; level < TZ_NUM_LEVELS; level++) {
699		if (tzp->ac[level] >= 0 && (tmp >= tzp->ac[level])) {
700			new_level = level;
701			break;
702		}
703	}
704
705	/*
706	 * if the active cooling threshold has changed, turn off the
707	 * devices associated with the old one and turn on the new one
708	 */
709	if (tzp->current_level != new_level) {
710		if ((tzp->current_level >= 0) &&
711		    (tzp->al[tzp->current_level].Length != 0))
712			tzmon_set_power(tzp->al[tzp->current_level], 0,
713			    (char *)tzp->zone_name);
714
715		if ((new_level >= 0) &&
716		    (tzp->al[new_level].Length != 0))
717			tzmon_set_power(tzp->al[new_level], 1,
718			    (char *)tzp->zone_name);
719
720		tzp->current_level = new_level;
721	}
722
723	mutex_exit(&tzp->lock);
724}
725
726
727/*
728 * tzmon_do_shutdown
729 * Initiates shutdown by sending a SIGPWR signal to init.
730 */
731static void
732tzmon_do_shutdown(void)
733{
734	proc_t *initpp;
735
736	mutex_enter(&pidlock);
737	initpp = prfind(P_INITPID);
738	mutex_exit(&pidlock);
739
740	/* if we can't find init, just halt */
741	if (initpp == NULL) {
742		mdboot(A_REBOOT, AD_HALT, NULL, B_FALSE);
743	}
744
745	/* graceful shutdown with inittab and all getting involved */
746	psignal(initpp, SIGPWR);
747}
748