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