xref: /illumos-gate/usr/src/cmd/picl/plugins/sun4u/grover/envd/piclenvd.c (revision 4eaa471005973e11a6110b69fe990530b3b95a38)
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, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright (c) 2000 by Sun Microsystems, Inc.
24  * All rights reserved.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 /*
30  * This file contains the environmental daemon module.
31  */
32 
33 
34 /*
35  * Grover system contains one temperature device, MAX1617, which consists
36  * of two sensors: CPU die and CPU ambient. Each sensor is represented
37  * as a different minor device and the current temperature is read via an
38  * I2C_GET_TEMPERATURE ioctl call to the max1617 driver. Additionally, the
39  * MAX1617 device supports both a low and high temperature limit, which
40  * can trigger an alert condition, causing power supply to turn off.
41  *
42  * The environmental daemon defines the following thresholds per sensor:
43  *
44  *	high_power_off		high hard shutdown
45  *	high_shutdown		high soft shutdown limit
46  *	high_warning		high warning limit
47  *	low_warning		low warning limit
48  *	low_shutdown		low soft shutdown limit
49  *	low_power_off		low hard shutdown limit
50  *
51  * Except for the low_power_off and high_power_off limits, all other threshold
52  * values can be changed via "piclenvd.conf" configuration file.
53  *
54  * Environmental monitoring is done by the "envthr" thread. It periodically
55  * monitors both CPU die and CPU ambient temperatures and takes appropriate
56  * action depending upon the current temperature and threshold values for
57  * that sensor. If the temperature reaches the high_shutdown limit or the
58  * low_shutdown limit, and remains there for over shutdown_interval seconds,
59  * it forces a graceful system shutdown via tuneable shutdown_cmd string
60  * variable. Otherwise, if the temperature reaches the high_warning limit
61  * or the low_warning limit, it logs and prints a message on the console.
62  * This message will be printed at most at "warning_interval" seconds
63  * interval, which is also a tuneable variable.
64  *
65  * Grover system also contains a fan, known as system fan, which can be turned
66  * ON or OFF under software control. However, its speed is automatically
67  * controlled by the hardware based upon the ambient temperature. When in EStar
68  * mode (i.e. lowest power state), the environmental daemon will turn off this
69  * fan provided the CPU die and ambient temperature is below the high warning
70  * limits.
71  *
72  * The power state monitoring is done by the "pmthr" thread. It uses the
73  * PM_GET_STATE_CHANGE and PM_GET_STATE_CHANGE_WAIT ioctl commands to pick
74  * up any power state change events. It processes all queued power state
75  * change events and determines the curret lowest power state and saves it
76  * in cur_lpstate variable. Whenever this state changes from the previous
77  * lowest power state (saved in prev_lpstate), it wakes up the "envtrh"
78  * thread.
79  *
80  * The "lpstate_lock" mutex and "lpstate_cond" condition variables are used
81  * to communicate power state change events from the "pmthr" to the "envthr"
82  * thread.  The "envthr" thread uses the pthread_cond_timedwait() interface
83  * to wait for any power state change notifications. The "pmthr" uses the
84  * pthread_signal() interface to wake up the "envthr" thread.
85  */
86 
87 #include <stdio.h>
88 #include <stdlib.h>
89 #include <string.h>
90 #include <stdarg.h>
91 #include <unistd.h>
92 #include <limits.h>
93 #include <syslog.h>
94 #include <errno.h>
95 #include <fcntl.h>
96 #include <picl.h>
97 #include <picltree.h>
98 #include <pthread.h>
99 #include <sys/pm.h>
100 #include <sys/open.h>
101 #include <sys/time.h>
102 #include <sys/utsname.h>
103 #include <sys/systeminfo.h>
104 #include <sys/i2c/clients/max1617.h>
105 #include <sys/i2c/clients/i2c_client.h>
106 #include "envd.h"
107 
108 
109 /*
110  * PICL plguin
111  */
112 static void piclenvd_register(void);
113 static void piclenvd_init(void);
114 static void piclenvd_fini(void);
115 extern void env_picl_setup();
116 
117 #pragma	init(piclenvd_register)
118 
119 static picld_plugin_reg_t  my_reg_info = {
120 	PICLD_PLUGIN_VERSION_1,
121 	PICLD_PLUGIN_CRITICAL,
122 	"SUNW_piclenvd",
123 	piclenvd_init,
124 	piclenvd_fini,
125 };
126 
127 /*
128  * tuneable variables
129  */
130 int		env_debug;
131 static int	sensor_poll_interval = SENSOR_POLL_INTERVAL;
132 static int	warning_interval = WARNING_INTERVAL;
133 static int	shutdown_interval = SHUTDOWN_INTERVAL;
134 static char	shutdown_cmd[128] = SHUTDOWN_CMD;
135 static int	monitor_temperature = 0;
136 
137 static sensor_thresh_t cpu_die_thresh = {
138 	CPU_DIE_LOW_POWER_OFF, CPU_DIE_HIGH_POWER_OFF,
139 	CPU_DIE_LOW_SHUTDOWN, CPU_DIE_HIGH_SHUTDOWN,
140 	CPU_DIE_LOW_WARNING, CPU_DIE_HIGH_WARNING,
141 	CPU_DIE_TARGET_TEMP
142 };
143 
144 static sensor_thresh_t cpu_amb_thresh = {
145 	CPU_AMB_LOW_POWER_OFF, CPU_AMB_HIGH_POWER_OFF,
146 	CPU_AMB_LOW_SHUTDOWN, CPU_AMB_HIGH_SHUTDOWN,
147 	CPU_AMB_LOW_WARNING, CPU_AMB_HIGH_WARNING,
148 	CPU_AMB_TARGET_TEMP
149 };
150 
151 /*
152  * Temperature sensors
153  */
154 
155 static env_sensor_t cpu_die_sensor =
156 	{ SENSOR_CPU_DIE, CPU_DIE_SENSOR_DEVFS, &cpu_die_thresh};
157 
158 static env_sensor_t cpu_amb_sensor =
159 	{ SENSOR_CPU_AMB, CPU_AMB_SENSOR_DEVFS, &cpu_amb_thresh};
160 
161 
162 static env_sensor_t *envd_sensors[] = {
163 	&cpu_die_sensor,
164 	&cpu_amb_sensor,
165 	NULL
166 };
167 
168 /*
169  * Fan devices
170  */
171 static env_fan_t envd_system_fan = {
172 	ENV_SYSTEM_FAN, ENV_SYSTEM_FAN_DEVFS,
173 	SYSTEM_FAN_SPEED_MIN, SYSTEM_FAN_SPEED_MAX,
174 };
175 
176 static env_fan_t *envd_fans[] =  {
177 	&envd_system_fan,
178 	NULL
179 };
180 
181 
182 /*
183  * Environmental thread variables
184  */
185 static boolean_t	envd_inited = B_FALSE;
186 static boolean_t	system_shutdown_started;
187 static boolean_t	envthr_created;		/* envthr created */
188 static pthread_t	envthr_tid;		/* envthr thread ID */
189 static pthread_attr_t	thr_attr;
190 
191 /*
192  * Power management thread (pmthr) variables
193  */
194 static pthread_t	pmthr_tid;		/* pmthr thread ID */
195 static int		pmthr_created;		/* pmthr created */
196 static int		pm_fd;			/* PM device file descriptor */
197 static int		cur_lpstate;		/* cur low power state */
198 
199 static pthread_mutex_t	lpstate_lock;		/* low power state lock */
200 static pthread_cond_t	lpstate_cond;		/* low power state condvar */
201 
202 
203 /*
204  * Tuneable variables data structure/array
205  */
206 
207 typedef struct {
208 	char	*name;		/* keyword */
209 	void	*addr;		/* memory (variable) address */
210 	int	type;		/* keyword type */
211 	int	size;		/* variable size */
212 } env_tuneable_t;
213 
214 /* keyword types */
215 #define	KTYPE_INT	1	/* signed int */
216 #define	KTYPE_STRING	2	/* string in double quotes */
217 
218 static env_tuneable_t env_tuneables[] = {
219 	{"cpu_amb_low_shutdown", &cpu_amb_thresh.low_shutdown, KTYPE_INT,
220 	    sizeof (tempr_t)},
221 	{"cpu_amb_low_warning", &cpu_amb_thresh.low_warning, KTYPE_INT,
222 	    sizeof (tempr_t)},
223 	{"cpu_amb_target_temp", &cpu_amb_thresh.target_temp, KTYPE_INT,
224 	    sizeof (tempr_t)},
225 	{"cpu_amb_high_shutdown", &cpu_amb_thresh.high_shutdown, KTYPE_INT,
226 	    sizeof (tempr_t)},
227 	{"cpu_amb_high_warning", &cpu_amb_thresh.high_warning, KTYPE_INT,
228 	    sizeof (tempr_t)},
229 	{"cpu_die_low_shutdown", &cpu_die_thresh.low_shutdown, KTYPE_INT,
230 	    sizeof (tempr_t)},
231 	{"cpu_die_low_warning", &cpu_die_thresh.low_warning, KTYPE_INT,
232 	    sizeof (tempr_t)},
233 	{"cpu_die_target_temp", &cpu_die_thresh.target_temp, KTYPE_INT,
234 	    sizeof (tempr_t)},
235 	{"cpu_die_high_shutdown", &cpu_die_thresh.high_shutdown, KTYPE_INT,
236 	    sizeof (tempr_t)},
237 	{"cpu_die_high_warning", &cpu_die_thresh.high_warning, KTYPE_INT,
238 	    sizeof (tempr_t)},
239 	{"sensor_poll_interval", &sensor_poll_interval, KTYPE_INT,
240 	    sizeof (sensor_poll_interval)},
241 	{"monitor_temperature", &monitor_temperature, KTYPE_INT,
242 	    sizeof (monitor_temperature)},
243 	{"warning_interval", &warning_interval, KTYPE_INT,
244 	    sizeof (warning_interval)},
245 	{"shutdown_interval", &shutdown_interval, KTYPE_INT,
246 	    sizeof (shutdown_interval)},
247 	{"shutdown_cmd", &shutdown_cmd[0], KTYPE_STRING, sizeof (shutdown_cmd)},
248 	{"env_debug", &env_debug, KTYPE_INT, sizeof (env_debug)},
249 	{ NULL, NULL, 0, 0}
250 };
251 
252 /*
253  * Lookup fan and return a pointer to env_fan_t data structure.
254  */
255 env_fan_t *
256 fan_lookup(char *name)
257 {
258 	int		i;
259 	env_fan_t	*fanp;
260 
261 	for (i = 0; (fanp = envd_fans[i]) != NULL; i++) {
262 		if (strcmp(fanp->name, name) == 0)
263 			return (fanp);
264 	}
265 	return (NULL);
266 }
267 
268 /*
269  * Lookup sensor and return a pointer to env_sensor_t data structure.
270  */
271 env_sensor_t *
272 sensor_lookup(char *name)
273 {
274 	int		i;
275 	env_sensor_t	*sensorp;
276 
277 	for (i = 0; (sensorp = envd_sensors[i]) != NULL; i++) {
278 		if (strcmp(sensorp->name, name) == 0)
279 			return (sensorp);
280 	}
281 	return (NULL);
282 }
283 
284 /*
285  * Get current temperature
286  * Returns -1 on error, 0 if successful
287  */
288 int
289 get_temperature(env_sensor_t *sensorp, tempr_t *temp)
290 {
291 	int	fd = sensorp->fd;
292 	int	retval = 0;
293 
294 	if (fd == -1)
295 		retval = -1;
296 	else if (ioctl(fd, I2C_GET_TEMPERATURE, temp) == -1) {
297 		retval = -1;
298 		if (sensorp->error == 0) {
299 			sensorp->error = 1;
300 			envd_log(LOG_WARNING, ENV_SENSOR_ACCESS_FAIL,
301 			    sensorp->name, errno, strerror(errno));
302 		}
303 	} else if (sensorp->error != 0) {
304 		sensorp->error = 0;
305 		envd_log(LOG_WARNING, ENV_SENSOR_ACCESS_OK, sensorp->name);
306 	}
307 
308 	return (retval);
309 }
310 
311 /*
312  * Get current fan speed
313  * Returns -1 on error, 0 if successful
314  */
315 int
316 get_fan_speed(env_fan_t *fanp, fanspeed_t *fanspeedp)
317 {
318 	int	fan_fd;
319 	int	retval = 0;
320 
321 	fan_fd = fanp->fd;
322 	if (fan_fd == -1 || read(fan_fd, fanspeedp, sizeof (fanspeed_t)) !=
323 	    sizeof (fanspeed_t))
324 		retval = -1;
325 	return (retval);
326 }
327 
328 /*
329  * Set fan speed
330  * Returns -1 on error, 0 if successful
331  */
332 static int
333 set_fan_speed(env_fan_t *fanp, fanspeed_t fanspeed)
334 {
335 	int	fan_fd;
336 	int	retval = 0;
337 
338 	fan_fd = fanp->fd;
339 	if (fan_fd == -1 || write(fan_fd, &fanspeed, sizeof (fanspeed)) !=
340 	    sizeof (fanspeed_t))
341 		retval = -1;
342 	return (retval);
343 }
344 
345 
346 /*
347  * close all fan devices
348  */
349 static void
350 envd_close_fans(void)
351 {
352 	int		i;
353 	env_fan_t	*fanp;
354 
355 	for (i = 0; (fanp = envd_fans[i]) != NULL; i++) {
356 		if (fanp->fd != -1) {
357 			(void) close(fanp->fd);
358 			fanp->fd = -1;
359 		}
360 	}
361 }
362 
363 /*
364  * Close sensor devices
365  */
366 static void
367 envd_close_sensors(void)
368 {
369 	int		i;
370 	env_sensor_t	*sensorp;
371 
372 	for (i = 0; (sensorp = envd_sensors[i]) != NULL; i++) {
373 		if (sensorp->fd != -1) {
374 			(void) close(sensorp->fd);
375 			sensorp->fd = -1;
376 		}
377 	}
378 }
379 
380 /*
381  * Close PM device
382  */
383 static void
384 envd_close_pm(void)
385 {
386 	if (pm_fd != -1) {
387 		(void) close(pm_fd);
388 		pm_fd = -1;
389 	}
390 }
391 
392 /*
393  * Open fan devices and initialize per fan data structure.
394  * Returns #fans found.
395  */
396 static int
397 envd_setup_fans(void)
398 {
399 	int		i, fd;
400 	fanspeed_t	speed;
401 	env_fan_t	*fanp;
402 	char		path[FILENAME_MAX];
403 	int		fancnt = 0;
404 
405 	for (i = 0; (fanp = envd_fans[i]) != NULL; i++) {
406 		fanp->fd = -1;
407 		fanp->cur_speed = 0;
408 		fanp->prev_speed = 0;
409 
410 		(void) strcpy(path, "/devices");
411 		(void) strlcat(path, fanp->devfs_path, sizeof (path));
412 		fd = open(path, O_RDWR);
413 		if (fd == -1) {
414 			envd_log(LOG_WARNING, ENV_FAN_OPEN_FAIL, fanp->name,
415 			    fanp->devfs_path, errno, strerror(errno));
416 			fanp->present = B_FALSE;
417 			continue;
418 		}
419 		fanp->fd = fd;
420 		fanp->present = B_TRUE;
421 		fancnt++;
422 
423 		/*
424 		 * Set cur_speed/prev_speed to current fan speed
425 		 */
426 		if (get_fan_speed(fanp, &speed) == -1) {
427 			/*
428 			 * The Fan driver does not know the current fan speed.
429 			 * Initialize it to 50% of the max speed and reread
430 			 * to get the current speed.
431 			 */
432 			speed = fanp->speed_max/2;
433 			(void) set_fan_speed(fanp, speed);
434 			if (get_fan_speed(fanp, &speed) == -1)
435 				continue;
436 		}
437 		fanp->cur_speed = speed;
438 		fanp->prev_speed = speed;
439 	}
440 	return (fancnt);
441 }
442 
443 /*
444  * Open temperature sensor devices and initialize per sensor data structure.
445  * Returns #sensors found.
446  */
447 static int
448 envd_setup_sensors(void)
449 {
450 	int		i;
451 	tempr_t		temp;
452 	env_sensor_t	*sensorp;
453 	char		path[FILENAME_MAX];
454 	int		sensorcnt = 0;
455 	sensor_thresh_t	*threshp;
456 
457 	for (i = 0; (sensorp = envd_sensors[i]) != NULL; i++) {
458 		sensorp->fd = -1;
459 		sensorp->shutdown_initiated = B_FALSE;
460 		sensorp->warning_tstamp = 0;
461 		sensorp->shutdown_tstamp = 0;
462 		threshp = sensorp->temp_thresh;
463 		sensorp->cur_temp = threshp->target_temp;
464 		sensorp->error = 0;
465 
466 		(void) strcpy(path, "/devices");
467 		(void) strlcat(path, sensorp->devfs_path, sizeof (path));
468 		sensorp->fd = open(path, O_RDWR);
469 		if (sensorp->fd == -1) {
470 			envd_log(LOG_WARNING, ENV_SENSOR_OPEN_FAIL,
471 			    sensorp->name, sensorp->devfs_path, errno,
472 			    strerror(errno));
473 			sensorp->present = B_FALSE;
474 			continue;
475 		}
476 		sensorp->present = B_TRUE;
477 		sensorcnt++;
478 
479 		if (monitor_temperature) {
480 			/*
481 			 * Set low_power_off and high_power_off limits
482 			 */
483 			(void) ioctl(sensorp->fd, MAX1617_SET_LOW_LIMIT,
484 			    &threshp->low_power_off);
485 			(void) ioctl(sensorp->fd, MAX1617_SET_HIGH_LIMIT,
486 			    &threshp->high_power_off);
487 		}
488 
489 		/*
490 		 * Set cur_temp field to the current temperature value
491 		 */
492 		if (get_temperature(sensorp, &temp) == 0) {
493 			sensorp->cur_temp = temp;
494 		}
495 	}
496 	return (sensorcnt);
497 }
498 
499 /*
500  * Read all temperature sensors and take appropriate action based
501  * upon temperature threshols associated with each sensor. Possible
502  * actions are:
503  *
504  *	temperature > high_shutdown
505  *	temperature < low_shutdown
506  *		Gracefully shutdown the system and log/print a message
507  *		on the system console provided the temperature has been
508  *		in shutdown range for "shutdown_interval" seconds.
509  *
510  *	high_warning < temperature <= high_shutdown
511  *	low_warning  > temperature >= low_shutdown
512  *		Log/print a warning message on the system console at most
513  *		once every "warning_interval" seconds.
514  *
515  * Note that the current temperature is recorded in the "cur_temp" field
516  * within each env_sensor_t structure.
517  */
518 static void
519 monitor_sensors(void)
520 {
521 	tempr_t 	temp;
522 	int		i;
523 	env_sensor_t	*sensorp;
524 	sensor_thresh_t	*threshp;
525 	struct timeval	ct;
526 	char		msgbuf[BUFSIZ];
527 	char		syscmd[BUFSIZ];
528 
529 	for (i = 0; (sensorp = envd_sensors[i]) != NULL; i++) {
530 		if (get_temperature(sensorp, &temp) < 0)
531 			continue;
532 
533 		sensorp->cur_temp = temp;
534 
535 		if (env_debug)
536 			envd_log(LOG_INFO,
537 			    "sensor: %-13s temp  cur:%3d  target:%3d\n",
538 			    sensorp->name, temp,
539 			    sensorp->temp_thresh->target_temp);
540 
541 		if (!monitor_temperature)
542 			continue;
543 
544 		/*
545 		 * If this sensor already triggered system shutdown, don't
546 		 * log any more shutdown/warning messages for it.
547 		 */
548 		if (sensorp->shutdown_initiated)
549 			continue;
550 
551 		/*
552 		 * Check for the temperature in warning and shutdown range
553 		 * and take appropriate action.
554 		 */
555 		threshp = sensorp->temp_thresh;
556 		if (TEMP_IN_WARNING_RANGE(temp, threshp)) {
557 			/*
558 			 * Log warning message at most once every
559 			 * warning_interval seconds.
560 			 */
561 			(void) gettimeofday(&ct, NULL);
562 			if ((ct.tv_sec - sensorp->warning_tstamp) >=
563 			    warning_interval) {
564 				envd_log(LOG_WARNING, ENV_WARNING_MSG,
565 				    sensorp->name, temp,
566 				    threshp->low_warning,
567 				    threshp->high_warning);
568 				sensorp->warning_tstamp = ct.tv_sec;
569 			}
570 		}
571 
572 		if (TEMP_IN_SHUTDOWN_RANGE(temp, threshp)) {
573 			(void) gettimeofday(&ct, NULL);
574 			if (sensorp->shutdown_tstamp == 0)
575 				sensorp->shutdown_tstamp = ct.tv_sec;
576 
577 			/*
578 			 * Shutdown the system if the temperature remains
579 			 * in the shutdown range for over shutdown_interval
580 			 * seconds.
581 			 */
582 			if ((ct.tv_sec - sensorp->shutdown_tstamp) >=
583 			    shutdown_interval) {
584 				/* log error */
585 				sensorp->shutdown_initiated = B_TRUE;
586 				(void) snprintf(msgbuf, sizeof (msgbuf),
587 				    ENV_SHUTDOWN_MSG, sensorp->name,
588 				    temp, threshp->low_shutdown,
589 				    threshp->high_shutdown);
590 				envd_log(LOG_CRIT, msgbuf);
591 
592 				/* shutdown the system (only once) */
593 				if (system_shutdown_started == B_FALSE) {
594 					(void) snprintf(syscmd, sizeof (syscmd),
595 					    "%s \"%s\"", shutdown_cmd, msgbuf);
596 					envd_log(LOG_CRIT, syscmd);
597 					system_shutdown_started = B_TRUE;
598 					(void) system(syscmd);
599 				}
600 			}
601 		} else if (sensorp->shutdown_tstamp != 0)
602 			sensorp->shutdown_tstamp = 0;
603 	}
604 }
605 
606 
607 /*
608  * This is the environment thread, which monitors the current temperature
609  * and power managed state and controls system fan speed.  Temperature is
610  * polled every sensor-poll_interval seconds duration.
611  */
612 static void *
613 envthr(void *args)
614 {
615 	int		err;
616 	fanspeed_t 	fan_speed;
617 	struct timeval	ct;
618 	struct timespec	to;
619 	env_fan_t	*pmfanp = &envd_system_fan;
620 	tempr_t		cpu_amb_temp, cpu_die_temp;
621 	tempr_t		cpu_amb_warning, cpu_die_warning;
622 
623 	(void) pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
624 	(void) pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
625 
626 	cpu_amb_warning = cpu_amb_sensor.temp_thresh->high_warning;
627 	cpu_die_warning = cpu_die_sensor.temp_thresh->high_warning;
628 
629 	for (;;) {
630 		(void) gettimeofday(&ct, NULL);
631 
632 		/*
633 		 * Monitor current temperature for all sensors
634 		 * (current temperature is recorded in the "cur_temp"
635 		 * field within each sensor data structure)
636 		 */
637 		monitor_sensors();
638 
639 		cpu_amb_temp =  cpu_amb_sensor.cur_temp;
640 		cpu_die_temp =  cpu_die_sensor.cur_temp;
641 
642 		/*
643 		 * Process any PM state change events while waiting until
644 		 * time to poll sensors again (i.e. sensor_poll_interval
645 		 * seconds from the last time).
646 		 */
647 		to.tv_sec = ct.tv_sec + sensor_poll_interval;
648 		to.tv_nsec = 0;
649 		for (;;) {
650 			/*
651 			 * Turn off system fan if in lowest power state
652 			 * and both CPU die and ambient temperatures are
653 			 * below corresponding high warning temperatures.
654 			 */
655 			fan_speed = pmfanp->speed_max;
656 			if (cur_lpstate && cpu_amb_temp < cpu_amb_warning &&
657 			    cpu_die_temp < cpu_die_warning)
658 				fan_speed = pmfanp->speed_min;
659 
660 			if (env_debug)
661 				envd_log(LOG_INFO,
662 				    "fan: %-16s speed cur:%3d  new:%3d "
663 				    "low-power:%d\n", pmfanp->name,
664 				    (uint_t)pmfanp->cur_speed,
665 				    (uint_t)fan_speed, cur_lpstate);
666 
667 			if (fan_speed != pmfanp->cur_speed &&
668 			    set_fan_speed(pmfanp, fan_speed) == 0)
669 				pmfanp->cur_speed = fan_speed;
670 
671 			/* wait for power state change or time to poll */
672 			pthread_mutex_lock(&lpstate_lock);
673 			err = pthread_cond_timedwait(&lpstate_cond,
674 			    &lpstate_lock, &to);
675 			pthread_mutex_unlock(&lpstate_lock);
676 			if (err == ETIMEDOUT)
677 				break;
678 		}
679 	}
680 	/*NOTREACHED*/
681 	return (NULL);
682 }
683 
684 /*
685  * This is the power management thread, which monitors all power state
686  * change events and wakes up the "envthr" thread when the system enters
687  * or exits the lowest power state.
688  */
689 static void *
690 pmthr(void *args)
691 {
692 	pm_state_change_t	pmstate;
693 	char			physpath[PATH_MAX];
694 	int			prev_lpstate;
695 
696 	(void) pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
697 	(void) pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
698 
699 	pmstate.physpath = physpath;
700 	pmstate.size = sizeof (physpath);
701 	cur_lpstate = 0;
702 	prev_lpstate = 0;
703 
704 	for (;;) {
705 		/*
706 		 * Get PM state change events to check if the system
707 		 * is in lowest power state and wake up the "envthr"
708 		 * thread when the power state changes.
709 		 *
710 		 * To minimize polling, we use the blocking interface
711 		 * to get the power state change event here.
712 		 */
713 		if (ioctl(pm_fd, PM_GET_STATE_CHANGE_WAIT, &pmstate) != 0) {
714 			if (errno != EINTR)
715 				break;
716 			continue;
717 		}
718 
719 		/*
720 		 * Extract the lowest power state from the last queued
721 		 * state change events. We pick up queued state change
722 		 * events using the non-blocking interface and wake up
723 		 * the "envthr" thread only after consuming all the
724 		 * state change events queued at that time.
725 		 */
726 		do {
727 			if (env_debug > 1)  {
728 				envd_log(LOG_INFO,
729 				    "pmstate event:0x%x flags:%x comp:%d "
730 				    "oldval:%d newval:%d path:%s\n",
731 				    pmstate.event, pmstate.flags,
732 				    pmstate.component, pmstate.old_level,
733 				    pmstate.new_level, pmstate.physpath);
734 			}
735 			cur_lpstate =
736 			    (pmstate.flags & PSC_ALL_LOWEST) ? 1 : 0;
737 		} while (ioctl(pm_fd, PM_GET_STATE_CHANGE, &pmstate) == 0);
738 
739 		if (cur_lpstate != prev_lpstate) {
740 			prev_lpstate = cur_lpstate;
741 			pthread_mutex_lock(&lpstate_lock);
742 			pthread_cond_signal(&lpstate_cond);
743 			pthread_mutex_unlock(&lpstate_lock);
744 		}
745 	}
746 
747 	/*
748 	 * We won't be able to monitor lowest power state any longer,
749 	 * hence reset it and wakeup the "envthr".
750 	 */
751 	if (cur_lpstate != 0) {
752 		prev_lpstate = cur_lpstate;
753 		cur_lpstate = 0;
754 		pthread_mutex_lock(&lpstate_lock);
755 		pthread_cond_signal(&lpstate_cond);
756 		pthread_mutex_unlock(&lpstate_lock);
757 	}
758 	envd_log(LOG_ERR, PM_THREAD_EXITING, errno, strerror(errno));
759 	return (NULL);
760 }
761 
762 
763 /*
764  * Parse string value (handling escaped double quotes and other characters)
765  * and return string end pointer.
766  */
767 
768 static char *
769 parse_string_val(char *buf)
770 {
771 	char	*p, c;
772 
773 	if (buf[0] != '"')
774 		return (NULL);
775 
776 	for (p = buf+1; (c = *p) != '\0'; p++)
777 		if (c == '"' || (c == '\\' && *++p == '\0'))
778 			break;
779 
780 	return ((*p == '"') ? p : NULL);
781 }
782 
783 
784 /*
785  * Process configuration file
786  */
787 static void
788 process_env_conf_file(void)
789 {
790 	int		line, len, val, toklen;
791 	char		buf[BUFSIZ];
792 	FILE		*fp;
793 	env_tuneable_t	*tunep;
794 	char		nmbuf[SYS_NMLN];
795 	char		fname[PATH_MAX];
796 	char		*tok, *valuep, *strend;
797 	char		tokdel[] = " \t\n\r";
798 	int		skip_line = 0;
799 
800 	if (sysinfo(SI_PLATFORM, nmbuf, sizeof (nmbuf)) == -1)
801 		return;
802 
803 	(void) snprintf(fname, sizeof (fname), PICLD_PLAT_PLUGIN_DIRF, nmbuf);
804 	(void) strlcat(fname, ENV_CONF_FILE, sizeof (fname));
805 	fp = fopen(fname, "r");
806 	if (fp == NULL)
807 		return;
808 
809 	/*
810 	 * Blank lines or lines starting with "#" or "*" in the first
811 	 * column are ignored. All other lines are assumed to contain
812 	 * input in the following format:
813 	 *
814 	 *	keyword value
815 	 *
816 	 * where the "value" can be a signed integer or string (in
817 	 * double quotes) depending upon the keyword.
818 	 */
819 
820 	for (line = 1; fgets(buf, sizeof (buf), fp) != NULL; line++) {
821 		len = strlen(buf);
822 		if (len <= 0)
823 			continue;
824 
825 		/* skip long lines */
826 		if (buf[len-1] != '\n') {
827 			skip_line = 1;
828 			continue;
829 		} else if (skip_line) {
830 			skip_line = 0;
831 			continue;
832 		} else
833 			buf[len-1] = '\0';
834 
835 		/* skip comments */
836 		if (buf[0] == '*' || buf[0] == '#')
837 			continue;
838 
839 		/*
840 		 * Skip over white space to get the keyword
841 		 */
842 		tok = buf + strspn(buf, tokdel);
843 		if (*tok == '\0')
844 			continue;			/* blank line */
845 
846 		toklen = strcspn(tok, tokdel);
847 		tok[toklen] = '\0';
848 
849 		/* Get possible location for value (within current line) */
850 		valuep = tok + toklen + 1;
851 		if (valuep > buf+len)
852 			valuep = buf + len;
853 
854 		/*
855 		 * Lookup the keyword and process value accordingly
856 		 */
857 		for (tunep = &env_tuneables[0]; tunep->name != NULL; tunep++) {
858 			if (strcmp(tunep->name, tok) != 0)
859 				continue;
860 
861 			switch (tunep->type) {
862 			case KTYPE_INT:
863 				errno = 0;
864 				val = strtol(valuep, &valuep, 0);
865 
866 				/* Check for invalid value or extra tokens */
867 				if (errno != 0 || strtok(valuep, tokdel)) {
868 					envd_log(LOG_INFO,
869 					    ENV_CONF_INT_EXPECTED,
870 					    fname, line, tok);
871 					break;
872 				}
873 
874 				/* Update only if value within range */
875 				if (tunep->size == sizeof (int8_t) &&
876 				    val == (int8_t)val)
877 					*(int8_t *)tunep->addr = (int8_t)val;
878 				else if (tunep->size == sizeof (short) &&
879 				    val == (short)val)
880 					*(short *)tunep->addr = (short)val;
881 				else if (tunep->size == sizeof (int))
882 					*(int *)tunep->addr = (int)val;
883 				else {
884 					envd_log(LOG_INFO,
885 					    ENV_CONF_INT_EXPECTED,
886 					    fname, line, tok);
887 					break;
888 				}
889 				if (env_debug)
890 					envd_log(LOG_INFO, "SUNW_piclenvd: "
891 					    "file:%s line:%d %s = %d\n",
892 					    fname, line, tok, val);
893 				break;
894 
895 			case KTYPE_STRING:
896 				/*
897 				 * String value must be within double quotes.
898 				 * Skip over initial white spaces before
899 				 * looking for value.
900 				 */
901 				valuep += strspn(valuep, tokdel);
902 				strend = parse_string_val(valuep);
903 
904 				if (strend == NULL || *valuep != '"' ||
905 				    strtok(strend+1, tokdel) != NULL ||
906 				    (strend-valuep) > tunep->size) {
907 					envd_log(LOG_INFO,
908 					    ENV_CONF_STRING_EXPECTED,
909 					    fname, line, tok,
910 					    tunep->size);
911 					break;
912 				}
913 				*strend = '\0';
914 				if (env_debug)
915 					envd_log(LOG_INFO, "piclenvd: file:%s"
916 					    " line:%d %s = \"%s\"\n",
917 					    fname, line, tok, valuep+1);
918 				(void) strcpy(tunep->addr, (caddr_t)valuep+1);
919 				break;
920 
921 			default:
922 				envd_log(LOG_INFO,
923 				    ENV_CONF_UNSUPPORTED_TYPE,
924 				    fname, line,
925 				    tunep->type, tunep->name);
926 			}
927 			break;
928 		}
929 
930 		if (tunep->name == NULL)
931 			envd_log(LOG_INFO, ENV_CONF_UNSUPPORTED_KEYWORD,
932 			    fname, line, tok);
933 	}
934 	(void) fclose(fp);
935 }
936 
937 /*
938  * Setup envrionmental daemon state and start threads to monitor
939  * temperature and power management state.
940  * Returns -1 on error, 0 if successful.
941  */
942 
943 static int
944 envd_setup(void)
945 {
946 	if (envd_inited == B_FALSE) {
947 		/*
948 		 * Initialize global state
949 		 */
950 		system_shutdown_started = B_FALSE;
951 		envthr_created = B_FALSE;
952 		pmthr_created = B_FALSE;
953 
954 		if (pthread_attr_init(&thr_attr) != 0 ||
955 		    pthread_attr_setscope(&thr_attr, PTHREAD_SCOPE_SYSTEM) != 0)
956 			return (-1);
957 
958 		if (pthread_mutex_init(&lpstate_lock, NULL) != 0 ||
959 		    pthread_cond_init(&lpstate_cond, NULL) != 0)
960 			return (-1);
961 
962 		/*
963 		 * Process tuneable parameters
964 		 */
965 		process_env_conf_file();
966 
967 		/*
968 		 * Setup temperature sensors and fail if we can't open
969 		 * at least one sensor.
970 		 */
971 		if (envd_setup_sensors() <= 0)
972 			return (-1);
973 
974 		/*
975 		 * Setup fan device (don't fail even if we can't access
976 		 * the fan as we can still monitor temeperature.
977 		 */
978 		(void) envd_setup_fans();
979 
980 		/*
981 		 * Create a thread to monitor temperature and control fan
982 		 * speed.
983 		 */
984 		if (envthr_created == B_FALSE && pthread_create(&envthr_tid,
985 		    &thr_attr, envthr, (void *)NULL) != 0) {
986 			envd_close_fans();
987 			envd_close_sensors();
988 			envd_log(LOG_CRIT, ENV_THREAD_CREATE_FAILED);
989 			return (-1);
990 		}
991 		envthr_created = B_TRUE;
992 	}
993 	envd_inited = B_TRUE;
994 
995 	/*
996 	 * Create a thread to monitor PM state
997 	 */
998 	if (pmthr_created == B_FALSE) {
999 		pm_fd = open(PM_DEVICE, O_RDONLY);
1000 		if (pm_fd == -1 || pthread_create(&pmthr_tid, &thr_attr,
1001 		    pmthr, (void *)NULL) != 0) {
1002 			envd_close_pm();
1003 			envd_log(LOG_CRIT, PM_THREAD_CREATE_FAILED);
1004 		} else
1005 			pmthr_created = B_TRUE;
1006 	}
1007 	return (0);
1008 }
1009 
1010 
1011 static void
1012 piclenvd_register(void)
1013 {
1014 	picld_plugin_register(&my_reg_info);
1015 }
1016 
1017 static void
1018 piclenvd_init(void)
1019 {
1020 	/*
1021 	 * Start environmental daemon/threads
1022 	 */
1023 	if (envd_setup() != 0) {
1024 		envd_log(LOG_CRIT, ENVD_PLUGIN_INIT_FAILED);
1025 		return;
1026 	}
1027 
1028 	/*
1029 	 * Now setup/populate PICL tree
1030 	 */
1031 	env_picl_setup();
1032 }
1033 
1034 static void
1035 piclenvd_fini(void)
1036 {
1037 	void	*exitval;
1038 
1039 	/*
1040 	 * Kill both "envthr" and "pmthr" threads.
1041 	 */
1042 	if (envthr_created) {
1043 		(void) pthread_cancel(envthr_tid);
1044 		(void) pthread_join(envthr_tid, &exitval);
1045 		envthr_created = B_FALSE;
1046 	}
1047 
1048 	if (pmthr_created) {
1049 		(void) pthread_cancel(pmthr_tid);
1050 		(void) pthread_join(pmthr_tid, &exitval);
1051 		pmthr_created = B_FALSE;
1052 	}
1053 
1054 	/*
1055 	 * close all sensors, fans and the power management device
1056 	 */
1057 	envd_close_pm();
1058 	envd_close_fans();
1059 	envd_close_sensors();
1060 	envd_inited = B_FALSE;
1061 }
1062 
1063 /*VARARGS2*/
1064 void
1065 envd_log(int pri, const char *fmt, ...)
1066 {
1067 	va_list	ap;
1068 
1069 	va_start(ap, fmt);
1070 	vsyslog(pri, fmt, ap);
1071 	va_end(ap);
1072 }
1073