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 2005 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 /* 30 * This file contains the environmental PICL plug-in module. 31 */ 32 33 34 /* 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. 41 * 42 * The environmental monitor defines the following thresholds per sensor: 43 * 44 * high_power_off high hard shutdown 45 * high_shutdown high soft shutdown limit 46 * high_warning high warning limit 47 * low_warning low warning limit 48 * low_shutdown low soft shutdown limit 49 * low_power_off low hard shutdown limit 50 * 51 * Above mentioned threshold values can be changed via "piclenvd.conf" 52 * configuration file. 53 * 54 * Environmental monitoring is done by the "envthr" thread. It periodically 55 * monitors both CPU die and CPU ambient temperatures and takes appropriate 56 * action depending upon the current temperature and threshold values for 57 * that sensor. If the temperature reaches the high_shutdown limit or the 58 * low_shutdown limit, and remains there for over shutdown_interval seconds, 59 * it forces a graceful system shutdown via tuneable shutdown_cmd string 60 * variable. Otherwise, if the temperature reaches the high_warning limit 61 * or the low_warning limit, it logs and prints a message on the console. 62 * This message will be printed at most at "warning_interval" seconds 63 * interval, which is also a tuneable variable. 64 * 65 * 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 71 * supply fan. 72 * 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 77 * also. 78 * 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. 83 * 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. 89 * 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. 95 */ 96 97 #include <stdio.h> 98 #include <stdlib.h> 99 #include <sys/sysmacros.h> 100 #include <limits.h> 101 #include <string.h> 102 #include <stdarg.h> 103 #include <alloca.h> 104 #include <unistd.h> 105 #include <sys/processor.h> 106 #include <syslog.h> 107 #include <errno.h> 108 #include <fcntl.h> 109 #include <picl.h> 110 #include <picltree.h> 111 #include <picldefs.h> 112 #include <pthread.h> 113 #include <signal.h> 114 #include <libdevinfo.h> 115 #include <sys/pm.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> 123 #include "envd.h" 124 125 static pthread_rwlock_t envd_rwlock = PTHREAD_RWLOCK_INITIALIZER; 126 127 /* 128 * PICL plguin 129 */ 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); 135 136 #pragma init(piclenvd_register) 137 138 static picld_plugin_reg_t my_reg_info = { 139 PICLD_PLUGIN_VERSION_1, 140 PICLD_PLUGIN_CRITICAL, 141 "SUNW_piclenvd", 142 piclenvd_init, 143 piclenvd_fini, 144 }; 145 146 147 /* 148 * Default threshold values for CPU junction/die and ambient sensors 149 */ 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, 157 0, 0, 0, 0 158 }; 159 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, 165 POLICY_LINEAR, 2, 166 CPU_AMB_LOW_NOMINAL, CPU_AMB_HIGH_NOMINAL, 167 0, 0, 0, 0 168 }; 169 170 171 /* 172 * Dummy sensor threshold data structure for processing threshold tuneables 173 */ 174 static sensor_thresh_t dummy_thresh; 175 176 /* 177 * Temperature related constants for fan speed adjustment 178 */ 179 #define AVG_TEMP_HYSTERESIS 0.25 180 #define RISING_TEMP_MARGIN 6 181 #define FALLING_TEMP_MARGIN 3 182 183 /* 184 * tuneable variables 185 */ 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" 191 192 int env_debug; 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; 204 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; 210 211 /* 212 * Temperature sensors 213 */ 214 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} 227 }; 228 229 230 /* 231 * Fan devices 232 */ 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, 236 }; 237 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, 241 }; 242 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, 246 }; 247 248 static env_fan_t *envd_fans[] = { 249 &envd_system_fan, 250 &envd_cpu_fan, 251 &envd_psupply_fan, 252 NULL 253 }; 254 255 /* 256 * Linked list of devices advertising lpm-ranges 257 */ 258 static lpm_dev_t *lpm_devices = NULL; 259 260 /* 261 * Excalibur lpm to system-fan speed 262 * lpm values must be monotonically increasing (avoid divide-by-zero) 263 */ 264 static point_t excal_lpm_system_fan_tbl[] = { 265 /* {lpm, fspeed} */ 266 {18, 12}, 267 {25, 20}, 268 {33, 26}, 269 {44, 32}, 270 {51, 39}, 271 {63, 52}, 272 {64, 63} 273 }; 274 275 static table_t lpm_fspeed = { 276 sizeof (excal_lpm_system_fan_tbl)/ sizeof (point_t), 277 excal_lpm_system_fan_tbl 278 }; 279 280 /* 281 * Sensor to fan map 282 */ 283 typedef struct { 284 char *sensor_name; 285 char *fan_name; 286 } sensor_fan_map_t; 287 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}, 293 {NULL, NULL} 294 }; 295 296 /* 297 * Sensor to PM device map 298 */ 299 struct sensor_pmdev { 300 int sensor_id; 301 char *sensor_name; 302 char *pmdev_name; 303 char *speed_comp_name; 304 int speed_comp; 305 int full_power; 306 int cur_power; 307 env_sensor_t *sensorp; 308 sensor_pmdev_t *next; 309 }; 310 311 #define SPEED_COMPONENT_NAME "CPU Speed" 312 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} 317 }; 318 319 /* 320 * Environmental thread variables 321 */ 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; 326 327 /* 328 * Power management thread (pmthr) variables 329 */ 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 */ 335 336 /* 337 * Miscellaneous variables and declarations 338 */ 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); 343 344 /* 345 * Tuneable data structure/array and processing functions 346 */ 347 348 typedef struct { 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) */ 354 } env_tuneable_t; 355 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); 363 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)}, 382 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}, 399 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}, 416 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} 447 }; 448 449 static void 450 fini_table(table_t *tblp) 451 { 452 if (tblp == NULL) 453 return; 454 free(tblp->xymap); 455 free(tblp); 456 } 457 458 static table_t * 459 init_table(int npoints) 460 { 461 table_t *tblp; 462 point_t *xy; 463 464 if (npoints == 0) 465 return (NULL); 466 467 if ((tblp = malloc(sizeof (*tblp))) == NULL) 468 return (NULL); 469 470 if ((xy = malloc(sizeof (*xy) * npoints)) == NULL) { 471 free(tblp); 472 return (NULL); 473 } 474 475 tblp->nentries = npoints; 476 tblp->xymap = xy; 477 478 return (tblp); 479 } 480 481 /* 482 * Temp-LPM Table format: 483 * temp, lpm, temp, lpm, ... 484 */ 485 static table_t * 486 parse_lpm_ranges(uint32_t *bufp, size_t nbytes) 487 { 488 int nentries; 489 table_t *tblp = NULL; 490 int i; 491 492 if (bufp == NULL) 493 return (NULL); 494 495 /* 496 * Table should have at least 2 points 497 * and all points should have x and y values 498 */ 499 if ((nbytes < (2 * sizeof (point_t))) || 500 (nbytes & (sizeof (point_t) - 1))) { 501 if (env_debug) 502 envd_log(LOG_ERR, ENV_INVALID_PROPERTY_FORMAT, 503 LPM_RANGES_PROPERTY); 504 return (NULL); 505 } 506 507 /* number of entries in the temp-lpm table */ 508 nentries = nbytes/sizeof (point_t); 509 510 tblp = init_table(nentries); 511 if (tblp == NULL) 512 return (tblp); 513 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) { 521 fini_table(tblp); 522 if (env_debug) 523 envd_log(LOG_ERR, ENV_INVALID_PROPERTY_FORMAT, 524 LPM_RANGES_PROPERTY); 525 return (NULL); 526 } 527 } 528 529 return (tblp); 530 } 531 532 /* 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' 536 */ 537 static int 538 y_of_x(table_t *tbl, int xval) 539 { 540 int i; 541 int entries; 542 point_t *xymap; 543 float newval; 544 float dy, dx, slope; 545 546 entries = tbl->nentries; 547 xymap = tbl->xymap; 548 if (xval <= xymap[0].x) 549 return (xymap[0].y); 550 else if (xval >= xymap[entries - 1].x) 551 return (xymap[entries - 1].y); 552 553 for (i = 1; i < entries - 1; i++) { 554 if (xval == xymap[i].x) 555 return (xymap[i].y); 556 if (xval < xymap[i].x) 557 break; 558 } 559 560 /* 561 * Use linear interpolation 562 */ 563 dy = (float)(xymap[i].y - xymap[i-1].y); 564 dx = (float)(xymap[i].x - xymap[i-1].x); 565 slope = dy/dx; 566 newval = xymap[i - 1].y + slope * (xval - xymap[i - 1].x); 567 return ((int)(newval + (newval >= 0 ? 0.5 : -0.5))); 568 } 569 570 static int 571 get_lpm_speed(lpm_dev_t *lpmdevs, int temp) 572 { 573 lpm_dev_t *devp; 574 int lpm; 575 int speed; 576 int maxspeed; 577 578 if (lpmdevs == NULL) 579 return (0); 580 maxspeed = 0; 581 for (devp = lpmdevs; devp != NULL; devp = devp->next) { 582 if (devp->temp_lpm_tbl == NULL) 583 continue; 584 lpm = y_of_x(devp->temp_lpm_tbl, temp); 585 if (env_debug) 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; 589 if (env_debug) 590 envd_log(LOG_INFO, "lpm %d fanspeed %d\n", lpm, speed); 591 } 592 return (maxspeed); 593 } 594 595 /* 596 * Callback function used by ptree_walk_tree_by_class 597 */ 598 static int 599 cb_lpm(picl_nodehdl_t nodeh, void *args) 600 { 601 lpm_dev_t **retp = (lpm_dev_t **)args; 602 int err; 603 ptree_propinfo_t pinfo; 604 picl_prophdl_t proph; 605 size_t psize; 606 void *bufp; 607 table_t *temp_lpm_tbl; 608 lpm_dev_t *newdev; 609 610 err = ptree_get_prop_by_name(nodeh, LPM_RANGES_PROPERTY, &proph); 611 if (err != PICL_SUCCESS) 612 return (PICL_WALK_CONTINUE); 613 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); 620 621 err = ptree_get_propval(proph, bufp, psize); 622 if (err != PICL_SUCCESS) 623 return (PICL_WALK_CONTINUE); 624 625 temp_lpm_tbl = parse_lpm_ranges(bufp, psize); 626 if (temp_lpm_tbl == NULL) { 627 return (PICL_WALK_CONTINUE); 628 } 629 630 newdev = malloc(sizeof (*newdev)); 631 if (newdev == NULL) { 632 fini_table(temp_lpm_tbl); 633 return (PICL_WALK_TERMINATE); 634 } 635 636 memset(newdev, 0, sizeof (*newdev)); 637 638 newdev->nodeh = nodeh; 639 newdev->temp_lpm_tbl = temp_lpm_tbl; 640 641 /* add newdev to the list */ 642 newdev->next = *retp; 643 *retp = newdev; 644 645 return (PICL_WALK_CONTINUE); 646 } 647 648 /* 649 * Find all devices advertising "lpm-ranges" property, parse and store 650 * the lpm tables for each device 651 */ 652 static int 653 setup_lpm_devices(lpm_dev_t **devpp) 654 { 655 picl_nodehdl_t plath; 656 int err; 657 lpm_dev_t *lpmp; 658 659 err = ptree_get_node_by_path("/platform", &plath); 660 if (err != PICL_SUCCESS) 661 return (err); 662 663 lpmp = NULL; 664 err = ptree_walk_tree_by_class(plath, NULL, (void *)&lpmp, cb_lpm); 665 if (err == PICL_SUCCESS) 666 *devpp = lpmp; 667 return (err); 668 } 669 670 /* 671 * Remove all lpm_devices and their tables. 672 */ 673 static void 674 delete_lpm_devices(void) 675 { 676 lpm_dev_t *devp, *next; 677 678 (void) pthread_rwlock_wrlock(&envd_rwlock); 679 680 if (lpm_devices == NULL) { 681 (void) pthread_rwlock_unlock(&envd_rwlock); 682 return; 683 } 684 685 devp = lpm_devices; 686 687 while (devp != NULL) { 688 fini_table(devp->temp_lpm_tbl); 689 next = devp->next; 690 free(devp); 691 devp = next; 692 } 693 694 lpm_devices = NULL; 695 696 (void) pthread_rwlock_unlock(&envd_rwlock); 697 } 698 699 /* 700 * Translate observed (measured) temperature into expected (correct) 701 * temperature 702 */ 703 static int 704 xlate_obs2exp(env_sensor_t *sensorp, tempr_t temp) 705 { 706 int i, entries, new_temp, denominator; 707 tempr_map_t *map; 708 float ftemp; 709 710 entries = sensorp->obs2exp_cnt; 711 map = sensorp->obs2exp_map; 712 if (entries < 2 || map == NULL) { 713 /* no map or can't map it */ 714 new_temp = temp; 715 } else { 716 /* 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. 720 */ 721 for (i = 1; i < entries-1; i++) 722 if (temp < map[i].observed) 723 break; 724 /* 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. 728 */ 729 denominator = map[i].observed - map[i-1].observed; 730 if (denominator == 0) { 731 /* 732 * Infinite slope. Since the temperature reading 733 * resolution is 1C, force denominator to 1 to 734 * avoid divide by zero. 735 */ 736 denominator = 1; 737 } 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)); 741 } 742 743 return (new_temp); 744 } 745 746 747 /* 748 * Translate expected (correct) temperature into observed (measured) 749 * temperature 750 */ 751 static int 752 xlate_exp2obs(env_sensor_t *sensorp, tempr_t temp) 753 { 754 int i, entries, new_temp, denominator; 755 tempr_map_t *map; 756 float ftemp; 757 sensor_thresh_t *threshp = sensorp->temp_thresh; 758 759 entries = sensorp->obs2exp_cnt; 760 map = sensorp->obs2exp_map; 761 if (entries < 2 || map == NULL) 762 /* no map or can't map it */ 763 new_temp = temp; 764 else { 765 /* 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. 769 */ 770 for (i = 1; i < entries-1; i++) 771 if (temp < map[i].expected) 772 break; 773 774 /* 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. 778 */ 779 denominator = map[i].expected - map[i-1].expected; 780 if (denominator == 0) { 781 /* 782 * Infinite slope. Since the temperature reading 783 * resolution is 1C, force denominator to 1 to 784 * avoid divide by zero. 785 */ 786 denominator = 1; 787 } 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)); 791 } 792 793 if (threshp) { 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; 798 } 799 800 return (new_temp); 801 } 802 803 804 /* 805 * Check if the specified FRU is present. 806 * Returns 1 if present; 0 otherwise. 807 */ 808 static int 809 fru_present(char *path) 810 { 811 char *p, physpath[PATH_MAX]; 812 di_node_t root_node; 813 int fru_present = 0; 814 815 /* 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. 819 */ 820 (void) strlcpy(physpath, path, sizeof (physpath)); 821 p = strrchr(physpath, ':'); 822 if (p != NULL) 823 *p = '\0'; 824 if ((root_node = di_init(physpath, DINFOMINOR)) != DI_NODE_NIL) { 825 di_fini(root_node); 826 fru_present = 1; 827 } 828 return (fru_present); 829 } 830 831 832 /* 833 * Get environmental segment from the specified FRU SEEPROM 834 */ 835 static int 836 get_envseg(int fd, void **envsegp, int *envseglenp) 837 { 838 int i, segcnt, envseglen; 839 section_layout_t section; 840 segment_layout_t segment; 841 uint8_t *envseg; 842 843 if (lseek(fd, (long)SECTION_HDR_OFFSET, 0) == -1L || 844 read(fd, §ion, sizeof (section)) != sizeof (section)) { 845 return (EINVAL); 846 } 847 848 /* 849 * Verify we have the correct section and contents are valid 850 * For now, we don't verify the CRC. 851 */ 852 if (section.header_tag != SECTION_HDR_TAG || 853 GET_UNALIGN16(§ion.header_version[0]) != SECTION_HDR_VER) { 854 if (env_debug) 855 envd_log(LOG_INFO, 856 "Invalid section header tag:%x version:%x\n", 857 section.header_tag, 858 GET_UNALIGN16(§ion.header_version)); 859 return (EINVAL); 860 } 861 862 /* 863 * Locate our environmental segment 864 */ 865 segcnt = section.segment_count; 866 for (i = 0; i < segcnt; i++) { 867 if (read(fd, &segment, sizeof (segment)) != sizeof (segment)) { 868 return (errno); 869 } 870 if (env_debug > 1) 871 envd_log(LOG_INFO, 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)); 877 878 if (GET_UNALIGN16(&segment.name) == ENVSEG_NAME) 879 break; 880 } 881 882 if (i >= segcnt) { 883 return (ENOENT); 884 } 885 886 /* 887 * Allocate memory to hold the environmental segment data. 888 */ 889 envseglen = GET_UNALIGN16(&segment.length); 890 if ((envseg = malloc(envseglen)) == NULL) { 891 return (ENOMEM); 892 } 893 894 if (lseek(fd, (long)GET_UNALIGN16(&segment.offset), 0) == -1L || 895 read(fd, envseg, envseglen) != envseglen) { 896 (void) free(envseg); 897 return (EIO); 898 } 899 900 *envsegp = envseg; 901 *envseglenp = envseglen; 902 903 if (env_debug > 1) { 904 char msgbuf[256]; 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", 909 (i & ~0xf), msgbuf); 910 } 911 } 912 913 return (0); 914 } 915 916 917 /* 918 * Get all environmental segments 919 */ 920 static fruenvseg_t * 921 get_fru_envsegs(void) 922 { 923 env_sensor_t *sensorp; 924 fruenvseg_t *frup, *fruenvsegs; 925 envseg_layout_t *envsegp; 926 void *envsegbufp; 927 int fd, envseglen, hdrlen; 928 char path[PATH_MAX]; 929 930 fruenvsegs = NULL; 931 for (sensorp = &envd_sensors[0]; sensorp->name != NULL; sensorp++) { 932 if (sensorp->fru == NULL) 933 continue; 934 935 for (frup = fruenvsegs; frup != NULL; frup = frup->next) 936 if (strcmp(frup->fru, sensorp->fru) == 0) 937 break; 938 939 if (frup != NULL) 940 continue; 941 942 frup = (fruenvseg_t *)malloc(sizeof (fruenvseg_t)); 943 if (frup == NULL) 944 continue; 945 946 /* add this FRU to our list */ 947 frup->fru = sensorp->fru; 948 frup->envsegbufp = NULL; 949 frup->envseglen = 0; 950 frup->next = fruenvsegs; 951 fruenvsegs = frup; 952 953 /* 954 * Now get the environmental segment from this FRU 955 */ 956 (void) strcpy(path, "/devices"); 957 (void) strlcat(path, sensorp->fru, sizeof (path)); 958 retry: 959 errno = 0; 960 fd = open(path, O_RDONLY); 961 if (env_debug > 1) 962 envd_log(LOG_INFO, 963 "fru SEEPROM: %s fd: %d errno:%d\n", 964 path, fd, errno); 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)); 970 continue; 971 972 } 973 /* 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. 981 */ 982 if (env_debug) 983 envd_log(LOG_INFO, 984 "Invoking '%s' to create FRU nodes\n", 985 fru_devfsadm_cmd); 986 fru_devfsadm_invoked = 1; 987 (void) system(fru_devfsadm_cmd); 988 goto retry; 989 } 990 991 /* 992 * Read environmental segment from this FRU SEEPROM 993 */ 994 if (get_envseg(fd, &envsegbufp, &envseglen) == 0) { 995 /* 996 * Validate envseg version number and header length 997 */ 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); 1002 1003 if (envsegp->version != ENVSEG_VERSION || 1004 envseglen < hdrlen) { 1005 /* 1006 * version mismatch or header not big enough 1007 */ 1008 envd_log(LOG_CRIT, ENV_FRU_BAD_ENVSEG, 1009 sensorp->fru, errno, strerror(errno)); 1010 if (envsegbufp != NULL) 1011 (void) free(envsegbufp); 1012 } else { 1013 frup->envseglen = envseglen; 1014 frup->envsegbufp = envsegbufp; 1015 } 1016 } 1017 (void) close(fd); 1018 } 1019 return (fruenvsegs); 1020 } 1021 1022 /* 1023 * Process environmental segment for all FRUs. 1024 */ 1025 static void 1026 process_fru_envseg() 1027 { 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; 1035 1036 /* 1037 * Lookup/read environmental segments from FRU SEEPROMs and 1038 * process it. Note that we read each SEEPROM once as it's 1039 * a slow device. 1040 */ 1041 fruenvsegs = get_fru_envsegs(); 1042 1043 for (sensorp = &envd_sensors[0]; sensorp->name != NULL; sensorp++) { 1044 if (sensorp->fru == NULL) 1045 continue; 1046 1047 /* 1048 * Locate our FRU environmental segment 1049 */ 1050 for (frup = fruenvsegs; frup != NULL; frup = frup->next) 1051 if (strcmp(frup->fru, sensorp->fru) == 0) 1052 break; 1053 if (frup == NULL || frup->envsegbufp == NULL) 1054 continue; 1055 1056 envsegp = (envseg_layout_t *)frup->envsegbufp; 1057 envseglen = frup->envseglen; 1058 sensorcnt = envsegp->sensor_count; 1059 1060 /* 1061 * Locate our sensor data record entry 1062 */ 1063 for (i = 0; i < sensorcnt; i++) { 1064 uint32_t id; 1065 1066 id = GET_UNALIGN32(&envsegp->sensors[i].sensor_id[0]); 1067 if (env_debug > 1) 1068 envd_log(LOG_INFO, " sensor[%d]: id:%x\n", 1069 i, id); 1070 if (id == sensorp->fru_sensor) 1071 break; 1072 } 1073 1074 if (i >= sensorcnt) 1075 continue; 1076 1077 /* 1078 * Validate offset/length of our sensor data record 1079 */ 1080 offset = (uint_t)GET_UNALIGN16(&envsegp->sensors[i].offset); 1081 datap = (envseg_sensor_data_t *)((intptr_t)frup->envsegbufp + 1082 offset); 1083 mapentries = GET_UNALIGN16(&datap->obs2exp_cnt); 1084 length = sizeof (envseg_sensor_data_t) - sizeof (envseg_map_t) + 1085 mapentries * sizeof (envseg_map_t); 1086 1087 if (env_debug > 1) 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); 1092 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); 1097 continue; 1098 } 1099 1100 if (env_debug > 1) { 1101 /* print threshold values */ 1102 envd_log(LOG_INFO, 1103 "Thresholds: HPwrOff %d HShutDn %d HWarn %d\n", 1104 datap->high_power_off, datap->high_shutdown, 1105 datap->high_warning); 1106 envd_log(LOG_INFO, 1107 "Thresholds: LWarn %d LShutDn %d LPwrOff %d\n", 1108 datap->low_warning, datap->low_shutdown, 1109 datap->low_power_off); 1110 1111 /* print policy data */ 1112 envd_log(LOG_INFO, 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]); 1118 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); 1124 } 1125 } 1126 1127 1128 /* 1129 * Copy threshold values 1130 */ 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; 1138 1139 /* 1140 * Copy policy data 1141 */ 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]; 1147 1148 /* 1149 * Copy temperature mapping info (discard duplicate entries) 1150 */ 1151 if (sensorp->obs2exp_map) { 1152 (void) free(sensorp->obs2exp_map); 1153 sensorp->obs2exp_map = NULL; 1154 sensorp->obs2exp_cnt = 0; 1155 } 1156 if (mapentries > 0) { 1157 tempr_map_t *map; 1158 int cnt; 1159 tempr_t observed, expected; 1160 1161 map = (tempr_map_t *)malloc(mapentries * 1162 sizeof (tempr_map_t)); 1163 1164 if (map == NULL) { 1165 envd_log(LOG_CRIT, ENV_FRU_SENSOR_MAP_NOMEM, 1166 sensorp->fru_sensor, sensorp->name, 1167 sensorp->fru); 1168 continue; 1169 } 1170 1171 for (i = 0, cnt = 0; i < mapentries; i++) { 1172 1173 observed = (tempr_t) 1174 datap->obs2exp_map[i].observed; 1175 expected = (tempr_t) 1176 datap->obs2exp_map[i].expected; 1177 1178 /* ignore if duplicate entry */ 1179 if (cnt > 0 && 1180 observed == map[cnt-1].observed && 1181 expected == map[cnt-1].expected) { 1182 continue; 1183 } 1184 map[cnt].observed = observed; 1185 map[cnt].expected = expected; 1186 cnt++; 1187 } 1188 sensorp->obs2exp_cnt = cnt; 1189 sensorp->obs2exp_map = map; 1190 } 1191 1192 if (env_debug > 2 && sensorp->obs2exp_cnt > 1) { 1193 char msgbuf[256]; 1194 1195 envd_log(LOG_INFO, 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) 1202 envd_log(LOG_INFO, 1203 "%8d: %s\n", (i & ~0x7), msgbuf); 1204 } 1205 if ((i & 0x7) != 0) 1206 (void) printf("%8d: %s\n", (i & ~0x7), msgbuf); 1207 1208 envd_log(LOG_INFO, 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) 1215 envd_log(LOG_INFO, 1216 "%8d: %s\n", (i & ~0x7), msgbuf); 1217 } 1218 if ((i & 0x7) != 0) 1219 envd_log(LOG_INFO, 1220 "%8d: %s\n", (i & ~0x7), msgbuf); 1221 } 1222 } 1223 1224 /* 1225 * Deallocate environmental segment list 1226 */ 1227 while (fruenvsegs) { 1228 frup = fruenvsegs; 1229 fruenvsegs = frup->next; 1230 if (frup->envsegbufp != NULL) 1231 (void) free(frup->envsegbufp); 1232 (void) free(frup); 1233 } 1234 } 1235 1236 /* 1237 * Lookup fan and return a pointer to env_fan_t data structure. 1238 */ 1239 env_fan_t * 1240 fan_lookup(char *name) 1241 { 1242 int i; 1243 env_fan_t *fanp; 1244 1245 for (i = 0; (fanp = envd_fans[i]) != NULL; i++) { 1246 if (strcmp(fanp->name, name) == 0) 1247 return (fanp); 1248 } 1249 return (NULL); 1250 } 1251 1252 /* 1253 * Lookup sensor and return a pointer to env_sensor_t data structure. 1254 */ 1255 env_sensor_t * 1256 sensor_lookup(char *name) 1257 { 1258 env_sensor_t *sensorp; 1259 1260 for (sensorp = &envd_sensors[0]; sensorp->name != NULL; sensorp++) { 1261 if (strcmp(sensorp->name, name) == 0) 1262 return (sensorp); 1263 } 1264 return (NULL); 1265 } 1266 1267 /* 1268 * Get current temperature 1269 * Returns -1 on error, 0 if successful 1270 */ 1271 int 1272 get_temperature(env_sensor_t *sensorp, tempr_t *temp) 1273 { 1274 int fd = sensorp->fd; 1275 int retval = 0; 1276 int expected_temp; 1277 1278 if (fd == -1) 1279 retval = -1; 1280 else if (ioctl(fd, I2C_GET_TEMPERATURE, temp) == -1) { 1281 retval = -1; 1282 if (sensorp->error == 0) { 1283 sensorp->error = 1; 1284 envd_log(LOG_WARNING, ENV_SENSOR_ACCESS_FAIL, 1285 sensorp->name, errno, strerror(errno)); 1286 } 1287 } else if (sensorp->error != 0) { 1288 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); 1292 if (env_debug > 1) 1293 envd_log(LOG_INFO, 1294 "sensor: %-13s temp:%d CORRECED to %d\n", 1295 sensorp->name, *temp, (tempr_t)expected_temp); 1296 *temp = (tempr_t)expected_temp; 1297 } 1298 1299 return (retval); 1300 } 1301 1302 /* 1303 * Get current fan speed 1304 * Returns -1 on error, 0 if successful 1305 */ 1306 int 1307 get_fan_speed(env_fan_t *fanp, fanspeed_t *fanspeedp) 1308 { 1309 int fan_fd; 1310 int retval = 0; 1311 1312 fan_fd = fanp->fd; 1313 if (fan_fd == -1 || read(fan_fd, fanspeedp, sizeof (fanspeed_t)) != 1314 sizeof (fanspeed_t)) 1315 retval = -1; 1316 return (retval); 1317 } 1318 1319 /* 1320 * Set fan speed 1321 * Returns -1 on error, 0 if successful 1322 */ 1323 static int 1324 set_fan_speed(env_fan_t *fanp, fanspeed_t fanspeed) 1325 { 1326 int fan_fd; 1327 int retval = 0; 1328 1329 fan_fd = fanp->fd; 1330 if (fan_fd == -1 || write(fan_fd, &fanspeed, sizeof (fanspeed)) != 1331 sizeof (fanspeed_t)) 1332 retval = -1; 1333 return (retval); 1334 } 1335 1336 1337 /* 1338 * close all fan devices 1339 */ 1340 static void 1341 envd_close_fans(void) 1342 { 1343 int i; 1344 env_fan_t *fanp; 1345 1346 for (i = 0; (fanp = envd_fans[i]) != NULL; i++) { 1347 if (fanp->fd != -1) { 1348 (void) close(fanp->fd); 1349 fanp->fd = -1; 1350 } 1351 } 1352 } 1353 1354 /* 1355 * Close sensor devices 1356 */ 1357 static void 1358 envd_close_sensors(void) 1359 { 1360 env_sensor_t *sensorp; 1361 1362 for (sensorp = &envd_sensors[0]; sensorp->name != NULL; sensorp++) { 1363 if (sensorp->fd != -1) { 1364 (void) close(sensorp->fd); 1365 sensorp->fd = -1; 1366 } 1367 } 1368 } 1369 1370 /* 1371 * Open PM device 1372 */ 1373 static void 1374 envd_open_pm(void) 1375 { 1376 pm_fd = open(PM_DEVICE, O_RDONLY); 1377 if (pm_fd != -1) 1378 (void) fcntl(pm_fd, F_SETFD, FD_CLOEXEC); 1379 } 1380 1381 /* 1382 * Close PM device 1383 */ 1384 static void 1385 envd_close_pm(void) 1386 { 1387 if (pm_fd != -1) { 1388 (void) close(pm_fd); 1389 pm_fd = -1; 1390 } 1391 } 1392 1393 /* 1394 * Open fan devices and initialize per fan data structure. 1395 * Returns #fans found. 1396 */ 1397 static int 1398 envd_setup_fans(void) 1399 { 1400 int i, fd; 1401 fanspeed_t speed; 1402 env_fan_t *fanp; 1403 char path[PATH_MAX]; 1404 int fancnt = 0; 1405 char *fan_name; 1406 sensor_fan_map_t *sfmap; 1407 env_sensor_t *sensorp; 1408 int sensor_cnt; 1409 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; 1415 1416 (void) strcpy(path, "/devices"); 1417 (void) strlcat(path, fanp->devfs_path, sizeof (path)); 1418 fd = open(path, O_RDWR); 1419 if (fd == -1) { 1420 envd_log(LOG_CRIT, 1421 ENV_FAN_OPEN_FAIL, fanp->name, 1422 fanp->devfs_path, errno, strerror(errno)); 1423 fanp->present = B_FALSE; 1424 continue; 1425 } 1426 (void) fcntl(fd, F_SETFD, FD_CLOEXEC); 1427 fanp->fd = fd; 1428 fanp->present = B_TRUE; 1429 } 1430 fancnt++; 1431 1432 /* 1433 * Set initial speed and update cur_speed/prev_speed 1434 */ 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) { 1442 /* 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 1447 * current speed. 1448 */ 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) 1454 continue; 1455 } 1456 } 1457 fanp->cur_speed = speed; 1458 fanp->prev_speed = speed; 1459 1460 /* 1461 * Process sensor_fan_map[] table and initialize sensors[] 1462 * array for this fan. 1463 */ 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) 1468 continue; 1469 sensorp = sensor_lookup(sfmap->sensor_name); 1470 if (sensorp != NULL && sensor_cnt < SENSORS_PER_FAN) { 1471 fanp->sensors[sensor_cnt] = sensorp; 1472 sensor_cnt++; 1473 } 1474 } 1475 fanp->sensor_cnt = sensor_cnt; 1476 } 1477 1478 return (fancnt); 1479 } 1480 1481 1482 /* 1483 * Adjust specified sensor target temperature and fan adjustment rate 1484 */ 1485 1486 static void 1487 adjust_sensor_target(env_sensor_t *sensorp) 1488 { 1489 int target, index; 1490 sensor_pmdev_t *pmdevp; 1491 sensor_thresh_t *threshp; 1492 float rate; 1493 1494 /* 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 1500 * full power mode. 1501 */ 1502 1503 if (sensorp == NULL || (threshp = sensorp->temp_thresh) == NULL || 1504 threshp->policy_type != POLICY_TARGET_TEMP) 1505 return; 1506 1507 target = threshp->policy_data[0]; 1508 rate = 1.0; 1509 for (pmdevp = sensorp->pmdevp; pmdevp != NULL; pmdevp = pmdevp->next) { 1510 index = pmdevp->full_power - pmdevp->cur_power; 1511 if (index <= 0) 1512 continue; 1513 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; 1521 if (env_debug > 1) 1522 envd_log(LOG_INFO, 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); 1526 } 1527 1528 if (env_debug) 1529 envd_log(LOG_INFO, 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)); 1534 1535 sensorp->fan_adjustment_rate = rate; 1536 sensorp->target_temp = target; 1537 } 1538 1539 /* 1540 * Update current power level of all PM devices we are tracking and adjust 1541 * the target temperature associated with the corresponding sensor. 1542 * 1543 * Returns 1 if one or more pmdev power level was adjusted; 0 otherwise. 1544 */ 1545 static int 1546 update_pmdev_power() 1547 { 1548 sensor_pmdev_t *pmdevp; 1549 pm_req_t pmreq; 1550 int cur_power; 1551 int updated = 0; 1552 1553 for (pmdevp = sensor_pmdevs; pmdevp->pmdev_name != NULL; pmdevp++) { 1554 pmreq.physpath = pmdevp->pmdev_name; 1555 pmreq.data = NULL; 1556 pmreq.datasize = 0; 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); 1563 updated = 1; 1564 } 1565 } 1566 } 1567 return (updated); 1568 } 1569 1570 /* 1571 * Check if the specified sensor is present. 1572 * Returns 1 if present; 0 otherwise. 1573 * 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. 1577 */ 1578 static int 1579 envd_sensor_present(env_sensor_t *sensorp) 1580 { 1581 char *p, physpath[PATH_MAX]; 1582 di_node_t root_node; 1583 int sensor_present = 0; 1584 1585 /* 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. 1589 */ 1590 (void) strcpy(physpath, sensorp->devfs_path); 1591 p = strrchr(physpath, ':'); 1592 if (p != NULL) 1593 *p = '\0'; 1594 if ((root_node = di_init(physpath, DINFOMINOR)) != DI_NODE_NIL) { 1595 di_fini(root_node); 1596 sensor_present = 1; 1597 } 1598 return (sensor_present); 1599 } 1600 1601 /* 1602 * Open temperature sensor devices and initialize per sensor data structure. 1603 * Returns #sensors found. 1604 */ 1605 static int 1606 envd_setup_sensors(void) 1607 { 1608 tempr_t temp; 1609 env_sensor_t *sensorp; 1610 char path[PATH_MAX]; 1611 int sensorcnt = 0; 1612 int sensor_present; 1613 sensor_thresh_t *threshp; 1614 sensor_pmdev_t *pmdevp; 1615 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; 1621 } else { 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; 1629 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; 1637 sensorp->error = 0; 1638 1639 (void) strcpy(path, "/devices"); 1640 (void) strlcat(path, sensorp->devfs_path, 1641 sizeof (path)); 1642 retry: 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') { 1648 /* 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. 1657 * 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. 1663 */ 1664 devfsadm_invoked = 1; 1665 (void) system(devfsadm_cmd); 1666 goto retry; 1667 } 1668 if (sensor_present) 1669 envd_log(LOG_CRIT, 1670 ENV_SENSOR_OPEN_FAIL, 1671 sensorp->name, 1672 sensorp->devfs_path, errno, 1673 strerror(errno)); 1674 sensorp->present = B_FALSE; 1675 continue; 1676 } 1677 (void) fcntl(sensorp->fd, F_SETFD, FD_CLOEXEC); 1678 sensorp->present = B_TRUE; 1679 1680 /* 1681 * Set cur_temp field to the current temperature value 1682 */ 1683 if (get_temperature(sensorp, &temp) == 0) { 1684 sensorp->cur_temp = temp; 1685 sensorp->avg_temp = temp; 1686 } 1687 } 1688 sensorcnt++; 1689 1690 /* 1691 * Set low_power_off and high_power_off limits 1692 */ 1693 if (threshp && !disable_power_off) { 1694 temp = xlate_exp2obs(sensorp, threshp->low_power_off); 1695 if (env_debug > 1) 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); 1700 1701 temp = xlate_exp2obs(sensorp, threshp->high_power_off); 1702 if (env_debug > 1) 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, 1707 &temp); 1708 } 1709 } 1710 1711 /* 1712 * Locate "CPU Speed" component for any PM devices associated with 1713 * the sensors. 1714 */ 1715 for (pmdevp = sensor_pmdevs; pmdevp->sensor_name; pmdevp++) { 1716 int i, ncomp; 1717 char physpath[PATH_MAX]; 1718 pm_req_t pmreq; 1719 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); 1725 1726 /* 1727 * Lookup speed component and get full and current power 1728 * level for that component. 1729 */ 1730 pmreq.physpath = pmdevp->pmdev_name; 1731 pmreq.data = physpath; 1732 pmreq.datasize = sizeof (physpath); 1733 1734 ncomp = ioctl(pm_fd, PM_GET_NUM_COMPONENTS, &pmreq); 1735 for (i = 0; i < ncomp; i++) { 1736 pmreq.component = i; 1737 physpath[0] = '\0'; 1738 if (ioctl(pm_fd, PM_GET_COMPONENT_NAME, &pmreq) <= 0) 1739 continue; 1740 if (strcasecmp(pmreq.data, pmdevp->speed_comp_name)) 1741 continue; 1742 pmdevp->speed_comp = i; 1743 1744 1745 /* 1746 * Get full power and current power level 1747 */ 1748 pmdevp->full_power = ioctl(pm_fd, PM_GET_FULL_POWER, 1749 &pmreq); 1750 1751 pmdevp->cur_power = ioctl(pm_fd, PM_GET_CURRENT_POWER, 1752 &pmreq); 1753 1754 if (sensorp) { 1755 pmdevp->next = sensorp->pmdevp; 1756 sensorp->pmdevp = pmdevp; 1757 adjust_sensor_target(sensorp); 1758 } 1759 break; 1760 } 1761 if (env_debug > 1) 1762 envd_log(LOG_INFO, 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); 1768 } 1769 return (sensorcnt); 1770 } 1771 1772 /* 1773 * Read all temperature sensors and take appropriate action based 1774 * upon temperature threshols associated with each sensor. Possible 1775 * actions are: 1776 * 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. 1782 * 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. 1787 * 1788 * Note that the current temperature is recorded in the "cur_temp" field 1789 * within each env_sensor_t structure. 1790 */ 1791 static void 1792 monitor_sensors(void) 1793 { 1794 tempr_t temp; 1795 env_sensor_t *sensorp; 1796 sensor_thresh_t *threshp; 1797 time_t ct; 1798 char msgbuf[BUFSIZ]; 1799 char syscmd[BUFSIZ]; 1800 1801 for (sensorp = &envd_sensors[0]; sensorp->name != NULL; sensorp++) { 1802 if (get_temperature(sensorp, &temp) < 0) 1803 continue; 1804 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; 1809 1810 if (env_debug) 1811 envd_log(LOG_INFO, 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); 1819 1820 1821 /* 1822 * If this sensor already triggered system shutdown, don't 1823 * log any more shutdown/warning messages for it. 1824 */ 1825 if (sensorp->shutdown_initiated || threshp == NULL) 1826 continue; 1827 1828 /* 1829 * Check for the temperature in warning and shutdown range 1830 * and take appropriate action. 1831 */ 1832 if (TEMP_IN_WARNING_RANGE(temp, threshp) && !disable_warning) { 1833 /* 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 1839 * seconds. 1840 */ 1841 time_t wtstamp = sensorp->warning_tstamp; 1842 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; 1854 } 1855 } else if (sensorp->warning_start != 0) 1856 sensorp->warning_start = 0; 1857 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; 1863 1864 /* 1865 * Shutdown the system if the temperature remains 1866 * in the shutdown range for over shutdown_interval 1867 * seconds. 1868 */ 1869 if ((ct - sensorp->shutdown_tstamp) >= 1870 shutdown_interval) { 1871 /* log error */ 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); 1878 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); 1886 } 1887 } 1888 } else if (sensorp->shutdown_tstamp != 0) 1889 sensorp->shutdown_tstamp = 0; 1890 } 1891 } 1892 1893 1894 /* 1895 * Adjust fan speed based upon the current temperature value of various 1896 * sensors affected by the specified fan. 1897 */ 1898 static int 1899 adjust_fan_speed(env_fan_t *fanp, lpm_dev_t *devp) 1900 { 1901 int i; 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; 1906 tempr_t temp; 1907 float avg_temp, tempdiff, targetdiff; 1908 int av_ambient; 1909 int amb_cnt; 1910 1911 1912 /* 1913 * Get current fan speed 1914 */ 1915 if (get_fan_speed(fanp, &fanspeed) < 0) 1916 return (-1); 1917 cur_speed = fanp->cur_speed; 1918 if (fanspeed != (int)cur_speed) 1919 cur_speed = (float)fanspeed; 1920 1921 /* 1922 * Calculate new fan speed for each sensor and pick the largest one. 1923 */ 1924 min_speed = fanp->speed_min; 1925 max_speed = fanp->speed_max; 1926 speed = 0; 1927 av_ambient = 0; 1928 amb_cnt = 0; 1929 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) 1934 continue; 1935 1936 temp = sensorp->cur_temp; 1937 avg_temp = sensorp->avg_temp; 1938 threshp = sensorp->temp_thresh; 1939 1940 /* 1941 * Note ambient temperatures to determine lpm for system fan 1942 */ 1943 if ((devp != NULL) && 1944 (sensorp->flags & SFLAG_CPU_AMB_SENSOR)) { 1945 av_ambient += temp; 1946 amb_cnt++; 1947 } 1948 1949 /* 1950 * If the current temperature is above the warning 1951 * threshold, use max fan speed. 1952 */ 1953 if (temp >= threshp->high_warning) { 1954 speed = max_speed; 1955 break; 1956 } else if (temp <= threshp->low_warning) { 1957 speed = min_speed; 1958 break; 1959 } 1960 1961 if (threshp->policy_type == POLICY_TARGET_TEMP) { 1962 /* 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. 1968 */ 1969 float delta, speed_change; 1970 float multiplier; 1971 1972 targetdiff = avg_temp - sensorp->target_temp; 1973 tempdiff = avg_temp - sensorp->prev_avg_temp; 1974 1975 if (tempdiff > AVG_TEMP_HYSTERESIS) { 1976 /* 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. 1982 */ 1983 delta = max_speed * .005 * 1984 (RISING_TEMP_MARGIN + targetdiff); 1985 if (delta <= 0) 1986 multiplier = 0; 1987 else 1988 multiplier = tempdiff/4 + 1989 ((cur_speed < max_speed/2) ? 1990 2 : 1); 1991 } else if (tempdiff < -AVG_TEMP_HYSTERESIS) { 1992 /* 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. 1998 */ 1999 delta = -max_speed * .005 * 2000 (FALLING_TEMP_MARGIN - targetdiff); 2001 if (delta >= 0) 2002 multiplier = 0; 2003 else 2004 multiplier = -tempdiff/4 + 2005 ((cur_speed > max_speed/2) ? 2006 2 : 1); 2007 } else { 2008 /* 2009 * Temperature is changing very slowly. 2010 * Adjust fan speed by 0.4% for every 1C 2011 * below/above the target temperature. 2012 */ 2013 delta = max_speed * .004 * targetdiff; 2014 multiplier = 1.0; 2015 } 2016 2017 2018 /* 2019 * Enforece some bounds on multiplier and the 2020 * speed change. 2021 */ 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; 2028 2029 if (env_debug > 1) 2030 envd_log(LOG_INFO, 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) { 2039 /* 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. 2045 */ 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]); 2050 if (env_debug > 1) 2051 envd_log(LOG_INFO, 2052 "sensor: %-8s policy: linear, cur_speed %5.2f"\ 2053 " new_speed: %5.2f\n", sensorp->name, cur_speed, 2054 new_speed); 2055 } else { 2056 new_speed = cur_speed; 2057 } 2058 speed = MAX(speed, new_speed); 2059 } 2060 2061 /* 2062 * Adjust speed using lpm tables 2063 */ 2064 if (amb_cnt > 0) { 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)); 2069 } 2070 2071 speed = MIN(speed, max_speed); 2072 speed = MAX(speed, min_speed); 2073 2074 /* 2075 * Record and update fan speed, if different. 2076 */ 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); 2082 } 2083 if (env_debug) 2084 envd_log(LOG_INFO, 2085 "fan: %-16s speed cur:%6.2f new:%6.2f\n", 2086 fanp->name, fanp->prev_speed, fanp->cur_speed); 2087 2088 return (0); 2089 } 2090 /* 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. 2094 */ 2095 /*ARGSUSED*/ 2096 static void * 2097 envthr(void *args) 2098 { 2099 env_sensor_t *sensorp; 2100 fanspeed_t fan_speed; 2101 env_fan_t *pmfanp = &envd_psupply_fan; 2102 int to; 2103 int xwd = -1; 2104 2105 for (sensorp = &envd_sensors[0]; sensorp->name != NULL; 2106 sensorp++) { 2107 if (sensorp->obs2exp_map) 2108 (void) free(sensorp->obs2exp_map); 2109 sensorp->obs2exp_map = NULL; 2110 sensorp->obs2exp_cnt = 0; 2111 } 2112 2113 /* 2114 * Process environmental segment data, if present, 2115 * in the FRU SEEPROM. 2116 */ 2117 process_fru_envseg(); 2118 2119 /* 2120 * Process tuneable parameters 2121 */ 2122 process_env_conf_file(); 2123 2124 /* 2125 * Setup temperature sensors and fail if we can't open 2126 * at least one sensor. 2127 */ 2128 if (envd_setup_sensors() <= 0) { 2129 envd_close_pm(); 2130 return (NULL); 2131 } 2132 2133 to = 3 * sensor_poll_interval + 1; 2134 xwd = open(XCALWD_DEVFS, O_RDONLY); 2135 if (xwd < 0) { 2136 envd_log(LOG_CRIT, ENV_WATCHDOG_INIT_FAIL, errno, 2137 strerror(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, 2141 strerror(errno)); 2142 (void) close(xwd); 2143 xwd = -1; 2144 } 2145 2146 /* 2147 * Setup fan device (don't fail even if we can't access 2148 * the fan as we can still monitor temeperature. 2149 */ 2150 (void) envd_setup_fans(); 2151 2152 for (;;) { 2153 (void) pthread_rwlock_rdlock(&envd_rwlock); 2154 2155 /* 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. 2159 */ 2160 if (pmthr_exists == B_FALSE) 2161 (void) update_pmdev_power(); 2162 2163 if (xwd >= 0) 2164 (void) ioctl(xwd, XCALWD_KEEPALIVE); 2165 2166 if (!disable_piclenvd) { 2167 /* 2168 * Monitor current temperature for all sensors 2169 * (current temperature is recorded in the "cur_temp" 2170 * field within each sensor data structure) 2171 */ 2172 monitor_sensors(); 2173 2174 /* 2175 * Adjust CPU and system fan speed 2176 */ 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, 2181 lpm_devices); 2182 2183 /* 2184 * Turn off power supply fan if in lowest power state. 2185 */ 2186 fan_speed = (cur_lpstate) ? pmfanp->speed_min : 2187 pmfanp->speed_max; 2188 2189 if (env_debug) 2190 envd_log(LOG_INFO, 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); 2195 2196 if (fan_speed != (fanspeed_t)pmfanp->cur_speed && 2197 set_fan_speed(pmfanp, fan_speed) == 0) 2198 pmfanp->cur_speed = fan_speed; 2199 } 2200 (void) pthread_rwlock_unlock(&envd_rwlock); 2201 2202 /* 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. 2207 */ 2208 (void) envd_sleep(sensor_poll_interval); 2209 } 2210 /*NOTREACHED*/ 2211 return (NULL); 2212 } 2213 2214 /* 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. 2218 */ 2219 /*ARGSUSED*/ 2220 static void * 2221 pmthr(void *args) 2222 { 2223 pm_state_change_t pmstate; 2224 char physpath[PATH_MAX]; 2225 2226 pmstate.physpath = physpath; 2227 pmstate.size = sizeof (physpath); 2228 cur_lpstate = 0; 2229 2230 for (;;) { 2231 /* 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. 2235 * 2236 * To minimize polling, we use the blocking interface 2237 * to get the power state change event here. 2238 */ 2239 if (ioctl(pm_fd, PM_GET_STATE_CHANGE_WAIT, &pmstate) != 0) { 2240 if (errno != EINTR) 2241 break; 2242 continue; 2243 } 2244 2245 /* 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. 2251 */ 2252 do { 2253 if (env_debug > 1) { 2254 envd_log(LOG_INFO, 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); 2260 } 2261 cur_lpstate = 2262 (pmstate.flags & PSC_ALL_LOWEST) ? 1 : 0; 2263 } while (ioctl(pm_fd, PM_GET_STATE_CHANGE, &pmstate) == 0); 2264 2265 /* 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. 2273 */ 2274 2275 (void) pthread_rwlock_rdlock(&envd_rwlock); 2276 (void) update_pmdev_power(); 2277 (void) pthread_rwlock_unlock(&envd_rwlock); 2278 } 2279 2280 /* 2281 * We won't be able to monitor lowest power state any longer, 2282 * hence reset it. 2283 */ 2284 cur_lpstate = 0; 2285 envd_log(LOG_ERR, PM_THREAD_EXITING, errno, strerror(errno)); 2286 pmthr_exists = B_FALSE; 2287 return (NULL); 2288 } 2289 2290 2291 /* 2292 * Process sensor threshold related tuneables 2293 */ 2294 static int 2295 process_threshold_tuneable(char *keyword, char *buf, void *dummy_thresh_addr, 2296 int flags, char *fname, int line) 2297 { 2298 int retval = 0; 2299 long val; 2300 void *addr; 2301 char *endp, *sname; 2302 env_sensor_t *sensorp; 2303 2304 /* 2305 * Tuneable entry can be in one of the following formats: 2306 * 2307 * threshold-keyword <int-value> 2308 * threshold-keyword <int-value> <sensor-name> ... 2309 * 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. 2314 * 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. 2320 */ 2321 2322 errno = 0; 2323 val = strtol(buf, &endp, 0); 2324 sname = strtok(endp, tokdel); 2325 2326 if (errno != 0 || val != (tempr_t)val) { 2327 retval = -1; 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); 2332 retval = -1; 2333 } else if (sname == NULL) { 2334 int cnt = 0; 2335 2336 for (sensorp = &envd_sensors[0]; sensorp->name; sensorp++) { 2337 if (sensorp->temp_thresh == NULL || 2338 (sensorp->flags & flags) == 0) 2339 continue; 2340 2341 /* 2342 * Convert dummy_thresh_addr into memory address 2343 * for this sensor threshold values. 2344 */ 2345 addr = (char *)sensorp->temp_thresh + 2346 (int)((char *)dummy_thresh_addr - 2347 (char *)&dummy_thresh); 2348 2349 *(tempr_t *)addr = (tempr_t)val; 2350 cnt++; 2351 if (env_debug) 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); 2355 } 2356 if (cnt == 0) 2357 envd_log(LOG_INFO, "SUNW_piclenvd: file:%s line:%d " 2358 "%s SKIPPED as no matching sensor found.\n", 2359 fname, line, keyword); 2360 } else { 2361 /* apply threshold value to the specified sensors */ 2362 do { 2363 sensorp = sensor_lookup(sname); 2364 if (sensorp == NULL || sensorp->temp_thresh == NULL || 2365 (flags && (sensorp->flags & flags) == 0)) { 2366 envd_log(LOG_INFO, 2367 "SUNW_piclenvd: file:%s line:%d %s SKIPPED" 2368 " for '%s' as not a valid sensor.\n", 2369 fname, line, keyword, sname); 2370 continue; 2371 } 2372 /* 2373 * Convert dummy_thresh_addr into memory address 2374 * for this sensor threshold values. 2375 */ 2376 addr = (char *)sensorp->temp_thresh + 2377 (int)((char *)dummy_thresh_addr - 2378 (char *)&dummy_thresh); 2379 2380 *(tempr_t *)addr = (tempr_t)val; 2381 if (env_debug) 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); 2386 } 2387 return (retval); 2388 } 2389 2390 2391 /* 2392 * Process integer tuneables 2393 */ 2394 static int 2395 process_int_tuneable(char *keyword, char *buf, void *addr, int size, 2396 char *fname, int line) 2397 { 2398 int retval = 0; 2399 char *endp; 2400 long val; 2401 2402 /* 2403 * Convert input into integer value and ensure that there is 2404 * no other token in the buffer. 2405 */ 2406 errno = 0; 2407 val = strtol(buf, &endp, 0); 2408 if (errno != 0 || strtok(endp, tokdel) != NULL) 2409 retval = -1; 2410 else { 2411 switch (size) { 2412 case 1: 2413 if (val != (int8_t)val) 2414 retval = -1; 2415 else 2416 *(int8_t *)addr = (int8_t)val; 2417 break; 2418 case 2: 2419 if (val != (short)val) 2420 retval = -1; 2421 else 2422 *(short *)addr = (short)val; 2423 break; 2424 case 4: 2425 *(int *)addr = (int)val; 2426 break; 2427 default: 2428 retval = -1; 2429 } 2430 } 2431 2432 if (retval == -1) 2433 envd_log(LOG_INFO, ENV_CONF_INT_EXPECTED, 2434 fname, line, keyword); 2435 else if (env_debug) 2436 envd_log(LOG_INFO, "SUNW_piclenvd: file:%s line:%d %s = %d\n", 2437 fname, line, keyword, val); 2438 2439 return (retval); 2440 } 2441 2442 2443 /* 2444 * Process string tuneables 2445 * 2446 * String value must be within double quotes. Skip over initial white 2447 * spaces before looking for string value. 2448 */ 2449 static int 2450 process_string_tuneable(char *keyword, char *buf, void *addr, int size, 2451 char *fname, int line) 2452 { 2453 int retval = 0; 2454 char c, *p, *strend; 2455 2456 /* Skip over white spaces */ 2457 buf += strspn(buf, tokdel); 2458 2459 /* 2460 * Parse srting and locate string end (handling escaped double quotes 2461 * and other characters) 2462 */ 2463 if (buf[0] != '"') 2464 strend = NULL; 2465 else { 2466 for (p = buf+1; (c = *p) != '\0'; p++) 2467 if (c == '"' || (c == '\\' && *++p == '\0')) 2468 break; 2469 strend = (*p == '"') ? p : NULL; 2470 } 2471 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); 2476 retval = -1; 2477 } else { 2478 *strend = '\0'; 2479 (void) strcpy(addr, (caddr_t)buf+1); 2480 if (env_debug) 2481 envd_log(LOG_INFO, "SUNW_piclenvd: file:%s line:%d " 2482 "%s = \"%s\"\n", fname, line, keyword, buf+1); 2483 } 2484 2485 return (retval); 2486 } 2487 2488 2489 /* 2490 * Process configuration file 2491 */ 2492 static void 2493 process_env_conf_file(void) 2494 { 2495 int line, len, toklen; 2496 char buf[BUFSIZ]; 2497 FILE *fp; 2498 env_tuneable_t *tunep; 2499 char nmbuf[SYS_NMLN]; 2500 char fname[PATH_MAX]; 2501 char *tok, *valuep; 2502 int skip_line = 0; 2503 2504 if (sysinfo(SI_PLATFORM, nmbuf, sizeof (nmbuf)) == -1) 2505 return; 2506 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"); 2510 if (fp == NULL) 2511 return; 2512 2513 /* 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: 2517 * 2518 * keyword value 2519 * 2520 * where the "value" can be a signed integer or string (in 2521 * double quotes) depending upon the keyword. 2522 */ 2523 2524 for (line = 1; fgets(buf, sizeof (buf), fp) != NULL; line++) { 2525 len = strlen(buf); 2526 if (len <= 0) 2527 continue; 2528 2529 /* skip long lines */ 2530 if (buf[len-1] != '\n') { 2531 skip_line = 1; 2532 continue; 2533 } else if (skip_line) { 2534 skip_line = 0; 2535 continue; 2536 } else 2537 buf[len-1] = '\0'; 2538 2539 /* skip comments */ 2540 if (buf[0] == '*' || buf[0] == '#') 2541 continue; 2542 2543 /* 2544 * Skip over white space to get the keyword 2545 */ 2546 tok = buf + strspn(buf, tokdel); 2547 if (*tok == '\0') 2548 continue; /* blank line */ 2549 2550 toklen = strcspn(tok, tokdel); 2551 tok[toklen] = '\0'; 2552 2553 /* Get possible location for value (within current line) */ 2554 valuep = tok + toklen + 1; 2555 if (valuep > buf+len) 2556 valuep = buf + len; 2557 2558 /* 2559 * Lookup the keyword and process value accordingly 2560 */ 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); 2565 break; 2566 } 2567 } 2568 2569 if (tunep->name == NULL) 2570 envd_log(LOG_INFO, ENV_CONF_UNSUPPORTED_KEYWORD, 2571 fname, line, tok); 2572 } 2573 (void) fclose(fp); 2574 } 2575 2576 /* 2577 * Setup envrionmental monitor state and start threads to monitor 2578 * temperature and power management state. 2579 * Returns -1 on error, 0 if successful. 2580 */ 2581 2582 static int 2583 envd_setup(void) 2584 { 2585 char *valp, *endp; 2586 int val; 2587 int err; 2588 2589 if (pthread_attr_init(&thr_attr) != 0 || 2590 pthread_attr_setscope(&thr_attr, PTHREAD_SCOPE_SYSTEM) != 0) 2591 return (-1); 2592 2593 if (pm_fd == -1) 2594 envd_open_pm(); 2595 2596 /* 2597 * Setup lpm devices 2598 */ 2599 lpm_devices = NULL; 2600 if ((err = setup_lpm_devices(&lpm_devices)) != PICL_SUCCESS) { 2601 if (env_debug) 2602 envd_log(LOG_ERR, "setup_lpm_devices failed err = %d\n", 2603 err); 2604 } 2605 2606 /* 2607 * Initialize global state to initial startup values 2608 */ 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; 2620 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; 2627 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)); 2636 2637 if ((valp = getenv("SUNW_piclenvd_debug")) != NULL) { 2638 val = strtol(valp, &endp, 0); 2639 if (strtok(endp, tokdel) == NULL) 2640 env_debug = val; 2641 } 2642 2643 /* 2644 * Create a thread to monitor temperature and control fan 2645 * speed. 2646 */ 2647 if (envthr_created == B_FALSE && pthread_create(&envthr_tid, 2648 &thr_attr, envthr, (void *)NULL) != 0) { 2649 envd_close_fans(); 2650 envd_close_sensors(); 2651 envd_close_pm(); 2652 envd_log(LOG_CRIT, ENV_THREAD_CREATE_FAILED); 2653 return (-1); 2654 } 2655 envthr_created = B_TRUE; 2656 2657 /* 2658 * Create a thread to monitor PM state 2659 */ 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); 2664 } else 2665 pmthr_exists = B_TRUE; 2666 } 2667 return (0); 2668 } 2669 2670 /* 2671 * Callback function used by ptree_walk_tree_by_class for the cpu class 2672 */ 2673 static int 2674 cb_cpu(picl_nodehdl_t nodeh, void *args) 2675 { 2676 sensor_pmdev_t *pmdevp; 2677 int err; 2678 ptree_propinfo_t pinfo; 2679 picl_prophdl_t proph; 2680 size_t psize; 2681 int id; 2682 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); 2687 2688 /* Get the pmdevp for the CPU */ 2689 pmdevp = sensor_pmdevs; 2690 while (pmdevp->sensor_id != -1) { 2691 if (id == pmdevp->sensor_id) 2692 break; 2693 pmdevp++; 2694 } 2695 2696 /* Return if didn't find the pmdevp for the cpu id */ 2697 if (pmdevp->sensor_id == -1) 2698 return (PICL_WALK_CONTINUE); 2699 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); 2704 2705 err = ptree_get_propinfo(proph, &pinfo); 2706 if ((err != PICL_SUCCESS) || 2707 (pinfo.piclinfo.type != PICL_PTYPE_CHARSTRING)) 2708 return (PICL_WALK_CONTINUE); 2709 2710 psize = pinfo.piclinfo.size; 2711 pmdevp->pmdev_name = malloc(psize); 2712 if (pmdevp->pmdev_name == NULL) 2713 return (PICL_WALK_CONTINUE); 2714 2715 err = ptree_get_propval(proph, pmdevp->pmdev_name, psize); 2716 if (err != PICL_SUCCESS) 2717 return (PICL_WALK_CONTINUE); 2718 2719 return (PICL_WALK_CONTINUE); 2720 } 2721 2722 /* 2723 * Find the CPU's in the picl tree, set the devfs-path for pmdev_name 2724 */ 2725 static void 2726 setup_pmdev_names() 2727 { 2728 picl_nodehdl_t plath; 2729 int err; 2730 2731 err = ptree_get_node_by_path(PLATFORM_PATH, &plath); 2732 if (err != PICL_SUCCESS) 2733 return; 2734 2735 err = ptree_walk_tree_by_class(plath, PICL_CLASS_CPU, NULL, cb_cpu); 2736 } 2737 2738 2739 static void 2740 piclenvd_register(void) 2741 { 2742 picld_plugin_register(&my_reg_info); 2743 } 2744 2745 static void 2746 piclenvd_init(void) 2747 { 2748 /* 2749 * Setup the names for the pm sensors, we do it just the first time 2750 */ 2751 if (pmdev_names_init == B_FALSE) { 2752 (void) setup_pmdev_names(); 2753 pmdev_names_init = B_TRUE; 2754 } 2755 2756 /* 2757 * Start environmental monitor/threads 2758 */ 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); 2763 return; 2764 } 2765 (void) pthread_rwlock_unlock(&envd_rwlock); 2766 2767 /* 2768 * Now setup/populate PICL tree 2769 */ 2770 env_picl_setup(); 2771 } 2772 2773 static void 2774 piclenvd_fini(void) 2775 { 2776 /* 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 2780 * by envd_setup. 2781 */ 2782 delete_lpm_devices(); 2783 2784 /* 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. 2789 */ 2790 env_picl_destroy(); 2791 2792 /* 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 2797 * handling also. 2798 */ 2799 } 2800 2801 /*VARARGS2*/ 2802 void 2803 envd_log(int pri, const char *fmt, ...) 2804 { 2805 va_list ap; 2806 2807 va_start(ap, fmt); 2808 vsyslog(pri, fmt, ap); 2809 va_end(ap); 2810 } 2811 2812 #ifdef __lint 2813 /* 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. 2817 */ 2818 #define sigwait my_posix_sigwait 2819 extern int my_posix_sigwait(const sigset_t *set, int *sig); 2820 #endif 2821 2822 /* 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. 2829 * 2830 * Note that SIGALRM and alarm() can't be used by any other thread 2831 * in this manner. 2832 */ 2833 2834 static unsigned int 2835 envd_sleep(unsigned int sleep_tm) 2836 { 2837 int sig; 2838 unsigned int unslept; 2839 sigset_t alrm_mask; 2840 2841 if (sleep_tm == 0) 2842 return (0); 2843 2844 (void) sigemptyset(&alrm_mask); 2845 (void) sigaddset(&alrm_mask, SIGALRM); 2846 2847 (void) alarm(sleep_tm); 2848 (void) sigwait(&alrm_mask, &sig); 2849 2850 unslept = alarm(0); 2851 return (unslept); 2852 } 2853