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