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
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.
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]
23 * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
27 #pragma ident "%Z%%M% %I% %E% SMI"
30 * This file contains the environmental PICL plug-in module.
35 * Excalibur system contains up to two CPU and two PCI MAX1617 temperature
36 * devices, each consisting of two sensors: die and ambient. Each sensor is
37 * represented as a different minor device and the current temperature is read
38 * via an I2C_GET_TEMPERATURE ioctl call to the max1617 driver. Additionally,
39 * the MAX1617 device supports both a low and high temperature limit, which
40 * can trigger an alert condition, causing power supply to turn off.
42 * The environmental monitor defines the following thresholds per sensor:
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
51 * Above mentioned threshold values can be changed via "piclenvd.conf"
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.
65 * Excalibur system contains three fans: cpu, system and power supply. The
66 * cpu and system fans are under software control and their speed can be
67 * set to a value in the range 0 through 63. However, the software has no
68 * control over the power supply fan's speed (it's automatically controlled
69 * by the hardware), but it can turn it ON or OFF. When in EStar mode (i.e.
70 * the lowest power state), the environmental monitor turns off the power
73 * Each fan is represented as a different minor device and the fan speed
74 * can be controlled by writing to the TDA8444 device driver. Note that
75 * these devices are read only and the driver caches the last speed set
76 * for each fan, thus allowing an interface to read the current fan speed
79 * The policy to control fan speed depends upon the sensor. For CPU die
80 * sensor, different policy is used depending upon whether the temperature
81 * is rising, falling or steady state. In case of CPU ambient sensor, only
82 * one policy (speed proportional to the current temperature) is used.
84 * The power state monitoring is done by the "pmthr" thread. It uses the
85 * PM_GET_STATE_CHANGE and PM_GET_STATE_CHANGE_WAIT ioctl commands to pick
86 * up any power state change events. It processes all queued power state
87 * change events and determines the curret lowest power state and saves it
88 * in cur_lpstate variable.
90 * Once the "envthr" and "pmthr" threads have been started, they are never
91 * killed. This is desirable so that we can do environmental monitoring
92 * during reinit process. The "envd_rwlock" reader/writer lock is used
93 * to protect initialization of global state during reinit process against
94 * the "envthr" and "pmthr" trying to reference that state.
99 #include <sys/sysmacros.h>
105 #include <sys/processor.h>
110 #include <picltree.h>
111 #include <picldefs.h>
114 #include <libdevinfo.h>
116 #include <sys/open.h>
117 #include <sys/time.h>
118 #include <sys/utsname.h>
119 #include <sys/systeminfo.h>
120 #include <sys/i2c/clients/max1617.h>
121 #include <sys/i2c/clients/i2c_client.h>
122 #include <sys/xcalwd.h>
125 static pthread_rwlock_t envd_rwlock
= PTHREAD_RWLOCK_INITIALIZER
;
130 static void piclenvd_register(void);
131 static void piclenvd_init(void);
132 static void piclenvd_fini(void);
133 extern void env_picl_setup(void);
134 extern void env_picl_destroy(void);
136 #pragma init(piclenvd_register)
138 static picld_plugin_reg_t my_reg_info
= {
139 PICLD_PLUGIN_VERSION_1
,
140 PICLD_PLUGIN_CRITICAL
,
148 * Default threshold values for CPU junction/die and ambient sensors
150 static sensor_thresh_t cpu_die_thresh_default
= {
151 CPU_DIE_LOW_POWER_OFF
, CPU_DIE_HIGH_POWER_OFF
,
152 CPU_DIE_LOW_SHUTDOWN
, CPU_DIE_HIGH_SHUTDOWN
,
153 CPU_DIE_LOW_WARNING
, CPU_DIE_HIGH_WARNING
,
154 MAX1617_MIN_TEMP
, MAX1617_MAX_TEMP
,
155 POLICY_TARGET_TEMP
, 2,
156 CPU_DIE_NORMAL_TARGET
, CPU_DIE_OTHER_TARGET
,
160 static sensor_thresh_t cpu_amb_thresh_default
= {
161 CPU_AMB_LOW_POWER_OFF
, CPU_AMB_HIGH_POWER_OFF
,
162 CPU_AMB_LOW_SHUTDOWN
, CPU_AMB_HIGH_SHUTDOWN
,
163 CPU_AMB_LOW_WARNING
, CPU_AMB_HIGH_WARNING
,
164 MAX1617_MIN_TEMP
, MAX1617_MAX_TEMP
,
166 CPU_AMB_LOW_NOMINAL
, CPU_AMB_HIGH_NOMINAL
,
172 * Dummy sensor threshold data structure for processing threshold tuneables
174 static sensor_thresh_t dummy_thresh
;
177 * Temperature related constants for fan speed adjustment
179 #define AVG_TEMP_HYSTERESIS 0.25
180 #define RISING_TEMP_MARGIN 6
181 #define FALLING_TEMP_MARGIN 3
186 #define FAN_SLOW_ADJUSTMENT 20 /* in percentage */
187 #define FAN_INCREMENT_LIMIT 6 /* absolute value */
188 #define FAN_DECREMENT_LIMIT 1 /* absolute value */
189 #define DEVFSADM_CMD "/usr/sbin/devfsadm -i max1617"
190 #define FRU_DEVFSADM_CMD "/usr/sbin/devfsadm -i seeprom"
193 static int sensor_poll_interval
;
194 static int warning_interval
;
195 static int warning_duration
;
196 static int shutdown_interval
;
197 static int fan_slow_adjustment
;
198 static int fan_incr_limit
;
199 static int fan_decr_limit
;
200 static int disable_piclenvd
;
201 static int disable_warning
;
202 static int disable_power_off
;
203 static int disable_shutdown
;
205 static char shutdown_cmd
[128];
206 static char devfsadm_cmd
[128];
207 static char fru_devfsadm_cmd
[128];
208 static sensor_thresh_t cpu0_die_thresh
, cpu0_amb_thresh
;
209 static sensor_thresh_t cpu1_die_thresh
, cpu1_amb_thresh
;
212 * Temperature sensors
215 static env_sensor_t envd_sensors
[] = {
216 { SENSOR_CPU0_DIE
, CPU0_DIE_SENSOR_DEVFS
, &cpu0_die_thresh
,
217 CPU0_FRU_DEVFS
, CPU_FRU_DIE_SENSOR
,
218 SFLAG_TARGET_TEMP
| SFLAG_CPU_DIE_SENSOR
, -1},
219 { SENSOR_CPU0_AMB
, CPU0_AMB_SENSOR_DEVFS
, &cpu0_amb_thresh
,
220 CPU0_FRU_DEVFS
, CPU_FRU_AMB_SENSOR
, SFLAG_CPU_AMB_SENSOR
, -1},
221 { SENSOR_CPU1_DIE
, CPU1_DIE_SENSOR_DEVFS
, &cpu1_die_thresh
,
222 CPU1_FRU_DEVFS
, CPU_FRU_DIE_SENSOR
,
223 SFLAG_TARGET_TEMP
| SFLAG_CPU_DIE_SENSOR
, -1},
224 { SENSOR_CPU1_AMB
, CPU1_AMB_SENSOR_DEVFS
, &cpu1_amb_thresh
,
225 CPU1_FRU_DEVFS
, CPU_FRU_AMB_SENSOR
, SFLAG_CPU_AMB_SENSOR
, -1},
226 { NULL
, NULL
, NULL
, NULL
, 0, 0, -1}
233 static env_fan_t envd_system_fan
= {
234 ENV_SYSTEM_FAN
, ENV_SYSTEM_FAN_DEVFS
,
235 SYSTEM_FAN_SPEED_MIN
, SYSTEM_FAN_SPEED_MAX
, -1, -1,
238 static env_fan_t envd_cpu_fan
= {
239 ENV_CPU_FAN
, ENV_CPU_FAN_DEVFS
,
240 CPU_FAN_SPEED_MIN
, CPU_FAN_SPEED_MAX
, -1, -1,
243 static env_fan_t envd_psupply_fan
= {
244 ENV_PSUPPLY_FAN
, ENV_PSUPPLY_FAN_DEVFS
,
245 PSUPPLY_FAN_SPEED_MIN
, PSUPPLY_FAN_SPEED_MAX
, -1, -1,
248 static env_fan_t
*envd_fans
[] = {
256 * Linked list of devices advertising lpm-ranges
258 static lpm_dev_t
*lpm_devices
= NULL
;
261 * Excalibur lpm to system-fan speed
262 * lpm values must be monotonically increasing (avoid divide-by-zero)
264 static point_t excal_lpm_system_fan_tbl
[] = {
275 static table_t lpm_fspeed
= {
276 sizeof (excal_lpm_system_fan_tbl
)/ sizeof (point_t
),
277 excal_lpm_system_fan_tbl
288 static sensor_fan_map_t sensor_fan_map
[] = {
289 {SENSOR_CPU0_DIE
, ENV_CPU_FAN
},
290 {SENSOR_CPU1_DIE
, ENV_CPU_FAN
},
291 {SENSOR_CPU0_AMB
, ENV_SYSTEM_FAN
},
292 {SENSOR_CPU1_AMB
, ENV_SYSTEM_FAN
},
297 * Sensor to PM device map
299 struct sensor_pmdev
{
303 char *speed_comp_name
;
307 env_sensor_t
*sensorp
;
308 sensor_pmdev_t
*next
;
311 #define SPEED_COMPONENT_NAME "CPU Speed"
313 static sensor_pmdev_t sensor_pmdevs
[] = {
314 {SENSOR_CPU0_ID
, SENSOR_CPU0_DIE
, NULL
, SPEED_COMPONENT_NAME
},
315 {SENSOR_CPU1_ID
, SENSOR_CPU1_DIE
, NULL
, SPEED_COMPONENT_NAME
},
316 {-1, NULL
, NULL
, NULL
}
320 * Environmental thread variables
322 static boolean_t system_shutdown_started
= B_FALSE
;
323 static boolean_t envthr_created
= B_FALSE
; /* envthr created */
324 static pthread_t envthr_tid
; /* envthr thread ID */
325 static pthread_attr_t thr_attr
;
328 * Power management thread (pmthr) variables
330 static boolean_t pmdev_names_init
= B_FALSE
;
331 static pthread_t pmthr_tid
; /* pmthr thread ID */
332 static int pmthr_exists
= B_FALSE
; /* pmthr exists */
333 static int pm_fd
= -1; /* PM device file descriptor */
334 static int cur_lpstate
; /* cur low power state */
337 * Miscellaneous variables and declarations
339 static int fru_devfsadm_invoked
= 0;
340 static int devfsadm_invoked
= 0;
341 static char tokdel
[] = " \t\n\r";
342 static uint_t
envd_sleep(uint_t
);
345 * Tuneable data structure/array and processing functions
349 char *name
; /* keyword */
350 int (*func
)(char *, char *, void *, int, char *, int);
351 /* tuneable processing function */
352 void *arg1
; /* tuneable arg1 (memory address) */
353 int arg2
; /* tuneable arg2 (size or flags) */
356 static int process_int_tuneable(char *keyword
, char *buf
, void *addr
,
357 int size
, char *fname
, int line
);
358 static int process_string_tuneable(char *keyword
, char *buf
, void *addr
,
359 int size
, char *fname
, int line
);
360 static int process_threshold_tuneable(char *keyword
, char *buf
, void *addr
,
361 int flags
, char *fname
, int line
);
362 static void process_env_conf_file(void);
364 static env_tuneable_t env_tuneables
[] = {
365 {"low_power_off", process_threshold_tuneable
,
366 &dummy_thresh
.low_power_off
, 0},
367 {"low_shutdown", process_threshold_tuneable
,
368 &dummy_thresh
.low_shutdown
, 0},
369 {"low_warning", process_threshold_tuneable
,
370 &dummy_thresh
.low_warning
, 0},
371 {"high_power_off", process_threshold_tuneable
,
372 &dummy_thresh
.high_power_off
, 0},
373 {"high_shutdown", process_threshold_tuneable
,
374 &dummy_thresh
.high_shutdown
, 0},
375 {"high_warning", process_threshold_tuneable
,
376 &dummy_thresh
.high_warning
, 0},
377 {"force_cpu_fan", process_int_tuneable
, &envd_cpu_fan
.forced_speed
,
378 sizeof (envd_cpu_fan
.forced_speed
)},
379 {"force_system_fan", process_int_tuneable
,
380 &envd_system_fan
.forced_speed
,
381 sizeof (envd_system_fan
.forced_speed
)},
383 {"cpu_amb_low_power_off", process_threshold_tuneable
,
384 &dummy_thresh
.low_power_off
, SFLAG_CPU_AMB_SENSOR
},
385 {"cpu_amb_low_shutdown", process_threshold_tuneable
,
386 &dummy_thresh
.low_shutdown
, SFLAG_CPU_AMB_SENSOR
},
387 {"cpu_amb_low_warning", process_threshold_tuneable
,
388 &dummy_thresh
.low_warning
, SFLAG_CPU_AMB_SENSOR
},
389 {"cpu_amb_low_nominal", process_threshold_tuneable
,
390 &dummy_thresh
.policy_data
[LOW_NOMINAL_LOC
], SFLAG_CPU_AMB_SENSOR
},
391 {"cpu_amb_high_power_off", process_threshold_tuneable
,
392 &dummy_thresh
.high_power_off
, SFLAG_CPU_AMB_SENSOR
},
393 {"cpu_amb_high_shutdown", process_threshold_tuneable
,
394 &dummy_thresh
.high_shutdown
, SFLAG_CPU_AMB_SENSOR
},
395 {"cpu_amb_high_warning", process_threshold_tuneable
,
396 &dummy_thresh
.high_warning
, SFLAG_CPU_AMB_SENSOR
},
397 {"cpu_amb_high_nominal", process_threshold_tuneable
,
398 &dummy_thresh
.policy_data
[HIGH_NOMINAL_LOC
], SFLAG_CPU_AMB_SENSOR
},
400 {"cpu_die_low_power_off", process_threshold_tuneable
,
401 &dummy_thresh
.low_power_off
, SFLAG_CPU_DIE_SENSOR
},
402 {"cpu_die_low_shutdown", process_threshold_tuneable
,
403 &dummy_thresh
.low_shutdown
, SFLAG_CPU_DIE_SENSOR
},
404 {"cpu_die_low_warning", process_threshold_tuneable
,
405 &dummy_thresh
.low_warning
, SFLAG_CPU_DIE_SENSOR
},
406 {"cpu_die_normal_target", process_threshold_tuneable
,
407 &dummy_thresh
.policy_data
[0], SFLAG_CPU_DIE_SENSOR
},
408 {"cpu_die_high_power_off", process_threshold_tuneable
,
409 &dummy_thresh
.high_power_off
, SFLAG_CPU_DIE_SENSOR
},
410 {"cpu_die_high_shutdown", process_threshold_tuneable
,
411 &dummy_thresh
.high_shutdown
, SFLAG_CPU_DIE_SENSOR
},
412 {"cpu_die_high_warning", process_threshold_tuneable
,
413 &dummy_thresh
.high_warning
, SFLAG_CPU_DIE_SENSOR
},
414 {"cpu_die_other_target", process_threshold_tuneable
,
415 &dummy_thresh
.policy_data
[1], SFLAG_CPU_DIE_SENSOR
},
417 {"sensor_poll_interval", process_int_tuneable
, &sensor_poll_interval
,
418 sizeof (sensor_poll_interval
)},
419 {"warning_interval", process_int_tuneable
, &warning_interval
,
420 sizeof (warning_interval
)},
421 {"warning_duration", process_int_tuneable
, &warning_duration
,
422 sizeof (warning_duration
)},
423 {"disable_piclenvd", process_int_tuneable
, &disable_piclenvd
,
424 sizeof (disable_piclenvd
)},
425 {"disable_power_off", process_int_tuneable
, &disable_power_off
,
426 sizeof (disable_power_off
)},
427 {"disable_warning", process_int_tuneable
, &disable_warning
,
428 sizeof (disable_warning
)},
429 {"disable_shutdown", process_int_tuneable
, &disable_shutdown
,
430 sizeof (disable_shutdown
)},
431 {"shutdown_interval", process_int_tuneable
, &shutdown_interval
,
432 sizeof (shutdown_interval
)},
433 {"shutdown_cmd", process_string_tuneable
, &shutdown_cmd
[0],
434 sizeof (shutdown_cmd
)},
435 {"devfsadm_cmd", process_string_tuneable
, &devfsadm_cmd
[0],
436 sizeof (devfsadm_cmd
)},
437 {"fru_devfsadm_cmd", process_string_tuneable
, &fru_devfsadm_cmd
[0],
438 sizeof (fru_devfsadm_cmd
)},
439 {"fan_slow_adjustment", process_int_tuneable
, &fan_slow_adjustment
,
440 sizeof (fan_slow_adjustment
)},
441 {"fan_incr_limit", process_int_tuneable
, &fan_incr_limit
,
442 sizeof (fan_incr_limit
)},
443 {"fan_decr_limit", process_int_tuneable
, &fan_decr_limit
,
444 sizeof (fan_decr_limit
)},
445 {"env_debug", process_int_tuneable
, &env_debug
, sizeof (env_debug
)},
446 { NULL
, NULL
, NULL
, 0}
450 fini_table(table_t
*tblp
)
459 init_table(int npoints
)
467 if ((tblp
= malloc(sizeof (*tblp
))) == NULL
)
470 if ((xy
= malloc(sizeof (*xy
) * npoints
)) == NULL
) {
475 tblp
->nentries
= npoints
;
482 * Temp-LPM Table format:
483 * temp, lpm, temp, lpm, ...
486 parse_lpm_ranges(uint32_t *bufp
, size_t nbytes
)
489 table_t
*tblp
= NULL
;
496 * Table should have at least 2 points
497 * and all points should have x and y values
499 if ((nbytes
< (2 * sizeof (point_t
))) ||
500 (nbytes
& (sizeof (point_t
) - 1))) {
502 envd_log(LOG_ERR
, ENV_INVALID_PROPERTY_FORMAT
,
503 LPM_RANGES_PROPERTY
);
507 /* number of entries in the temp-lpm table */
508 nentries
= nbytes
/sizeof (point_t
);
510 tblp
= init_table(nentries
);
514 /* copy the tuples */
515 tblp
->xymap
[0].x
= (int)*bufp
++;
516 tblp
->xymap
[0].y
= (int)*bufp
++;
517 for (i
= 1; i
< nentries
; ++i
) {
518 tblp
->xymap
[i
].x
= (int)*bufp
++;
519 tblp
->xymap
[i
].y
= (int)*bufp
++;
520 if (tblp
->xymap
[i
].x
<= tblp
->xymap
[i
- 1].x
) {
523 envd_log(LOG_ERR
, ENV_INVALID_PROPERTY_FORMAT
,
524 LPM_RANGES_PROPERTY
);
533 * function: calculates y for a given x based on a table of points
534 * for monotonically increasing x values.
535 * 'tbl' specifies the table to use, 'val' specifies the 'x', returns 'y'
538 y_of_x(table_t
*tbl
, int xval
)
546 entries
= tbl
->nentries
;
548 if (xval
<= xymap
[0].x
)
550 else if (xval
>= xymap
[entries
- 1].x
)
551 return (xymap
[entries
- 1].y
);
553 for (i
= 1; i
< entries
- 1; i
++) {
554 if (xval
== xymap
[i
].x
)
556 if (xval
< xymap
[i
].x
)
561 * Use linear interpolation
563 dy
= (float)(xymap
[i
].y
- xymap
[i
-1].y
);
564 dx
= (float)(xymap
[i
].x
- xymap
[i
-1].x
);
566 newval
= xymap
[i
- 1].y
+ slope
* (xval
- xymap
[i
- 1].x
);
567 return ((int)(newval
+ (newval
>= 0 ? 0.5 : -0.5)));
571 get_lpm_speed(lpm_dev_t
*lpmdevs
, int temp
)
581 for (devp
= lpmdevs
; devp
!= NULL
; devp
= devp
->next
) {
582 if (devp
->temp_lpm_tbl
== NULL
)
584 lpm
= y_of_x(devp
->temp_lpm_tbl
, temp
);
586 envd_log(LOG_INFO
, "ambient %d lpm %d\n", temp
, lpm
);
587 speed
= y_of_x(&lpm_fspeed
, lpm
);
588 maxspeed
= maxspeed
> speed
? maxspeed
: speed
;
590 envd_log(LOG_INFO
, "lpm %d fanspeed %d\n", lpm
, speed
);
596 * Callback function used by ptree_walk_tree_by_class
599 cb_lpm(picl_nodehdl_t nodeh
, void *args
)
601 lpm_dev_t
**retp
= (lpm_dev_t
**)args
;
603 ptree_propinfo_t pinfo
;
604 picl_prophdl_t proph
;
607 table_t
*temp_lpm_tbl
;
610 err
= ptree_get_prop_by_name(nodeh
, LPM_RANGES_PROPERTY
, &proph
);
611 if (err
!= PICL_SUCCESS
)
612 return (PICL_WALK_CONTINUE
);
614 err
= ptree_get_propinfo(proph
, &pinfo
);
615 if ((err
!= PICL_SUCCESS
) ||
616 (pinfo
.piclinfo
.type
!= PICL_PTYPE_BYTEARRAY
))
617 return (PICL_WALK_CONTINUE
);
618 psize
= pinfo
.piclinfo
.size
;
619 bufp
= alloca(psize
);
621 err
= ptree_get_propval(proph
, bufp
, psize
);
622 if (err
!= PICL_SUCCESS
)
623 return (PICL_WALK_CONTINUE
);
625 temp_lpm_tbl
= parse_lpm_ranges(bufp
, psize
);
626 if (temp_lpm_tbl
== NULL
) {
627 return (PICL_WALK_CONTINUE
);
630 newdev
= malloc(sizeof (*newdev
));
631 if (newdev
== NULL
) {
632 fini_table(temp_lpm_tbl
);
633 return (PICL_WALK_TERMINATE
);
636 memset(newdev
, 0, sizeof (*newdev
));
638 newdev
->nodeh
= nodeh
;
639 newdev
->temp_lpm_tbl
= temp_lpm_tbl
;
641 /* add newdev to the list */
642 newdev
->next
= *retp
;
645 return (PICL_WALK_CONTINUE
);
649 * Find all devices advertising "lpm-ranges" property, parse and store
650 * the lpm tables for each device
653 setup_lpm_devices(lpm_dev_t
**devpp
)
655 picl_nodehdl_t plath
;
659 err
= ptree_get_node_by_path("/platform", &plath
);
660 if (err
!= PICL_SUCCESS
)
664 err
= ptree_walk_tree_by_class(plath
, NULL
, (void *)&lpmp
, cb_lpm
);
665 if (err
== PICL_SUCCESS
)
671 * Remove all lpm_devices and their tables.
674 delete_lpm_devices(void)
676 lpm_dev_t
*devp
, *next
;
678 (void) pthread_rwlock_wrlock(&envd_rwlock
);
680 if (lpm_devices
== NULL
) {
681 (void) pthread_rwlock_unlock(&envd_rwlock
);
687 while (devp
!= NULL
) {
688 fini_table(devp
->temp_lpm_tbl
);
696 (void) pthread_rwlock_unlock(&envd_rwlock
);
700 * Translate observed (measured) temperature into expected (correct)
704 xlate_obs2exp(env_sensor_t
*sensorp
, tempr_t temp
)
706 int i
, entries
, new_temp
, denominator
;
710 entries
= sensorp
->obs2exp_cnt
;
711 map
= sensorp
->obs2exp_map
;
712 if (entries
< 2 || map
== NULL
) {
713 /* no map or can't map it */
717 * Any point beyond the range specified by the map is
718 * extrapolated using either the first two or the last
719 * two entries in the map.
721 for (i
= 1; i
< entries
-1; i
++)
722 if (temp
< map
[i
].observed
)
725 * Interpolate/extrapolate the temperature using linear
726 * equation with map[i-1] and map[i] being the two ends
727 * of the line segment.
729 denominator
= map
[i
].observed
- map
[i
-1].observed
;
730 if (denominator
== 0) {
732 * Infinite slope. Since the temperature reading
733 * resolution is 1C, force denominator to 1 to
734 * avoid divide by zero.
738 ftemp
= map
[i
-1].expected
+ (temp
- map
[i
-1].observed
) *
739 (float)(map
[i
].expected
- map
[i
-1].expected
)/denominator
;
740 new_temp
= (int)(ftemp
+ (ftemp
>= 0 ? 0.5 : -0.5));
748 * Translate expected (correct) temperature into observed (measured)
752 xlate_exp2obs(env_sensor_t
*sensorp
, tempr_t temp
)
754 int i
, entries
, new_temp
, denominator
;
757 sensor_thresh_t
*threshp
= sensorp
->temp_thresh
;
759 entries
= sensorp
->obs2exp_cnt
;
760 map
= sensorp
->obs2exp_map
;
761 if (entries
< 2 || map
== NULL
)
762 /* no map or can't map it */
766 * Any point beyond the range specified by the map is
767 * extrapolated using either the first two or the last
768 * two entries in the map.
770 for (i
= 1; i
< entries
-1; i
++)
771 if (temp
< map
[i
].expected
)
775 * Interpolate/extrapolate the temperature using linear
776 * equation with map[i-1] and map[i] being the two ends
777 * of the line segment.
779 denominator
= map
[i
].expected
- map
[i
-1].expected
;
780 if (denominator
== 0) {
782 * Infinite slope. Since the temperature reading
783 * resolution is 1C, force denominator to 1 to
784 * avoid divide by zero.
788 ftemp
= map
[i
-1].observed
+ (temp
- map
[i
-1].expected
) *
789 (float)(map
[i
].observed
- map
[i
-1].observed
)/denominator
;
790 new_temp
= (int)(ftemp
+ (ftemp
>= 0 ? 0.5 : -0.5));
794 if (new_temp
> threshp
->max_limit
)
795 new_temp
= threshp
->max_limit
;
796 else if (new_temp
< threshp
->min_limit
)
797 new_temp
= threshp
->min_limit
;
805 * Check if the specified FRU is present.
806 * Returns 1 if present; 0 otherwise.
809 fru_present(char *path
)
811 char *p
, physpath
[PATH_MAX
];
816 * Construct FRU device path by stripping minor
817 * node name from the path and use di_init() to
818 * see if the node exists.
820 (void) strlcpy(physpath
, path
, sizeof (physpath
));
821 p
= strrchr(physpath
, ':');
824 if ((root_node
= di_init(physpath
, DINFOMINOR
)) != DI_NODE_NIL
) {
828 return (fru_present
);
833 * Get environmental segment from the specified FRU SEEPROM
836 get_envseg(int fd
, void **envsegp
, int *envseglenp
)
838 int i
, segcnt
, envseglen
;
839 section_layout_t section
;
840 segment_layout_t segment
;
843 if (lseek(fd
, (long)SECTION_HDR_OFFSET
, 0) == -1L ||
844 read(fd
, §ion
, sizeof (section
)) != sizeof (section
)) {
849 * Verify we have the correct section and contents are valid
850 * For now, we don't verify the CRC.
852 if (section
.header_tag
!= SECTION_HDR_TAG
||
853 GET_UNALIGN16(§ion
.header_version
[0]) != SECTION_HDR_VER
) {
856 "Invalid section header tag:%x version:%x\n",
858 GET_UNALIGN16(§ion
.header_version
));
863 * Locate our environmental segment
865 segcnt
= section
.segment_count
;
866 for (i
= 0; i
< segcnt
; i
++) {
867 if (read(fd
, &segment
, sizeof (segment
)) != sizeof (segment
)) {
872 "Seg name: %x desc:%x off:%x len:%x\n",
873 GET_UNALIGN16(&segment
.name
),
874 GET_UNALIGN32(&segment
.descriptor
[0]),
875 GET_UNALIGN16(&segment
.offset
),
876 GET_UNALIGN16(&segment
.length
));
878 if (GET_UNALIGN16(&segment
.name
) == ENVSEG_NAME
)
887 * Allocate memory to hold the environmental segment data.
889 envseglen
= GET_UNALIGN16(&segment
.length
);
890 if ((envseg
= malloc(envseglen
)) == NULL
) {
894 if (lseek(fd
, (long)GET_UNALIGN16(&segment
.offset
), 0) == -1L ||
895 read(fd
, envseg
, envseglen
) != envseglen
) {
901 *envseglenp
= envseglen
;
905 for (i
= 0; i
< envseglen
; i
++) {
906 (void) sprintf(&msgbuf
[3*(i
&0xf)], "%2x ", envseg
[i
]);
907 if ((i
& 0xf) == 0xf || i
== (envseglen
-1))
908 envd_log(LOG_INFO
, "envseg[%2x]: %s\n",
918 * Get all environmental segments
921 get_fru_envsegs(void)
923 env_sensor_t
*sensorp
;
924 fruenvseg_t
*frup
, *fruenvsegs
;
925 envseg_layout_t
*envsegp
;
927 int fd
, envseglen
, hdrlen
;
931 for (sensorp
= &envd_sensors
[0]; sensorp
->name
!= NULL
; sensorp
++) {
932 if (sensorp
->fru
== NULL
)
935 for (frup
= fruenvsegs
; frup
!= NULL
; frup
= frup
->next
)
936 if (strcmp(frup
->fru
, sensorp
->fru
) == 0)
942 frup
= (fruenvseg_t
*)malloc(sizeof (fruenvseg_t
));
946 /* add this FRU to our list */
947 frup
->fru
= sensorp
->fru
;
948 frup
->envsegbufp
= NULL
;
950 frup
->next
= fruenvsegs
;
954 * Now get the environmental segment from this FRU
956 (void) strcpy(path
, "/devices");
957 (void) strlcat(path
, sensorp
->fru
, sizeof (path
));
960 fd
= open(path
, O_RDONLY
);
963 "fru SEEPROM: %s fd: %d errno:%d\n",
965 if (fd
== -1 && errno
== ENOENT
&& fru_present(frup
->fru
)) {
966 if (fru_devfsadm_invoked
||
967 fru_devfsadm_cmd
[0] == '\0') {
968 envd_log(LOG_CRIT
, ENV_FRU_OPEN_FAIL
,
969 sensorp
->fru
, errno
, strerror(errno
));
974 * FRU is present but no path exists as
975 * someone rebooted the system without
976 * "-r" option. Let's invoke "devfsadm"
977 * once to create seeprom nodes and try
978 * again so that we can monitor all
979 * accessible sensors properly and prevent
980 * any CPU overheating.
984 "Invoking '%s' to create FRU nodes\n",
986 fru_devfsadm_invoked
= 1;
987 (void) system(fru_devfsadm_cmd
);
992 * Read environmental segment from this FRU SEEPROM
994 if (get_envseg(fd
, &envsegbufp
, &envseglen
) == 0) {
996 * Validate envseg version number and header length
998 envsegp
= (envseg_layout_t
*)envsegbufp
;
999 hdrlen
= sizeof (envseg_layout_t
) -
1000 sizeof (envseg_sensor_t
) +
1001 (envsegp
->sensor_count
) * sizeof (envseg_sensor_t
);
1003 if (envsegp
->version
!= ENVSEG_VERSION
||
1004 envseglen
< hdrlen
) {
1006 * version mismatch or header not big enough
1008 envd_log(LOG_CRIT
, ENV_FRU_BAD_ENVSEG
,
1009 sensorp
->fru
, errno
, strerror(errno
));
1010 if (envsegbufp
!= NULL
)
1011 (void) free(envsegbufp
);
1013 frup
->envseglen
= envseglen
;
1014 frup
->envsegbufp
= envsegbufp
;
1019 return (fruenvsegs
);
1023 * Process environmental segment for all FRUs.
1026 process_fru_envseg()
1028 env_sensor_t
*sensorp
;
1029 sensor_thresh_t
*threshp
;
1030 envseg_layout_t
*envsegp
;
1031 envseg_sensor_data_t
*datap
;
1032 fruenvseg_t
*frup
, *fruenvsegs
;
1033 int i
, envseglen
, sensorcnt
;
1034 uint_t offset
, length
, mapentries
;
1037 * Lookup/read environmental segments from FRU SEEPROMs and
1038 * process it. Note that we read each SEEPROM once as it's
1041 fruenvsegs
= get_fru_envsegs();
1043 for (sensorp
= &envd_sensors
[0]; sensorp
->name
!= NULL
; sensorp
++) {
1044 if (sensorp
->fru
== NULL
)
1048 * Locate our FRU environmental segment
1050 for (frup
= fruenvsegs
; frup
!= NULL
; frup
= frup
->next
)
1051 if (strcmp(frup
->fru
, sensorp
->fru
) == 0)
1053 if (frup
== NULL
|| frup
->envsegbufp
== NULL
)
1056 envsegp
= (envseg_layout_t
*)frup
->envsegbufp
;
1057 envseglen
= frup
->envseglen
;
1058 sensorcnt
= envsegp
->sensor_count
;
1061 * Locate our sensor data record entry
1063 for (i
= 0; i
< sensorcnt
; i
++) {
1066 id
= GET_UNALIGN32(&envsegp
->sensors
[i
].sensor_id
[0]);
1068 envd_log(LOG_INFO
, " sensor[%d]: id:%x\n",
1070 if (id
== sensorp
->fru_sensor
)
1078 * Validate offset/length of our sensor data record
1080 offset
= (uint_t
)GET_UNALIGN16(&envsegp
->sensors
[i
].offset
);
1081 datap
= (envseg_sensor_data_t
*)((intptr_t)frup
->envsegbufp
+
1083 mapentries
= GET_UNALIGN16(&datap
->obs2exp_cnt
);
1084 length
= sizeof (envseg_sensor_data_t
) - sizeof (envseg_map_t
) +
1085 mapentries
* sizeof (envseg_map_t
);
1088 envd_log(LOG_INFO
, "Found sensor_id:%x idx:%x "
1089 "off:%x #maps:%x expected length:%x\n",
1090 sensorp
->fru_sensor
, i
, offset
,
1091 mapentries
, length
);
1093 if (offset
>= envseglen
|| (offset
+length
) > envseglen
) {
1094 /* corrupted sensor record */
1095 envd_log(LOG_CRIT
, ENV_FRU_BAD_SENSOR_ENTRY
,
1096 sensorp
->fru_sensor
, sensorp
->name
, sensorp
->fru
);
1100 if (env_debug
> 1) {
1101 /* print threshold values */
1103 "Thresholds: HPwrOff %d HShutDn %d HWarn %d\n",
1104 datap
->high_power_off
, datap
->high_shutdown
,
1105 datap
->high_warning
);
1107 "Thresholds: LWarn %d LShutDn %d LPwrOff %d\n",
1108 datap
->low_warning
, datap
->low_shutdown
,
1109 datap
->low_power_off
);
1111 /* print policy data */
1113 " Policy type: %d #%d data: %x %x %x %x %x %x\n",
1114 datap
->policy_type
, datap
->policy_entries
,
1115 datap
->policy_data
[0], datap
->policy_data
[1],
1116 datap
->policy_data
[2], datap
->policy_data
[3],
1117 datap
->policy_data
[4], datap
->policy_data
[5]);
1119 /* print map table */
1120 for (i
= 0; i
< mapentries
; i
++) {
1121 envd_log(LOG_INFO
, " Map pair# %d: %d %d\n",
1122 i
, datap
->obs2exp_map
[i
].observed
,
1123 datap
->obs2exp_map
[i
].expected
);
1129 * Copy threshold values
1131 threshp
= sensorp
->temp_thresh
;
1132 threshp
->high_power_off
= datap
->high_power_off
;
1133 threshp
->high_shutdown
= datap
->high_shutdown
;
1134 threshp
->high_warning
= datap
->high_warning
;
1135 threshp
->low_warning
= datap
->low_warning
;
1136 threshp
->low_shutdown
= datap
->low_shutdown
;
1137 threshp
->low_power_off
= datap
->low_power_off
;
1142 threshp
->policy_type
= datap
->policy_type
;
1143 threshp
->policy_entries
= datap
->policy_entries
;
1144 for (i
= 0; i
< MAX_POLICY_ENTRIES
; i
++)
1145 threshp
->policy_data
[i
] =
1146 (tempr_t
)datap
->policy_data
[i
];
1149 * Copy temperature mapping info (discard duplicate entries)
1151 if (sensorp
->obs2exp_map
) {
1152 (void) free(sensorp
->obs2exp_map
);
1153 sensorp
->obs2exp_map
= NULL
;
1154 sensorp
->obs2exp_cnt
= 0;
1156 if (mapentries
> 0) {
1159 tempr_t observed
, expected
;
1161 map
= (tempr_map_t
*)malloc(mapentries
*
1162 sizeof (tempr_map_t
));
1165 envd_log(LOG_CRIT
, ENV_FRU_SENSOR_MAP_NOMEM
,
1166 sensorp
->fru_sensor
, sensorp
->name
,
1171 for (i
= 0, cnt
= 0; i
< mapentries
; i
++) {
1173 observed
= (tempr_t
)
1174 datap
->obs2exp_map
[i
].observed
;
1175 expected
= (tempr_t
)
1176 datap
->obs2exp_map
[i
].expected
;
1178 /* ignore if duplicate entry */
1180 observed
== map
[cnt
-1].observed
&&
1181 expected
== map
[cnt
-1].expected
) {
1184 map
[cnt
].observed
= observed
;
1185 map
[cnt
].expected
= expected
;
1188 sensorp
->obs2exp_cnt
= cnt
;
1189 sensorp
->obs2exp_map
= map
;
1192 if (env_debug
> 2 && sensorp
->obs2exp_cnt
> 1) {
1196 "Measured --> Correct temperature table "
1197 "for sensor: %s\n", sensorp
->name
);
1198 for (i
= -128; i
< 128; i
++) {
1199 (void) sprintf(&msgbuf
[6*(i
&0x7)], "%6d",
1200 xlate_obs2exp(sensorp
, i
));
1201 if ((i
&0x7) == 0x7)
1203 "%8d: %s\n", (i
& ~0x7), msgbuf
);
1206 (void) printf("%8d: %s\n", (i
& ~0x7), msgbuf
);
1209 "Correct --> Measured temperature table "
1210 "for sensor: %s\n", sensorp
->name
);
1211 for (i
= -128; i
< 128; i
++) {
1212 (void) sprintf(&msgbuf
[6*(i
&0x7)], "%6d",
1213 xlate_exp2obs(sensorp
, i
));
1214 if ((i
&0x7) == 0x7)
1216 "%8d: %s\n", (i
& ~0x7), msgbuf
);
1220 "%8d: %s\n", (i
& ~0x7), msgbuf
);
1225 * Deallocate environmental segment list
1227 while (fruenvsegs
) {
1229 fruenvsegs
= frup
->next
;
1230 if (frup
->envsegbufp
!= NULL
)
1231 (void) free(frup
->envsegbufp
);
1237 * Lookup fan and return a pointer to env_fan_t data structure.
1240 fan_lookup(char *name
)
1245 for (i
= 0; (fanp
= envd_fans
[i
]) != NULL
; i
++) {
1246 if (strcmp(fanp
->name
, name
) == 0)
1253 * Lookup sensor and return a pointer to env_sensor_t data structure.
1256 sensor_lookup(char *name
)
1258 env_sensor_t
*sensorp
;
1260 for (sensorp
= &envd_sensors
[0]; sensorp
->name
!= NULL
; sensorp
++) {
1261 if (strcmp(sensorp
->name
, name
) == 0)
1268 * Get current temperature
1269 * Returns -1 on error, 0 if successful
1272 get_temperature(env_sensor_t
*sensorp
, tempr_t
*temp
)
1274 int fd
= sensorp
->fd
;
1280 else if (ioctl(fd
, I2C_GET_TEMPERATURE
, temp
) == -1) {
1282 if (sensorp
->error
== 0) {
1284 envd_log(LOG_WARNING
, ENV_SENSOR_ACCESS_FAIL
,
1285 sensorp
->name
, errno
, strerror(errno
));
1287 } else if (sensorp
->error
!= 0) {
1289 envd_log(LOG_WARNING
, ENV_SENSOR_ACCESS_OK
, sensorp
->name
);
1290 } else if (sensorp
->obs2exp_map
!= NULL
) {
1291 expected_temp
= xlate_obs2exp(sensorp
, (tempr_t
)*temp
);
1294 "sensor: %-13s temp:%d CORRECED to %d\n",
1295 sensorp
->name
, *temp
, (tempr_t
)expected_temp
);
1296 *temp
= (tempr_t
)expected_temp
;
1303 * Get current fan speed
1304 * Returns -1 on error, 0 if successful
1307 get_fan_speed(env_fan_t
*fanp
, fanspeed_t
*fanspeedp
)
1313 if (fan_fd
== -1 || read(fan_fd
, fanspeedp
, sizeof (fanspeed_t
)) !=
1314 sizeof (fanspeed_t
))
1321 * Returns -1 on error, 0 if successful
1324 set_fan_speed(env_fan_t
*fanp
, fanspeed_t fanspeed
)
1330 if (fan_fd
== -1 || write(fan_fd
, &fanspeed
, sizeof (fanspeed
)) !=
1331 sizeof (fanspeed_t
))
1338 * close all fan devices
1341 envd_close_fans(void)
1346 for (i
= 0; (fanp
= envd_fans
[i
]) != NULL
; i
++) {
1347 if (fanp
->fd
!= -1) {
1348 (void) close(fanp
->fd
);
1355 * Close sensor devices
1358 envd_close_sensors(void)
1360 env_sensor_t
*sensorp
;
1362 for (sensorp
= &envd_sensors
[0]; sensorp
->name
!= NULL
; sensorp
++) {
1363 if (sensorp
->fd
!= -1) {
1364 (void) close(sensorp
->fd
);
1376 pm_fd
= open(PM_DEVICE
, O_RDONLY
);
1378 (void) fcntl(pm_fd
, F_SETFD
, FD_CLOEXEC
);
1388 (void) close(pm_fd
);
1394 * Open fan devices and initialize per fan data structure.
1395 * Returns #fans found.
1398 envd_setup_fans(void)
1403 char path
[PATH_MAX
];
1406 sensor_fan_map_t
*sfmap
;
1407 env_sensor_t
*sensorp
;
1410 for (i
= 0; (fanp
= envd_fans
[i
]) != NULL
; i
++) {
1411 if (fanp
->fd
== -1) {
1412 fanp
->sensor_cnt
= 0;
1413 fanp
->cur_speed
= 0;
1414 fanp
->prev_speed
= 0;
1416 (void) strcpy(path
, "/devices");
1417 (void) strlcat(path
, fanp
->devfs_path
, sizeof (path
));
1418 fd
= open(path
, O_RDWR
);
1421 ENV_FAN_OPEN_FAIL
, fanp
->name
,
1422 fanp
->devfs_path
, errno
, strerror(errno
));
1423 fanp
->present
= B_FALSE
;
1426 (void) fcntl(fd
, F_SETFD
, FD_CLOEXEC
);
1428 fanp
->present
= B_TRUE
;
1433 * Set initial speed and update cur_speed/prev_speed
1435 if (fanp
->forced_speed
>= 0) {
1436 speed
= (fanspeed_t
)fanp
->forced_speed
;
1437 if (speed
> fanp
->speed_max
)
1438 speed
= fanp
->speed_max
;
1439 if (!disable_piclenvd
)
1440 (void) set_fan_speed(fanp
, speed
);
1441 } else if (get_fan_speed(fanp
, &speed
) == -1) {
1443 * The Fan driver does not know the current fan speed.
1444 * Initialize all ON/OFF fans to ON state and all
1445 * variable speed fans under software control to 50%
1446 * of the max speed and reread the fan to get the
1449 speed
= (fanp
== &envd_psupply_fan
) ?
1450 fanp
->speed_max
: fanp
->speed_max
/2;
1451 if (!disable_piclenvd
) {
1452 (void) set_fan_speed(fanp
, speed
);
1453 if (get_fan_speed(fanp
, &speed
) == -1)
1457 fanp
->cur_speed
= speed
;
1458 fanp
->prev_speed
= speed
;
1461 * Process sensor_fan_map[] table and initialize sensors[]
1462 * array for this fan.
1464 fan_name
= fanp
->name
;
1465 for (sensor_cnt
= 0, sfmap
= &sensor_fan_map
[0];
1466 sfmap
->sensor_name
!= NULL
; sfmap
++) {
1467 if (strcmp(sfmap
->fan_name
, fan_name
) != 0)
1469 sensorp
= sensor_lookup(sfmap
->sensor_name
);
1470 if (sensorp
!= NULL
&& sensor_cnt
< SENSORS_PER_FAN
) {
1471 fanp
->sensors
[sensor_cnt
] = sensorp
;
1475 fanp
->sensor_cnt
= sensor_cnt
;
1483 * Adjust specified sensor target temperature and fan adjustment rate
1487 adjust_sensor_target(env_sensor_t
*sensorp
)
1490 sensor_pmdev_t
*pmdevp
;
1491 sensor_thresh_t
*threshp
;
1495 * Look at current power state of all power managed devices
1496 * associated with this sensor and look up the desired target
1497 * temperature and pick the lowest one of those values. Also,
1498 * calculate the rate of change based upon whether one or more
1499 * of the associated power managed devices are not running at
1503 if (sensorp
== NULL
|| (threshp
= sensorp
->temp_thresh
) == NULL
||
1504 threshp
->policy_type
!= POLICY_TARGET_TEMP
)
1507 target
= threshp
->policy_data
[0];
1509 for (pmdevp
= sensorp
->pmdevp
; pmdevp
!= NULL
; pmdevp
= pmdevp
->next
) {
1510 index
= pmdevp
->full_power
- pmdevp
->cur_power
;
1514 /* not running at full power */
1515 if (index
>= threshp
->policy_entries
)
1516 index
= threshp
->policy_entries
- 1;
1517 if (target
> threshp
->policy_data
[index
])
1518 target
= threshp
->policy_data
[index
];
1519 if (rate
> (float)fan_slow_adjustment
/100)
1520 rate
= (float)fan_slow_adjustment
/100;
1523 "pmdev: %-13s new_target:%d cur:%d power:%d/%d\n",
1524 pmdevp
->pmdev_name
, target
, sensorp
->target_temp
,
1525 pmdevp
->cur_power
, pmdevp
->full_power
);
1530 "sensor: %-13s new_target:%d cur:%d power:%d/%d\n",
1531 sensorp
->name
, target
, sensorp
->target_temp
,
1532 ((sensorp
->pmdevp
) ? sensorp
->pmdevp
->cur_power
: -1),
1533 ((sensorp
->pmdevp
) ? sensorp
->pmdevp
->full_power
: -1));
1535 sensorp
->fan_adjustment_rate
= rate
;
1536 sensorp
->target_temp
= target
;
1540 * Update current power level of all PM devices we are tracking and adjust
1541 * the target temperature associated with the corresponding sensor.
1543 * Returns 1 if one or more pmdev power level was adjusted; 0 otherwise.
1546 update_pmdev_power()
1548 sensor_pmdev_t
*pmdevp
;
1553 for (pmdevp
= sensor_pmdevs
; pmdevp
->pmdev_name
!= NULL
; pmdevp
++) {
1554 pmreq
.physpath
= pmdevp
->pmdev_name
;
1557 pmreq
.component
= pmdevp
->speed_comp
;
1558 cur_power
= ioctl(pm_fd
, PM_GET_CURRENT_POWER
, &pmreq
);
1559 if (pmdevp
->cur_power
!= cur_power
) {
1560 pmdevp
->cur_power
= cur_power
;
1561 if (pmdevp
->sensorp
) {
1562 adjust_sensor_target(pmdevp
->sensorp
);
1571 * Check if the specified sensor is present.
1572 * Returns 1 if present; 0 otherwise.
1574 * Note that we don't use ptree_get_node_by_path() here to detect
1575 * if a temperature device is present as we don't want to make
1576 * "devtree" a critical plugin.
1579 envd_sensor_present(env_sensor_t
*sensorp
)
1581 char *p
, physpath
[PATH_MAX
];
1582 di_node_t root_node
;
1583 int sensor_present
= 0;
1586 * Construct temperature device path by stripping minor
1587 * node name from the devfs_path and use di_init() to
1588 * see if the node exists.
1590 (void) strcpy(physpath
, sensorp
->devfs_path
);
1591 p
= strrchr(physpath
, ':');
1594 if ((root_node
= di_init(physpath
, DINFOMINOR
)) != DI_NODE_NIL
) {
1598 return (sensor_present
);
1602 * Open temperature sensor devices and initialize per sensor data structure.
1603 * Returns #sensors found.
1606 envd_setup_sensors(void)
1609 env_sensor_t
*sensorp
;
1610 char path
[PATH_MAX
];
1613 sensor_thresh_t
*threshp
;
1614 sensor_pmdev_t
*pmdevp
;
1616 for (sensorp
= &envd_sensors
[0]; sensorp
->name
!= NULL
; sensorp
++) {
1617 if (sensorp
->fd
!= -1) {
1618 /* Don't reinitialize opened sensor */
1619 threshp
= sensorp
->temp_thresh
;
1620 sensorp
->pmdevp
= NULL
;
1622 /* Initialize sensor's initial state */
1623 sensorp
->shutdown_initiated
= B_FALSE
;
1624 sensorp
->warning_tstamp
= 0;
1625 sensorp
->warning_start
= 0;
1626 sensorp
->shutdown_tstamp
= 0;
1627 sensorp
->pmdevp
= NULL
;
1628 sensorp
->fan_adjustment_rate
= 1.0;
1630 threshp
= sensorp
->temp_thresh
;
1631 temp
= (threshp
&& threshp
->policy_entries
> 0) ?
1632 threshp
->policy_data
[0] : 0;
1633 sensorp
->target_temp
= temp
;
1634 sensorp
->cur_temp
= temp
;
1635 sensorp
->avg_temp
= temp
;
1636 sensorp
->prev_avg_temp
= temp
;
1639 (void) strcpy(path
, "/devices");
1640 (void) strlcat(path
, sensorp
->devfs_path
,
1643 sensorp
->fd
= open(path
, O_RDWR
);
1644 if (sensorp
->fd
== -1) {
1645 sensor_present
= envd_sensor_present(sensorp
);
1646 if (sensor_present
&& !devfsadm_invoked
&&
1647 devfsadm_cmd
[0] != '\0') {
1649 * Sensor is present but no path
1650 * exists as someone rebooted the
1651 * system without "-r" option. Let's
1652 * invoke "devfsadm" once to create
1653 * max1617 sensors paths in /devices
1654 * subtree and try again so that we
1655 * can monitor all accessible sensors
1656 * and prevent any CPU overheating.
1658 * Note that this routine is always
1659 * called in main thread context and
1660 * serialized with respect to other
1661 * plugins' initialization. Hence, it's
1662 * safe to use system(3C) call here.
1664 devfsadm_invoked
= 1;
1665 (void) system(devfsadm_cmd
);
1670 ENV_SENSOR_OPEN_FAIL
,
1672 sensorp
->devfs_path
, errno
,
1674 sensorp
->present
= B_FALSE
;
1677 (void) fcntl(sensorp
->fd
, F_SETFD
, FD_CLOEXEC
);
1678 sensorp
->present
= B_TRUE
;
1681 * Set cur_temp field to the current temperature value
1683 if (get_temperature(sensorp
, &temp
) == 0) {
1684 sensorp
->cur_temp
= temp
;
1685 sensorp
->avg_temp
= temp
;
1691 * Set low_power_off and high_power_off limits
1693 if (threshp
&& !disable_power_off
) {
1694 temp
= xlate_exp2obs(sensorp
, threshp
->low_power_off
);
1696 envd_log(LOG_INFO
, "sensor: %-13s low_power_"
1697 "off set to %d (real %d)\n", sensorp
->name
,
1698 (int)temp
, threshp
->low_power_off
);
1699 (void) ioctl(sensorp
->fd
, MAX1617_SET_LOW_LIMIT
, &temp
);
1701 temp
= xlate_exp2obs(sensorp
, threshp
->high_power_off
);
1703 envd_log(LOG_INFO
, "sensor: %-13s high_power_"
1704 "off set to %d (real %d)\n", sensorp
->name
,
1705 (int)temp
, threshp
->high_power_off
);
1706 (void) ioctl(sensorp
->fd
, MAX1617_SET_HIGH_LIMIT
,
1712 * Locate "CPU Speed" component for any PM devices associated with
1715 for (pmdevp
= sensor_pmdevs
; pmdevp
->sensor_name
; pmdevp
++) {
1717 char physpath
[PATH_MAX
];
1720 pmdevp
->speed_comp
= -1;
1721 pmdevp
->full_power
= -1;
1722 pmdevp
->cur_power
= -1;
1723 pmdevp
->next
= NULL
;
1724 pmdevp
->sensorp
= sensorp
= sensor_lookup(pmdevp
->sensor_name
);
1727 * Lookup speed component and get full and current power
1728 * level for that component.
1730 pmreq
.physpath
= pmdevp
->pmdev_name
;
1731 pmreq
.data
= physpath
;
1732 pmreq
.datasize
= sizeof (physpath
);
1734 ncomp
= ioctl(pm_fd
, PM_GET_NUM_COMPONENTS
, &pmreq
);
1735 for (i
= 0; i
< ncomp
; i
++) {
1736 pmreq
.component
= i
;
1738 if (ioctl(pm_fd
, PM_GET_COMPONENT_NAME
, &pmreq
) <= 0)
1740 if (strcasecmp(pmreq
.data
, pmdevp
->speed_comp_name
))
1742 pmdevp
->speed_comp
= i
;
1746 * Get full power and current power level
1748 pmdevp
->full_power
= ioctl(pm_fd
, PM_GET_FULL_POWER
,
1751 pmdevp
->cur_power
= ioctl(pm_fd
, PM_GET_CURRENT_POWER
,
1755 pmdevp
->next
= sensorp
->pmdevp
;
1756 sensorp
->pmdevp
= pmdevp
;
1757 adjust_sensor_target(sensorp
);
1763 "sensor:%s %p pmdev:%s comp:%s %d power:%d/%d\n",
1764 pmdevp
->sensor_name
, pmdevp
->sensorp
,
1765 pmdevp
->pmdev_name
, pmdevp
->speed_comp_name
,
1766 pmdevp
->speed_comp
, pmdevp
->cur_power
,
1767 pmdevp
->full_power
);
1773 * Read all temperature sensors and take appropriate action based
1774 * upon temperature threshols associated with each sensor. Possible
1777 * temperature > high_shutdown
1778 * temperature < low_shutdown
1779 * Gracefully shutdown the system and log/print a message
1780 * on the system console provided the temperature has been
1781 * in shutdown range for "shutdown_interval" seconds.
1783 * high_warning < temperature <= high_shutdown
1784 * low_warning > temperature >= low_shutdown
1785 * Log/print a warning message on the system console at most
1786 * once every "warning_interval" seconds.
1788 * Note that the current temperature is recorded in the "cur_temp" field
1789 * within each env_sensor_t structure.
1792 monitor_sensors(void)
1795 env_sensor_t
*sensorp
;
1796 sensor_thresh_t
*threshp
;
1798 char msgbuf
[BUFSIZ
];
1799 char syscmd
[BUFSIZ
];
1801 for (sensorp
= &envd_sensors
[0]; sensorp
->name
!= NULL
; sensorp
++) {
1802 if (get_temperature(sensorp
, &temp
) < 0)
1805 sensorp
->prev_avg_temp
= sensorp
->avg_temp
;
1806 sensorp
->cur_temp
= temp
;
1807 sensorp
->avg_temp
= (sensorp
->avg_temp
+ temp
)/2;
1808 threshp
= sensorp
->temp_thresh
;
1812 "sensor: %-13s temp prev_avg:%6.2f "
1813 "cur:%d avg_temp:%6.2f power:%d/%d target:%d\n",
1814 sensorp
->name
, sensorp
->prev_avg_temp
,
1815 temp
, sensorp
->avg_temp
, ((sensorp
->pmdevp
) ?
1816 sensorp
->pmdevp
->cur_power
: -1),
1817 ((sensorp
->pmdevp
) ? sensorp
->pmdevp
->full_power
:
1818 -1), sensorp
->target_temp
);
1822 * If this sensor already triggered system shutdown, don't
1823 * log any more shutdown/warning messages for it.
1825 if (sensorp
->shutdown_initiated
|| threshp
== NULL
)
1829 * Check for the temperature in warning and shutdown range
1830 * and take appropriate action.
1832 if (TEMP_IN_WARNING_RANGE(temp
, threshp
) && !disable_warning
) {
1834 * Check if the temperature has been in warning
1835 * range during last warning_duration interval.
1836 * If so, the temperature is truly in warning
1837 * range and we need to log a warning message,
1838 * but no more than once every warning_interval
1841 time_t wtstamp
= sensorp
->warning_tstamp
;
1843 ct
= (time_t)(gethrtime() / NANOSEC
);
1844 if (sensorp
->warning_start
== 0)
1845 sensorp
->warning_start
= ct
;
1846 if (((ct
- sensorp
->warning_start
) >=
1847 warning_duration
) && (wtstamp
== 0 ||
1848 (ct
- wtstamp
) >= warning_interval
)) {
1849 envd_log(LOG_CRIT
, ENV_WARNING_MSG
,
1850 sensorp
->name
, temp
,
1851 threshp
->low_warning
,
1852 threshp
->high_warning
);
1853 sensorp
->warning_tstamp
= ct
;
1855 } else if (sensorp
->warning_start
!= 0)
1856 sensorp
->warning_start
= 0;
1858 if (TEMP_IN_SHUTDOWN_RANGE(temp
, threshp
) &&
1859 !disable_shutdown
) {
1860 ct
= (time_t)(gethrtime() / NANOSEC
);
1861 if (sensorp
->shutdown_tstamp
== 0)
1862 sensorp
->shutdown_tstamp
= ct
;
1865 * Shutdown the system if the temperature remains
1866 * in the shutdown range for over shutdown_interval
1869 if ((ct
- sensorp
->shutdown_tstamp
) >=
1870 shutdown_interval
) {
1872 sensorp
->shutdown_initiated
= B_TRUE
;
1873 (void) snprintf(msgbuf
, sizeof (msgbuf
),
1874 ENV_SHUTDOWN_MSG
, sensorp
->name
,
1875 temp
, threshp
->low_shutdown
,
1876 threshp
->high_shutdown
);
1877 envd_log(LOG_ALERT
, msgbuf
);
1879 /* shutdown the system (only once) */
1880 if (system_shutdown_started
== B_FALSE
) {
1881 (void) snprintf(syscmd
, sizeof (syscmd
),
1882 "%s \"%s\"", shutdown_cmd
, msgbuf
);
1883 envd_log(LOG_ALERT
, syscmd
);
1884 system_shutdown_started
= B_TRUE
;
1885 (void) system(syscmd
);
1888 } else if (sensorp
->shutdown_tstamp
!= 0)
1889 sensorp
->shutdown_tstamp
= 0;
1895 * Adjust fan speed based upon the current temperature value of various
1896 * sensors affected by the specified fan.
1899 adjust_fan_speed(env_fan_t
*fanp
, lpm_dev_t
*devp
)
1902 fanspeed_t fanspeed
;
1903 float speed
, cur_speed
, new_speed
, max_speed
, min_speed
;
1904 env_sensor_t
*sensorp
;
1905 sensor_thresh_t
*threshp
;
1907 float avg_temp
, tempdiff
, targetdiff
;
1913 * Get current fan speed
1915 if (get_fan_speed(fanp
, &fanspeed
) < 0)
1917 cur_speed
= fanp
->cur_speed
;
1918 if (fanspeed
!= (int)cur_speed
)
1919 cur_speed
= (float)fanspeed
;
1922 * Calculate new fan speed for each sensor and pick the largest one.
1924 min_speed
= fanp
->speed_min
;
1925 max_speed
= fanp
->speed_max
;
1930 for (i
= 0; i
< fanp
->sensor_cnt
; i
++) {
1931 sensorp
= fanp
->sensors
[i
];
1932 if (sensorp
== NULL
|| sensorp
->fd
== -1 ||
1933 sensorp
->temp_thresh
== NULL
)
1936 temp
= sensorp
->cur_temp
;
1937 avg_temp
= sensorp
->avg_temp
;
1938 threshp
= sensorp
->temp_thresh
;
1941 * Note ambient temperatures to determine lpm for system fan
1943 if ((devp
!= NULL
) &&
1944 (sensorp
->flags
& SFLAG_CPU_AMB_SENSOR
)) {
1950 * If the current temperature is above the warning
1951 * threshold, use max fan speed.
1953 if (temp
>= threshp
->high_warning
) {
1956 } else if (temp
<= threshp
->low_warning
) {
1961 if (threshp
->policy_type
== POLICY_TARGET_TEMP
) {
1963 * Try to achieve the desired target temperature.
1964 * Calculate new fan speed based upon whether the
1965 * temperature is rising, falling or steady state.
1966 * Also take into consideration the current fan
1967 * speed as well as the desired target temperature.
1969 float delta
, speed_change
;
1972 targetdiff
= avg_temp
- sensorp
->target_temp
;
1973 tempdiff
= avg_temp
- sensorp
->prev_avg_temp
;
1975 if (tempdiff
> AVG_TEMP_HYSTERESIS
) {
1977 * Temperature is rising. Increase fan
1978 * speed 0.5% for every 1C above the
1979 * (target - RISING_TEMP_MARGIN) limit.
1980 * Also take into consideration temperature
1981 * rising rate and the current fan speed.
1983 delta
= max_speed
* .005 *
1984 (RISING_TEMP_MARGIN
+ targetdiff
);
1988 multiplier
= tempdiff
/4 +
1989 ((cur_speed
< max_speed
/2) ?
1991 } else if (tempdiff
< -AVG_TEMP_HYSTERESIS
) {
1993 * Temperature is falling. Decrease fan
1994 * speed 0.5% for every 1C below the
1995 * (target + FALLING_TEMP_MARGIN) limit.
1996 * Also take into consideration temperature
1997 * falling rate and the current fan speed.
1999 delta
= -max_speed
* .005 *
2000 (FALLING_TEMP_MARGIN
- targetdiff
);
2004 multiplier
= -tempdiff
/4 +
2005 ((cur_speed
> max_speed
/2) ?
2009 * Temperature is changing very slowly.
2010 * Adjust fan speed by 0.4% for every 1C
2011 * below/above the target temperature.
2013 delta
= max_speed
* .004 * targetdiff
;
2019 * Enforece some bounds on multiplier and the
2022 multiplier
= MIN(multiplier
, 3.0);
2023 speed_change
= delta
* multiplier
*
2024 sensorp
->fan_adjustment_rate
;
2025 speed_change
= MIN(speed_change
, fan_incr_limit
);
2026 speed_change
= MAX(speed_change
, -fan_decr_limit
);
2027 new_speed
= cur_speed
+ speed_change
;
2031 "sensor: %-8s temp/diff:%d/%3.1f "
2032 "target/diff:%d/%3.1f change:%4.2f x "
2033 "%4.2f x %4.2f speed %5.2f -> %5.2f\n",
2034 sensorp
->name
, temp
, tempdiff
,
2035 sensorp
->target_temp
, targetdiff
, delta
,
2036 multiplier
, sensorp
->fan_adjustment_rate
,
2037 cur_speed
, new_speed
);
2038 } else if (threshp
->policy_type
== POLICY_LINEAR
) {
2040 * Set fan speed linearly within the operating
2041 * range specified by the policy_data[LOW_NOMINAL_LOC]
2042 * and policy_data[HIGH_NOMINAL_LOC] threshold values.
2043 * Fan speed is set to minimum value at LOW_NOMINAL
2044 * and to maximum value at HIGH_NOMINAL value.
2046 new_speed
= min_speed
+ (max_speed
- min_speed
) *
2047 (avg_temp
- threshp
->policy_data
[LOW_NOMINAL_LOC
])/
2048 (threshp
->policy_data
[HIGH_NOMINAL_LOC
] -
2049 threshp
->policy_data
[LOW_NOMINAL_LOC
]);
2052 "sensor: %-8s policy: linear, cur_speed %5.2f"\
2053 " new_speed: %5.2f\n", sensorp
->name
, cur_speed
,
2056 new_speed
= cur_speed
;
2058 speed
= MAX(speed
, new_speed
);
2062 * Adjust speed using lpm tables
2065 av_ambient
= (av_ambient
>= 0 ?
2066 (int)(0.5 + (float)av_ambient
/(float)amb_cnt
):
2067 (int)(-0.5 + (float)av_ambient
/(float)amb_cnt
));
2068 speed
= MAX(speed
, (fanspeed_t
)get_lpm_speed(devp
, av_ambient
));
2071 speed
= MIN(speed
, max_speed
);
2072 speed
= MAX(speed
, min_speed
);
2075 * Record and update fan speed, if different.
2077 fanp
->prev_speed
= fanp
->cur_speed
;
2078 fanp
->cur_speed
= speed
;
2079 if ((fanspeed_t
)speed
!= fanspeed
) {
2080 fanspeed
= (fanspeed_t
)speed
;
2081 (void) set_fan_speed(fanp
, fanspeed
);
2085 "fan: %-16s speed cur:%6.2f new:%6.2f\n",
2086 fanp
->name
, fanp
->prev_speed
, fanp
->cur_speed
);
2091 * This is the environment thread, which monitors the current temperature
2092 * and power managed state and controls system fan speed. Temperature is
2093 * polled every sensor-poll_interval seconds duration.
2099 env_sensor_t
*sensorp
;
2100 fanspeed_t fan_speed
;
2101 env_fan_t
*pmfanp
= &envd_psupply_fan
;
2105 for (sensorp
= &envd_sensors
[0]; sensorp
->name
!= NULL
;
2107 if (sensorp
->obs2exp_map
)
2108 (void) free(sensorp
->obs2exp_map
);
2109 sensorp
->obs2exp_map
= NULL
;
2110 sensorp
->obs2exp_cnt
= 0;
2114 * Process environmental segment data, if present,
2115 * in the FRU SEEPROM.
2117 process_fru_envseg();
2120 * Process tuneable parameters
2122 process_env_conf_file();
2125 * Setup temperature sensors and fail if we can't open
2126 * at least one sensor.
2128 if (envd_setup_sensors() <= 0) {
2133 to
= 3 * sensor_poll_interval
+ 1;
2134 xwd
= open(XCALWD_DEVFS
, O_RDONLY
);
2136 envd_log(LOG_CRIT
, ENV_WATCHDOG_INIT_FAIL
, errno
,
2138 } else if (ioctl(xwd
, XCALWD_STOPWATCHDOG
) < 0 ||
2139 ioctl(xwd
, XCALWD_STARTWATCHDOG
, &to
) < 0) {
2140 envd_log(LOG_CRIT
, ENV_WATCHDOG_INIT_FAIL
, errno
,
2147 * Setup fan device (don't fail even if we can't access
2148 * the fan as we can still monitor temeperature.
2150 (void) envd_setup_fans();
2153 (void) pthread_rwlock_rdlock(&envd_rwlock
);
2156 * If no "pmthr" thread, then we need to update the
2157 * current power level for all power managed deviecs
2158 * so that we can determine correct target temperature.
2160 if (pmthr_exists
== B_FALSE
)
2161 (void) update_pmdev_power();
2164 (void) ioctl(xwd
, XCALWD_KEEPALIVE
);
2166 if (!disable_piclenvd
) {
2168 * Monitor current temperature for all sensors
2169 * (current temperature is recorded in the "cur_temp"
2170 * field within each sensor data structure)
2175 * Adjust CPU and system fan speed
2177 if (envd_cpu_fan
.forced_speed
< 0)
2178 (void) adjust_fan_speed(&envd_cpu_fan
, NULL
);
2179 if (envd_system_fan
.forced_speed
< 0)
2180 (void) adjust_fan_speed(&envd_system_fan
,
2184 * Turn off power supply fan if in lowest power state.
2186 fan_speed
= (cur_lpstate
) ? pmfanp
->speed_min
:
2191 "fan: %-16s speed cur:%6.2f new:%6.2f "
2192 "low-power:%d\n", pmfanp
->name
,
2193 (float)pmfanp
->cur_speed
,
2194 (float)fan_speed
, cur_lpstate
);
2196 if (fan_speed
!= (fanspeed_t
)pmfanp
->cur_speed
&&
2197 set_fan_speed(pmfanp
, fan_speed
) == 0)
2198 pmfanp
->cur_speed
= fan_speed
;
2200 (void) pthread_rwlock_unlock(&envd_rwlock
);
2203 * Wait for sensor_poll_interval seconds before polling
2204 * again. Note that we use our own envd_sleep() routine
2205 * as sleep() in POSIX thread library gets affected by
2206 * the wall clock time being set back.
2208 (void) envd_sleep(sensor_poll_interval
);
2215 * This is the power management thread, which monitors all power state
2216 * change events and wakes up the "envthr" thread when the system enters
2217 * or exits the lowest power state.
2223 pm_state_change_t pmstate
;
2224 char physpath
[PATH_MAX
];
2226 pmstate
.physpath
= physpath
;
2227 pmstate
.size
= sizeof (physpath
);
2232 * Get PM state change events to check if the system
2233 * is in lowest power state and wake up the "envthr"
2234 * thread when the power state changes.
2236 * To minimize polling, we use the blocking interface
2237 * to get the power state change event here.
2239 if (ioctl(pm_fd
, PM_GET_STATE_CHANGE_WAIT
, &pmstate
) != 0) {
2246 * Extract the lowest power state from the last queued
2247 * state change events. We pick up queued state change
2248 * events using the non-blocking interface and wake up
2249 * the "envthr" thread only after consuming all the
2250 * state change events queued at that time.
2253 if (env_debug
> 1) {
2255 "pmstate event:0x%x flags:%x comp:%d "
2256 "oldval:%d newval:%d path:%s\n",
2257 pmstate
.event
, pmstate
.flags
,
2258 pmstate
.component
, pmstate
.old_level
,
2259 pmstate
.new_level
, pmstate
.physpath
);
2262 (pmstate
.flags
& PSC_ALL_LOWEST
) ? 1 : 0;
2263 } while (ioctl(pm_fd
, PM_GET_STATE_CHANGE
, &pmstate
) == 0);
2266 * Update current PM state for the components we are
2267 * tracking. In case of CPU devices, PM state change
2268 * event can be generated even before the state change
2269 * takes effect, hence we need to get the current state
2270 * for all CPU devices every time and recalculate the
2271 * target temperature. We do this once after consuming
2272 * all the queued events.
2275 (void) pthread_rwlock_rdlock(&envd_rwlock
);
2276 (void) update_pmdev_power();
2277 (void) pthread_rwlock_unlock(&envd_rwlock
);
2281 * We won't be able to monitor lowest power state any longer,
2285 envd_log(LOG_ERR
, PM_THREAD_EXITING
, errno
, strerror(errno
));
2286 pmthr_exists
= B_FALSE
;
2292 * Process sensor threshold related tuneables
2295 process_threshold_tuneable(char *keyword
, char *buf
, void *dummy_thresh_addr
,
2296 int flags
, char *fname
, int line
)
2302 env_sensor_t
*sensorp
;
2305 * Tuneable entry can be in one of the following formats:
2307 * threshold-keyword <int-value>
2308 * threshold-keyword <int-value> <sensor-name> ...
2310 * Convert threshold value into integer value and check for
2311 * optional sensor name. If no sensor name is specified, then
2312 * the tuneable applies to all sensors specified by the "flags".
2313 * Otherwise, it is applicable to the specified sensors.
2315 * Note that the dummy_thresh_addr is the address of the threshold
2316 * to be changed and is converted into offset by subtracting the
2317 * base dummy_thresh address. This offset is added to the base
2318 * address of the threshold structure to be update to determine
2319 * the final memory address to be modified.
2323 val
= strtol(buf
, &endp
, 0);
2324 sname
= strtok(endp
, tokdel
);
2326 if (errno
!= 0 || val
!= (tempr_t
)val
) {
2328 envd_log(LOG_INFO
, ENV_CONF_INT_EXPECTED
, fname
, line
, keyword
);
2329 } else if (flags
== 0 && sname
== NULL
) {
2330 envd_log(LOG_INFO
, "SUNW_piclenvd: file:%s line:%d SKIPPED"
2331 " as no sensor specified.\n", fname
, line
, keyword
);
2333 } else if (sname
== NULL
) {
2336 for (sensorp
= &envd_sensors
[0]; sensorp
->name
; sensorp
++) {
2337 if (sensorp
->temp_thresh
== NULL
||
2338 (sensorp
->flags
& flags
) == 0)
2342 * Convert dummy_thresh_addr into memory address
2343 * for this sensor threshold values.
2345 addr
= (char *)sensorp
->temp_thresh
+
2346 (int)((char *)dummy_thresh_addr
-
2347 (char *)&dummy_thresh
);
2349 *(tempr_t
*)addr
= (tempr_t
)val
;
2352 envd_log(LOG_INFO
, "SUNW_piclenvd: file:%s "
2353 "line:%d %s = %d for sensor: '%s'\n",
2354 fname
, line
, keyword
, val
, sensorp
->name
);
2357 envd_log(LOG_INFO
, "SUNW_piclenvd: file:%s line:%d "
2358 "%s SKIPPED as no matching sensor found.\n",
2359 fname
, line
, keyword
);
2361 /* apply threshold value to the specified sensors */
2363 sensorp
= sensor_lookup(sname
);
2364 if (sensorp
== NULL
|| sensorp
->temp_thresh
== NULL
||
2365 (flags
&& (sensorp
->flags
& flags
) == 0)) {
2367 "SUNW_piclenvd: file:%s line:%d %s SKIPPED"
2368 " for '%s' as not a valid sensor.\n",
2369 fname
, line
, keyword
, sname
);
2373 * Convert dummy_thresh_addr into memory address
2374 * for this sensor threshold values.
2376 addr
= (char *)sensorp
->temp_thresh
+
2377 (int)((char *)dummy_thresh_addr
-
2378 (char *)&dummy_thresh
);
2380 *(tempr_t
*)addr
= (tempr_t
)val
;
2382 envd_log(LOG_INFO
, "SUNW_piclenvd: file:%s "
2383 "line:%d %s = %d for sensor: '%s'\n",
2384 fname
, line
, keyword
, val
, sensorp
->name
);
2385 } while ((sname
= strtok(NULL
, tokdel
)) != NULL
);
2392 * Process integer tuneables
2395 process_int_tuneable(char *keyword
, char *buf
, void *addr
, int size
,
2396 char *fname
, int line
)
2403 * Convert input into integer value and ensure that there is
2404 * no other token in the buffer.
2407 val
= strtol(buf
, &endp
, 0);
2408 if (errno
!= 0 || strtok(endp
, tokdel
) != NULL
)
2413 if (val
!= (int8_t)val
)
2416 *(int8_t *)addr
= (int8_t)val
;
2419 if (val
!= (short)val
)
2422 *(short *)addr
= (short)val
;
2425 *(int *)addr
= (int)val
;
2433 envd_log(LOG_INFO
, ENV_CONF_INT_EXPECTED
,
2434 fname
, line
, keyword
);
2436 envd_log(LOG_INFO
, "SUNW_piclenvd: file:%s line:%d %s = %d\n",
2437 fname
, line
, keyword
, val
);
2444 * Process string tuneables
2446 * String value must be within double quotes. Skip over initial white
2447 * spaces before looking for string value.
2450 process_string_tuneable(char *keyword
, char *buf
, void *addr
, int size
,
2451 char *fname
, int line
)
2454 char c
, *p
, *strend
;
2456 /* Skip over white spaces */
2457 buf
+= strspn(buf
, tokdel
);
2460 * Parse srting and locate string end (handling escaped double quotes
2461 * and other characters)
2466 for (p
= buf
+1; (c
= *p
) != '\0'; p
++)
2467 if (c
== '"' || (c
== '\\' && *++p
== '\0'))
2469 strend
= (*p
== '"') ? p
: NULL
;
2472 if (strend
== NULL
|| (strend
-buf
) > size
||
2473 strtok(strend
+1, tokdel
) != NULL
) {
2474 envd_log(LOG_WARNING
, ENV_CONF_STRING_EXPECTED
,
2475 fname
, line
, keyword
, size
);
2479 (void) strcpy(addr
, (caddr_t
)buf
+1);
2481 envd_log(LOG_INFO
, "SUNW_piclenvd: file:%s line:%d "
2482 "%s = \"%s\"\n", fname
, line
, keyword
, buf
+1);
2490 * Process configuration file
2493 process_env_conf_file(void)
2495 int line
, len
, toklen
;
2498 env_tuneable_t
*tunep
;
2499 char nmbuf
[SYS_NMLN
];
2500 char fname
[PATH_MAX
];
2504 if (sysinfo(SI_PLATFORM
, nmbuf
, sizeof (nmbuf
)) == -1)
2507 (void) snprintf(fname
, sizeof (fname
), PICLD_PLAT_PLUGIN_DIRF
, nmbuf
);
2508 (void) strlcat(fname
, ENV_CONF_FILE
, sizeof (fname
));
2509 fp
= fopen(fname
, "r");
2514 * Blank lines or lines starting with "#" or "*" in the first
2515 * column are ignored. All other lines are assumed to contain
2516 * input in the following format:
2520 * where the "value" can be a signed integer or string (in
2521 * double quotes) depending upon the keyword.
2524 for (line
= 1; fgets(buf
, sizeof (buf
), fp
) != NULL
; line
++) {
2529 /* skip long lines */
2530 if (buf
[len
-1] != '\n') {
2533 } else if (skip_line
) {
2540 if (buf
[0] == '*' || buf
[0] == '#')
2544 * Skip over white space to get the keyword
2546 tok
= buf
+ strspn(buf
, tokdel
);
2548 continue; /* blank line */
2550 toklen
= strcspn(tok
, tokdel
);
2553 /* Get possible location for value (within current line) */
2554 valuep
= tok
+ toklen
+ 1;
2555 if (valuep
> buf
+len
)
2559 * Lookup the keyword and process value accordingly
2561 for (tunep
= &env_tuneables
[0]; tunep
->name
!= NULL
; tunep
++) {
2562 if (strcasecmp(tunep
->name
, tok
) == 0) {
2563 (void) (*tunep
->func
)(tok
, valuep
,
2564 tunep
->arg1
, tunep
->arg2
, fname
, line
);
2569 if (tunep
->name
== NULL
)
2570 envd_log(LOG_INFO
, ENV_CONF_UNSUPPORTED_KEYWORD
,
2577 * Setup envrionmental monitor state and start threads to monitor
2578 * temperature and power management state.
2579 * Returns -1 on error, 0 if successful.
2589 if (pthread_attr_init(&thr_attr
) != 0 ||
2590 pthread_attr_setscope(&thr_attr
, PTHREAD_SCOPE_SYSTEM
) != 0)
2600 if ((err
= setup_lpm_devices(&lpm_devices
)) != PICL_SUCCESS
) {
2602 envd_log(LOG_ERR
, "setup_lpm_devices failed err = %d\n",
2607 * Initialize global state to initial startup values
2609 sensor_poll_interval
= SENSOR_POLL_INTERVAL
;
2610 fan_slow_adjustment
= FAN_SLOW_ADJUSTMENT
;
2611 fan_incr_limit
= FAN_INCREMENT_LIMIT
;
2612 fan_decr_limit
= FAN_DECREMENT_LIMIT
;
2613 warning_interval
= WARNING_INTERVAL
;
2614 warning_duration
= WARNING_DURATION
;
2615 shutdown_interval
= SHUTDOWN_INTERVAL
;
2616 disable_piclenvd
= 0;
2617 disable_power_off
= 0;
2618 disable_shutdown
= 0;
2619 disable_warning
= 0;
2621 (void) strlcpy(shutdown_cmd
, SHUTDOWN_CMD
, sizeof (shutdown_cmd
));
2622 (void) strlcpy(devfsadm_cmd
, DEVFSADM_CMD
, sizeof (devfsadm_cmd
));
2623 (void) strlcpy(fru_devfsadm_cmd
, FRU_DEVFSADM_CMD
,
2624 sizeof (fru_devfsadm_cmd
));
2625 envd_cpu_fan
.forced_speed
= -1;
2626 envd_system_fan
.forced_speed
= -1;
2628 (void) memcpy(&cpu0_die_thresh
, &cpu_die_thresh_default
,
2629 sizeof (cpu_die_thresh_default
));
2630 (void) memcpy(&cpu0_amb_thresh
, &cpu_amb_thresh_default
,
2631 sizeof (cpu_amb_thresh_default
));
2632 (void) memcpy(&cpu1_die_thresh
, &cpu_die_thresh_default
,
2633 sizeof (cpu_die_thresh_default
));
2634 (void) memcpy(&cpu1_amb_thresh
, &cpu_amb_thresh_default
,
2635 sizeof (cpu_amb_thresh_default
));
2637 if ((valp
= getenv("SUNW_piclenvd_debug")) != NULL
) {
2638 val
= strtol(valp
, &endp
, 0);
2639 if (strtok(endp
, tokdel
) == NULL
)
2644 * Create a thread to monitor temperature and control fan
2647 if (envthr_created
== B_FALSE
&& pthread_create(&envthr_tid
,
2648 &thr_attr
, envthr
, (void *)NULL
) != 0) {
2650 envd_close_sensors();
2652 envd_log(LOG_CRIT
, ENV_THREAD_CREATE_FAILED
);
2655 envthr_created
= B_TRUE
;
2658 * Create a thread to monitor PM state
2660 if (pmthr_exists
== B_FALSE
) {
2661 if (pm_fd
== -1 || pthread_create(&pmthr_tid
, &thr_attr
,
2662 pmthr
, (void *)NULL
) != 0) {
2663 envd_log(LOG_CRIT
, PM_THREAD_CREATE_FAILED
);
2665 pmthr_exists
= B_TRUE
;
2671 * Callback function used by ptree_walk_tree_by_class for the cpu class
2674 cb_cpu(picl_nodehdl_t nodeh
, void *args
)
2676 sensor_pmdev_t
*pmdevp
;
2678 ptree_propinfo_t pinfo
;
2679 picl_prophdl_t proph
;
2683 /* Get CPU's ID, it is an int */
2684 err
= ptree_get_propval_by_name(nodeh
, PICL_PROP_ID
, &id
, sizeof (int));
2685 if (err
!= PICL_SUCCESS
)
2686 return (PICL_WALK_CONTINUE
);
2688 /* Get the pmdevp for the CPU */
2689 pmdevp
= sensor_pmdevs
;
2690 while (pmdevp
->sensor_id
!= -1) {
2691 if (id
== pmdevp
->sensor_id
)
2696 /* Return if didn't find the pmdevp for the cpu id */
2697 if (pmdevp
->sensor_id
== -1)
2698 return (PICL_WALK_CONTINUE
);
2700 /* Get the devfs-path property */
2701 err
= ptree_get_prop_by_name(nodeh
, PICL_PROP_DEVFS_PATH
, &proph
);
2702 if (err
!= PICL_SUCCESS
)
2703 return (PICL_WALK_CONTINUE
);
2705 err
= ptree_get_propinfo(proph
, &pinfo
);
2706 if ((err
!= PICL_SUCCESS
) ||
2707 (pinfo
.piclinfo
.type
!= PICL_PTYPE_CHARSTRING
))
2708 return (PICL_WALK_CONTINUE
);
2710 psize
= pinfo
.piclinfo
.size
;
2711 pmdevp
->pmdev_name
= malloc(psize
);
2712 if (pmdevp
->pmdev_name
== NULL
)
2713 return (PICL_WALK_CONTINUE
);
2715 err
= ptree_get_propval(proph
, pmdevp
->pmdev_name
, psize
);
2716 if (err
!= PICL_SUCCESS
)
2717 return (PICL_WALK_CONTINUE
);
2719 return (PICL_WALK_CONTINUE
);
2723 * Find the CPU's in the picl tree, set the devfs-path for pmdev_name
2728 picl_nodehdl_t plath
;
2731 err
= ptree_get_node_by_path(PLATFORM_PATH
, &plath
);
2732 if (err
!= PICL_SUCCESS
)
2735 err
= ptree_walk_tree_by_class(plath
, PICL_CLASS_CPU
, NULL
, cb_cpu
);
2740 piclenvd_register(void)
2742 picld_plugin_register(&my_reg_info
);
2749 * Setup the names for the pm sensors, we do it just the first time
2751 if (pmdev_names_init
== B_FALSE
) {
2752 (void) setup_pmdev_names();
2753 pmdev_names_init
= B_TRUE
;
2757 * Start environmental monitor/threads
2759 (void) pthread_rwlock_wrlock(&envd_rwlock
);
2760 if (envd_setup() != 0) {
2761 (void) pthread_rwlock_unlock(&envd_rwlock
);
2762 envd_log(LOG_CRIT
, ENVD_PLUGIN_INIT_FAILED
);
2765 (void) pthread_rwlock_unlock(&envd_rwlock
);
2768 * Now setup/populate PICL tree
2777 * Delete the lpm device list. After this the lpm information
2778 * will not be used in determining the fan speed, till the lpm
2779 * device information is initialized by setup_lpm_devices called
2782 delete_lpm_devices();
2785 * Invoke env_picl_destroy() to remove any PICL nodes/properties
2786 * (including volatile properties) we created. Once this call
2787 * returns, there can't be any more calls from the PICL framework
2788 * to get current temperature or fan speed.
2793 * Since this is a critical plug-in, we know that it won't be
2794 * unloaded and will be reinited again unless picld process is
2795 * going away. Therefore, it's okay to let "envthr" and "pmthr"
2796 * continue so that we can monitor the environment during SIGHUP
2803 envd_log(int pri
, const char *fmt
, ...)
2808 vsyslog(pri
, fmt
, ap
);
2814 * Redefine sigwait to posix style external declaration so that LINT
2815 * does not check against libc version of sigwait() and complain as
2816 * it uses different number of arguments.
2818 #define sigwait my_posix_sigwait
2819 extern int my_posix_sigwait(const sigset_t
*set
, int *sig
);
2823 * sleep() in libpthread gets affected by time being set back, hence
2824 * can cause the "envthr" not to wakeup for extended duration. For
2825 * now, we implement our own sleep() routine below using alarm().
2826 * This will work only if SIGALRM is masked off in all other threads.
2827 * Note that SIGALRM signal is masked off in the main thread, hence
2828 * in all threads, including the envthr, the one calling this routine.
2830 * Note that SIGALRM and alarm() can't be used by any other thread
2835 envd_sleep(unsigned int sleep_tm
)
2838 unsigned int unslept
;
2844 (void) sigemptyset(&alrm_mask
);
2845 (void) sigaddset(&alrm_mask
, SIGALRM
);
2847 (void) alarm(sleep_tm
);
2848 (void) sigwait(&alrm_mask
, &sig
);