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