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