/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2004 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #pragma ident "%Z%%M% %I% %E% SMI" /* * This file contains the environmental PICL plug-in module. */ /* * This plugin sets up the PICLTREE for Enchilada WS. * It provides functionality to get/set temperatures and * fan speeds. * * The environmental policy defaults to the auto mode * as programmed by OBP at boot time. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "envd.h" #include #include /* * PICL plugin entry points */ static void piclenvd_register(void); static void piclenvd_init(void); static void piclenvd_fini(void); /* * Env setup routines */ extern void env_picl_setup(void); extern void env_picl_destroy(void); extern int env_picl_setup_tuneables(void); /* * Sleep routine used for polling */ static int get_dimm_fan_speed(int, fanspeed_t *); static int is_dimm_fan_failed(void); #pragma init(piclenvd_register) /* * Plugin registration information */ static picld_plugin_reg_t my_reg_info = { PICLD_PLUGIN_VERSION, PICLD_PLUGIN_CRITICAL, "SUNW_piclenvd", piclenvd_init, piclenvd_fini, }; #define REGISTER_INFORMATION_STRING_LENGTH 16 static char dimm_fan_rpm_string[REGISTER_INFORMATION_STRING_LENGTH] = {0}; static char dimm_fan_status_string[REGISTER_INFORMATION_STRING_LENGTH] = {0}; static char dimm_fan_command_string[REGISTER_INFORMATION_STRING_LENGTH] = {0}; static char dimm_fan_debug_string[REGISTER_INFORMATION_STRING_LENGTH] = {0}; static int scsi_log_sense(int fd, uchar_t page_code, uchar_t *pagebuf, uint16_t pagelen); static int get_disk_temp(env_disk_t *); /* * ES Segment data structures */ static sensor_ctrl_blk_t sensor_ctrl[MAX_SENSORS]; static fan_ctrl_blk_t fan_ctrl[MAX_FANS]; static fruenvseg_t *envfru = NULL; /* * Env thread variables */ static boolean_t system_shutdown_started = B_FALSE; static boolean_t ovtemp_thr1_created = B_FALSE; static pthread_t ovtemp_thr1_id; static pthread_attr_t thr_attr; static boolean_t ovtemp_thr2_created = B_FALSE; static pthread_t ovtemp_thr2_id; static boolean_t dimm_fan_thr_created = B_FALSE; static pthread_t dimm_fan_thr_id; static boolean_t disk_temp_thr_created = B_FALSE; static pthread_t disk_temp_thr_id; /* * PM thread related variables */ static pthread_t pmthr_tid; /* pmthr thread ID */ static int pm_fd = -1; /* PM device file descriptor */ static boolean_t pmthr_created = B_FALSE; static int cur_lpstate; /* cur low power state */ /* * Envd plug-in verbose flag set by SUNW_PICLENVD_DEBUG environment var * Setting the verbose tuneable also enables debugging for better * control */ int env_debug = 0; /* * Fan devices */ static env_fan_t envd_sys_out_fan = { ENV_SYSTEM_OUT_FAN, ENV_SYSTEM_OUT_FAN_DEVFS, NULL, SYSTEM_OUT_FAN_ID, SYSTEM_OUT_FAN_SPEED_MIN, SYSTEM_OUT_FAN_SPEED_MAX, -1, -1, }; static env_fan_t envd_sys_in_fan = { ENV_SYSTEM_INTAKE_FAN, ENV_SYSTEM_INTAKE_FAN_DEVFS, NULL, SYSTEM_INTAKE_FAN_ID, SYSTEM_INTAKE_FAN_SPEED_MIN, SYSTEM_INTAKE_FAN_SPEED_MAX, -1, -1, }; static env_fan_t envd_cpu0_fan = { ENV_CPU0_FAN, ENV_CPU0_FAN_DEVFS, NULL, CPU0_FAN_ID, CPU_FAN_SPEED_MIN, CPU_FAN_SPEED_MAX, -1, -1, }; static env_fan_t envd_cpu1_fan = { ENV_CPU1_FAN, ENV_CPU1_FAN_DEVFS, NULL, CPU1_FAN_ID, CPU_FAN_SPEED_MIN, CPU_FAN_SPEED_MAX, -1, -1, }; static env_fan_t envd_dimm_fan = { ENV_DIMM_FAN, ENV_DIMM_FAN_DEVFS, NULL, DIMM_FAN_ID, 100, 100, -1, -1, }; static env_disk_t envd_disk0 = { ENV_DISK0, ENV_DISK0_DEVFS, DISK0_PHYSPATH, DISK0_NODE_PATH, DISK0_ID, -1, -1, }; static env_disk_t envd_disk1 = { ENV_DISK1, ENV_DISK1_DEVFS, DISK1_PHYSPATH, DISK1_NODE_PATH, DISK1_ID, -1, -1, }; /* * The vendor-id and device-id are the properties associated with * the SCSI controller. This is used to identify a particular controller * like LSI1030. */ #define VENDOR_ID "vendor-id" #define DEVICE_ID "device-id" /* * The implementation for SCSI disk drives to supply info. about * temperature is not mandatory. Hence we first determine if the * temperature page is supported. To do this we need to scan the list * of pages supported. */ #define SUPPORTED_LPAGES 0 #define TEMPERATURE_PAGE 0x0D #define LOGPAGEHDRSIZE 4 /* * NULL terminated array of fans */ static env_fan_t *envd_fans[] = { &envd_cpu0_fan, &envd_cpu1_fan, &envd_sys_out_fan, &envd_sys_in_fan, &envd_dimm_fan, NULL }; static env_disk_t *envd_disks[] = { &envd_disk0, &envd_disk1, NULL }; /* * ADM1031 speedrange map is indexed by a 2-bit value */ static int adm_speedrange_map[] = {1, 2, 4, 8}; /* * ADM1031 devices */ static char *hwm_devs[] = { CPU_HWM_DEVFS, /* CPU_HWM_ID */ SYS_HWM_DEVFS /* SYS_HWM_ID */ }; /* * Fan names associated with each ADM1031 hwms - used to * print fault messages. */ static char *hwm_fans[MAX_HWMS][2] = { {ENV_CPU0_FAN, ENV_CPU1_FAN}, {ENV_SYSTEM_INTAKE_FAN, ENV_SYSTEM_OUT_FAN} }; /* * Temperature sensors */ static env_sensor_t envd_sensors[] = { { SENSOR_CPU0_DIE, SENSOR_CPU0_DIE_DEVFS, NULL, CPU0_SENSOR_ID, CPU_HWM_ID, (void *)&envd_cpu0_fan, -1}, { SENSOR_CPU1_DIE, SENSOR_CPU1_DIE_DEVFS, NULL, CPU1_SENSOR_ID, CPU_HWM_ID, (void *)&envd_cpu1_fan, -1}, { SENSOR_INT_AMB_0, SENSOR_INT_AMB_0_DEVFS, NULL, INT_AMB0_SENSOR_ID, CPU_HWM_ID, NULL, -1}, { SENSOR_SYS_OUT, SENSOR_SYS_OUT_DEVFS, NULL, SYS_OUT_SENSOR_ID, SYS_HWM_ID, (void *)&envd_sys_out_fan, -1}, { SENSOR_INT_AMB_1, SENSOR_INT_AMB_1_DEVFS, NULL, INT_AMB1_SENSOR_ID, SYS_HWM_ID, NULL, -1}, { SENSOR_SYS_IN, SENSOR_SYS_IN_DEVFS, NULL, SYS_IN_SENSOR_ID, SYS_HWM_ID, (void *)&envd_sys_in_fan, -1}, }; #define N_ENVD_SENSORS (sizeof (envd_sensors)/sizeof (envd_sensors[0])) #define NOT_AVAILABLE "NA" /* * ADM1031 macros */ #define TACH_UNKNOWN 255 #define FAN_OUT_OF_RANGE (TACH_UNKNOWN) #define ADM_HYSTERISIS 5 #define N_SEQ_TACH 15 #define TMIN_MASK (0xF8) #define TMIN_SHIFT (3) #define TMIN_UNITS (4) /* increments of 4 degrees celsius */ #define TRANGE_MASK (0x7) #define TMIN(regval) (((regval & TMIN_MASK) >> TMIN_SHIFT) * TMIN_UNITS) #define TRANGE(regval) (regval & TRANGE_MASK) #define GET_TMIN_RANGE(tmin, trange) \ ((((tmin / TMIN_UNITS) & TMIN_MASK) << TMIN_SHIFT) | \ (trange & TRANGE_MASK)) #define TACH_ENABLE_MASK (0x0C) #define ADM_SETFANSPEED_CONV(speed) (15 * speed / 100) /* * Tuneables */ #define ENABLE 1 #define DISABLE 0 int monitor_disk_temp = 1; /* enabled */ static int disk_high_warn_temperature = DISK_HIGH_WARN_TEMPERATURE; static int disk_low_warn_temperature = DISK_LOW_WARN_TEMPERATURE; static int disk_high_shutdown_temperature = DISK_HIGH_SHUTDOWN_TEMPERATURE; static int disk_low_shutdown_temperature = DISK_LOW_SHUTDOWN_TEMPERATURE; static int disk_scan_interval = DISK_SCAN_INTERVAL; static int get_monitor_cpu_mode(ptree_rarg_t *parg, void *buf); static int set_monitor_cpu_mode(ptree_warg_t *parg, const void *buf); static int get_monitor_sys_mode(ptree_rarg_t *parg, void *buf); static int set_monitor_sys_mode(ptree_warg_t *parg, const void *buf); static int get_int_val(ptree_rarg_t *parg, void *buf); static int set_int_val(ptree_warg_t *parg, const void *buf); static int get_string_val(ptree_rarg_t *parg, void *buf); static int set_string_val(ptree_warg_t *parg, const void *buf); static int get_cpu_tach(ptree_rarg_t *parg, void *buf); static int set_cpu_tach(ptree_warg_t *parg, const void *buf); static int get_sys_tach(ptree_rarg_t *parg, void *buf); static int set_sys_tach(ptree_warg_t *parg, const void *buf); static int shutdown_override = 0; static int sensor_poll_interval = SENSORPOLL_INTERVAL; static int warning_interval = WARNING_INTERVAL; static int disk_warning_interval = DISK_WARNING_INTERVAL; static int disk_warning_duration = DISK_WARNING_DURATION; static int shutdown_interval = SHUTDOWN_INTERVAL; static int disk_shutdown_interval = DISK_SHUTDOWN_INTERVAL; static int ovtemp_monitor = 1; /* enabled */ static int pm_monitor = 1; /* enabled */ static int mon_fanstat = 1; /* enabled */ static int cpu_mode; static int sys_mode; static int cpu_tach; static int sys_tach; static char shutdown_cmd[] = SHUTDOWN_CMD; env_tuneable_t tuneables[] = { {"ovtemp-monitor", PICL_PTYPE_INT, &ovtemp_monitor, &get_int_val, &set_int_val, sizeof (int)}, {"pm-monitor", PICL_PTYPE_INT, &pm_monitor, &get_int_val, &set_int_val, sizeof (int)}, {"shutdown-override", PICL_PTYPE_INT, &shutdown_override, &get_int_val, &set_int_val, sizeof (int)}, {"cpu-hm-automode-enable", PICL_PTYPE_INT, &cpu_mode, &get_monitor_cpu_mode, &set_monitor_cpu_mode, sizeof (int)}, {"sys-hm-automode-enable", PICL_PTYPE_INT, &sys_mode, &get_monitor_sys_mode, &set_monitor_sys_mode, sizeof (int)}, {"sensor-poll-interval", PICL_PTYPE_INT, &sensor_poll_interval, &get_int_val, &set_int_val, sizeof (int)}, {"disk-scan-interval", PICL_PTYPE_INT, &disk_scan_interval, &get_int_val, &set_int_val, sizeof (int)}, {"warning-interval", PICL_PTYPE_INT, &warning_interval, &get_int_val, &set_int_val, sizeof (int)}, {"shutdown-interval", PICL_PTYPE_INT, &shutdown_interval, &get_int_val, &set_int_val, sizeof (int)}, {"disk_warning-interval", PICL_PTYPE_INT, &disk_warning_interval, &get_int_val, &set_int_val, sizeof (int)}, {"disk_warning-duration", PICL_PTYPE_INT, &disk_warning_duration, &get_int_val, &set_int_val, sizeof (int)}, {"disk_shutdown-interval", PICL_PTYPE_INT, &disk_shutdown_interval, &get_int_val, &set_int_val, sizeof (int)}, {"shutdown-command", PICL_PTYPE_CHARSTRING, shutdown_cmd, &get_string_val, &set_string_val, sizeof (shutdown_cmd)}, {"cpu-tach-enable", PICL_PTYPE_INT, &cpu_tach, &get_cpu_tach, &set_cpu_tach, sizeof (int)}, {"sys-tach-enable", PICL_PTYPE_INT, &sys_tach, &get_sys_tach, &set_sys_tach, sizeof (int)}, {"monitor-fanstat", PICL_PTYPE_INT, &mon_fanstat, &get_int_val, &set_int_val, sizeof (int)}, {"monitor-disk-temp", PICL_PTYPE_INT, &monitor_disk_temp, &get_int_val, &set_int_val, sizeof (int)}, {"disk-high-warn-temperature", PICL_PTYPE_INT, &disk_high_warn_temperature, &get_int_val, &set_int_val, sizeof (int)}, {"disk-low-warn-temperature", PICL_PTYPE_INT, &disk_low_warn_temperature, &get_int_val, &set_int_val, sizeof (int)}, {"disk-high-shutdown-temperature", PICL_PTYPE_INT, &disk_high_shutdown_temperature, &get_int_val, &set_int_val, sizeof (int)}, {"disk-low-shutdown-temperature", PICL_PTYPE_INT, &disk_low_shutdown_temperature, &get_int_val, &set_int_val, sizeof (int)}, {"verbose", PICL_PTYPE_INT, &env_debug, &get_int_val, &set_int_val, sizeof (int)}, }; /* * We use this to figure out how many tuneables there are * This is variable because the publishing routine needs this info * in piclenvsetup.c */ int ntuneables = (sizeof (tuneables)/sizeof (tuneables[0])); /* * Table Handling Code */ static void fini_table(table_t *tblp) { if (tblp == NULL) return; free(tblp->xymap); free(tblp); } static table_t * init_table(int npoints) { table_t *tblp; point_t *xy; if (npoints == 0) return (NULL); if ((tblp = malloc(sizeof (*tblp))) == NULL) return (NULL); if ((xy = malloc(sizeof (*xy) * npoints)) == NULL) { free(tblp); return (NULL); } tblp->nentries = npoints; tblp->xymap = xy; return (tblp); } /* * function: calculates y for a given x based on a table of points * for monotonically increasing x values. * 'tbl' specifies the table to use, 'val' specifies the 'x', returns 'y' */ static int y_of_x(table_t *tbl, int xval) { int i; int entries; point_t *xymap; float newval; float dy, dx, slope; entries = tbl->nentries; xymap = tbl->xymap; /* * If the temperature is outside the correction table * then simply return the original value. */ if ((xval < xymap[0].x) || (xval > xymap[entries - 1].x)) return (xval); if (xval == xymap[0].x) return (xymap[0].y); if (xval == xymap[entries - 1].x) return (xymap[entries - 1].y); for (i = 1; i < entries - 1; i++) { if (xval == xymap[i].x) return (xymap[i].y); if (xval < xymap[i].x) break; } /* * Use linear interpolation */ dy = (float)(xymap[i].y - xymap[i-1].y); dx = (float)(xymap[i].x - xymap[i-1].x); slope = dy/dx; newval = xymap[i - 1].y + slope * (xval - xymap[i - 1].x); return ((int)(newval + (newval >= 0 ? 0.5 : -0.5))); } /* * Get environmental segment from the specified FRU SEEPROM */ static int get_envseg(int fd, void **envsegp, int *envseglenp) { int i, segcnt, envseglen; section_layout_t section; segment_layout_t segment; uint8_t *envseg; if (lseek(fd, (long)SECTION_HDR_OFFSET, 0) == -1L || read(fd, §ion, sizeof (section)) != sizeof (section)) { return (EINVAL); } /* * Verify we have the correct section and contents are valid * For now, we don't verify the CRC. */ if (section.header_tag != SECTION_HDR_TAG || GET_UNALIGN16(§ion.header_version[0]) != SECTION_HDR_VER) { if (env_debug) envd_log(LOG_INFO, "Invalid section header tag:%x version:%x\n", section.header_tag, GET_UNALIGN16(§ion.header_version)); return (EINVAL); } /* * Locate our environmental segment */ segcnt = section.segment_count; for (i = 0; i < segcnt; i++) { if (read(fd, &segment, sizeof (segment)) != sizeof (segment)) { return (EINVAL); } if (env_debug) envd_log(LOG_INFO, "Seg name: %x desc:%x off:%x len:%x\n", GET_UNALIGN16(&segment.name), GET_UNALIGN32(&segment.descriptor[0]), GET_UNALIGN16(&segment.offset), GET_UNALIGN16(&segment.length)); if (GET_UNALIGN16(&segment.name) == ENVSEG_NAME) break; } if (i >= segcnt) { return (ENOENT); } /* * Allocate memory to hold the environmental segment data. */ envseglen = GET_UNALIGN16(&segment.length); if ((envseg = malloc(envseglen)) == NULL) { return (ENOMEM); } if (lseek(fd, (long)GET_UNALIGN16(&segment.offset), 0) == -1L || read(fd, envseg, envseglen) != envseglen) { (void) free(envseg); return (EIO); } *envsegp = envseg; *envseglenp = envseglen; return (0); } /* * Get all environmental segments * Return NULL on error */ static fruenvseg_t * get_fru_envsegs(void) { fruenvseg_t *fruenvsegs; envseg_layout_t *envsegp; void *envsegbufp; int fd, envseglen, hdrlen; char path[PATH_MAX]; fruenvsegs = NULL; fruenvsegs = malloc(sizeof (*fruenvsegs)); if (fruenvsegs == NULL) { return (NULL); } /* * Now get the environmental segment from this FRU */ (void) snprintf(path, sizeof (path), "%s%s", I2C_DEVFS, MBFRU_DEV); fd = open(path, O_RDONLY); if (fd == -1) { envd_log(LOG_ERR, ENV_FRU_OPEN_FAIL, errno, path); free(fruenvsegs); return (NULL); } /* * Read environmental segment from this FRU SEEPROM */ if (get_envseg(fd, &envsegbufp, &envseglen) != 0) { envd_log(LOG_ERR, ENV_FRU_BAD_ENVSEG, path); free(fruenvsegs); (void) close(fd); return (NULL); } /* * Validate envseg version number and header length */ envsegp = (envseg_layout_t *)envsegbufp; hdrlen = sizeof (envseg_layout_t) - sizeof (envseg_sensor_t) + (envsegp->sensor_count) * sizeof (envseg_sensor_t); if (envsegp->version != ENVSEG_VERSION || envseglen < hdrlen) { /* * version mismatch or header not big enough */ envd_log(LOG_CRIT, ENV_FRU_BAD_ENVSEG, FRU_SEEPROM_NAME); if (envsegbufp != NULL) (void) free(envsegbufp); free(fruenvsegs); (void) close(fd); return (NULL); } fruenvsegs->envseglen = envseglen; fruenvsegs->envsegbufp = envsegbufp; (void) close(fd); return (fruenvsegs); } static int process_fru_seeprom(unsigned char *buff) { id_off_t id; int i; int id_offset = 0; int nsensors; int nfans; env_fan_t *fnodep; env_sensor_t *snodep; #define NSENSOR_OFFSET 1 #define ID_OFF_SIZE 6 #define NFANS_OFFSET(x) ((x * ID_OFF_SIZE) + 2) nsensors = (int)buff[NSENSOR_OFFSET]; if (nsensors != MAX_SENSORS) { envd_log(LOG_CRIT, ENV_FRU_BAD_ENVSEG, FRU_SEEPROM_NAME); return (-1); } nfans = (int)buff[NFANS_OFFSET(nsensors)]; if (nfans != MAX_FANS) { envd_log(LOG_CRIT, ENV_FRU_BAD_ENVSEG, FRU_SEEPROM_NAME); return (-1); } while (nsensors > 0) { (void) memcpy((char *)&id, (char *)&buff[id_offset + 2], ID_OFF_SIZE); if (env_debug) envd_log(LOG_ERR, "\n Sensor Id %x offset %x", id.id, id.offset); if (id.id > MAX_SENSOR_ID) { envd_log(LOG_CRIT, ENV_FRU_BAD_ENVSEG, FRU_SEEPROM_NAME); return (-1); } /* * Copy into the sensor control block array according to the * sensor ID */ (void) memcpy((char *)&sensor_ctrl[id.id], (char *)&buff[id.offset], sizeof (sensor_ctrl_blk_t)); nsensors--; id_offset += ID_OFF_SIZE; } /* * Skip past no of Fan entry(single byte) */ id_offset++; while (nfans > 0) { (void) memcpy((char *)&id, (char *)&buff[id_offset + 2], ID_OFF_SIZE); if (env_debug) envd_log(LOG_ERR, "\n Fan Id %x offset %x", id.id, id.offset); if (id.id > 3) { envd_log(LOG_CRIT, ENV_FRU_BAD_ENVSEG, FRU_SEEPROM_NAME); return (-1); } (void) memcpy((char *)&fan_ctrl[id.id], (char *)&buff[id.offset], sizeof (fan_ctrl_blk_t)); nfans--; id_offset += ID_OFF_SIZE; } /* * Match Sensor/ES ID and point correct data * based on IDs */ for (i = 0; i < N_ENVD_SENSORS; i++) { snodep = &envd_sensors[i]; snodep->es_ptr = &sensor_ctrl[snodep->id]; } /* * Match Fan/ES ID and point to correct ES Data * based on IDs */ for (i = 0; (fnodep = envd_fans[i]) != NULL; i++) fnodep->es_ptr = &fan_ctrl[fnodep->id]; return (0); } static int envd_es_setup(void) { envfru = get_fru_envsegs(); if (envfru == NULL) { envd_log(LOG_CRIT, ENV_FRU_BAD_ENVSEG, FRU_SEEPROM_NAME); return (-1); } return (process_fru_seeprom((uchar_t *)envfru->envsegbufp)); } static void envd_es_destroy(void) { if (envfru != NULL) free(envfru->envsegbufp); } /* * Lookup fan and return a pointer to env_fan_t data structure. */ env_fan_t * fan_lookup(char *name) { int i; env_fan_t *fanp; for (i = 0; (fanp = envd_fans[i]) != NULL; i++) { if (strcmp(fanp->name, name) == 0) return (fanp); } return (NULL); } /* * Lookup sensor and return a pointer to env_sensor_t data structure. */ env_sensor_t * sensor_lookup(char *name) { env_sensor_t *sensorp; int i; for (i = 0; i < N_ENVD_SENSORS; ++i) { sensorp = &envd_sensors[i]; if (strcmp(sensorp->name, name) == 0) return (sensorp); } return (NULL); } /* * Lookup disk and return a pointer to env_disk_t data structure. */ env_disk_t * disk_lookup(char *name) { int i; env_disk_t *diskp; for (i = 0; (diskp = envd_disks[i]) != NULL; i++) { if (strncmp(diskp->name, name, strlen(name)) == 0) return (diskp); } return (NULL); } /* * Get current temperature * Returns -1 on error, 0 if successful */ int get_temperature(env_sensor_t *sensorp, tempr_t *temp) { int fd = sensorp->fd; int retval = 0; if (fd == -1) retval = -1; else if (ioctl(fd, I2C_GET_TEMPERATURE, temp) == -1) { retval = -1; if (sensorp->error == 0) { sensorp->error = 1; envd_log(LOG_WARNING, ENV_SENSOR_ACCESS_FAIL, sensorp->name, errno, strerror(errno)); } } else if (sensorp->error != 0) { sensorp->error = 0; envd_log(LOG_WARNING, ENV_SENSOR_ACCESS_OK, sensorp->name); } if (sensorp->crtbl != NULL) { *temp = (tempr_t)y_of_x(sensorp->crtbl, *temp); } return (retval); } /* * Get current disk temperature * Returns -1 on error, 0 if successful */ int disk_temperature(env_disk_t *diskp, tempr_t *temp) { int retval = 0; if (diskp == NULL) retval = -1; else { *temp = diskp->current_temp; } return (retval); } /* * Get uncorrected current temperature * Returns -1 on error, 0 if successful */ static int get_raw_temperature(env_sensor_t *sensorp, tempr_t *temp) { int fd = sensorp->fd; int retval = 0; if (fd == -1) retval = -1; else if (ioctl(fd, I2C_GET_TEMPERATURE, temp) == -1) { retval = -1; } return (retval); } /* * Return Fan RPM given N & tach * count and N are retrived from the * ADM1031 chip. */ static int tach_to_rpm(int n, uint8_t tach) { if (n * tach == 0) return (0); return ((ADCSAMPLE * 60) / (n * tach)); } static int get_raw_fan_speed(env_fan_t *fanp, uint8_t *fanspeedp) { int fan_fd; int retval = 0; fan_fd = fanp->fd; if (fan_fd == -1) retval = -1; else if (ioctl(fan_fd, I2C_GET_FAN_SPEED, fanspeedp) == -1) { retval = -1; } return (retval); } /* * Get current fan speed * This function returns a RPM value for fanspeed * in fanspeedp. * Returns -1 on error, 0 if successful */ int get_fan_speed(env_fan_t *fanp, fanspeed_t *fanspeedp) { int fan_fd; uint8_t tach; fan_fd = fanp->fd; if (fan_fd == -1) return (-1); if (fanp->id == DIMM_FAN_ID) { return (get_dimm_fan_speed(fan_fd, fanspeedp)); } if (ioctl(fan_fd, I2C_GET_FAN_SPEED, &tach) == -1) { return (-1); } /* * Fanspeeds are reported as 0 * if the tach is out of range or fan status is off * and if monitoring fan status is enabled. */ if (mon_fanstat && (!fanp->fanstat || tach == FAN_OUT_OF_RANGE)) { *fanspeedp = 0; } else { *fanspeedp = tach_to_rpm(fanp->speedrange, tach); } return (0); } /* * Set fan speed * This function accepts a percentage of fan speed * from 0-100 and programs the HW monitor fans to the corresponding * fanspeed value. * Returns -1 on error, -2 on invalid args passed, 0 if successful */ int set_fan_speed(env_fan_t *fanp, fanspeed_t fanspeed) { int fan_fd; int retval = 0; uint8_t speed; fan_fd = fanp->fd; if (fan_fd == -1) return (-1); if (fanspeed < 0 || fanspeed > 100) return (-2); speed = (uint8_t)ADM_SETFANSPEED_CONV(fanspeed); if (ioctl(fan_fd, I2C_SET_FAN_SPEED, &speed) == -1) { retval = -1; } return (retval); } /* * close all fan devices */ static void envd_close_fans(void) { int i; env_fan_t *fanp; for (i = 0; (fanp = envd_fans[i]) != NULL; i++) { if (fanp->fd != -1) { (void) close(fanp->fd); fanp->fd = -1; } } } /* * Close sensor devices and freeup resources */ static void envd_close_sensors(void) { env_sensor_t *sensorp; int i; for (i = 0; i < N_ENVD_SENSORS; ++i) { sensorp = &envd_sensors[i]; if (sensorp->fd != -1) { (void) close(sensorp->fd); sensorp->fd = -1; } if (sensorp->crtbl != NULL) fini_table(sensorp->crtbl); } } /* * Open fan devices and initialize per fan data structure. * Returns #fans found. */ static int envd_setup_fans(void) { int i, fd; env_fan_t *fanp; char path[PATH_MAX]; int fancnt = 0; uint8_t n = 0; picl_nodehdl_t tnodeh; i2c_reg_t i2c_reg; for (i = 0; (fanp = envd_fans[i]) != NULL; i++) { /* make sure cpu0/1 present for validating cpu fans */ if (fanp->id == CPU0_FAN_ID) { if (ptree_get_node_by_path(CPU0_PATH, &tnodeh) != PICL_SUCCESS) { fanp->present = B_FALSE; continue; } } if (fanp->id == CPU1_FAN_ID) { if (ptree_get_node_by_path(CPU1_PATH, &tnodeh) != PICL_SUCCESS) { fanp->present = B_FALSE; continue; } } if (fanp->id == DIMM_FAN_ID) { if (ptree_get_node_by_path(DIMM_FAN_CONTROLLER_PATH, &tnodeh) != PICL_SUCCESS) { if (env_debug) envd_log(LOG_ERR, "dimm Fan not found in the system.\n"); fanp->present = B_FALSE; continue; } } (void) strcpy(path, "/devices"); (void) strlcat(path, fanp->devfs_path, sizeof (path)); fd = open(path, O_RDWR); if (fd == -1) { envd_log(LOG_CRIT, ENV_FAN_OPEN_FAIL, fanp->name, fanp->devfs_path, errno, strerror(errno)); fanp->present = B_FALSE; continue; } fanp->fd = fd; if (fanp->id == DIMM_FAN_ID) { /* * set the SW aware bit in command register. * Clear the Fan fault latch bit. */ i2c_reg.reg_num = PIC16F819_COMMAND_REGISTER; i2c_reg.reg_value = (PIC16F819_SW_AWARE_MODE | PIC16F819_FAN_FAULT_CLEAR); if (ioctl(fd, I2C_SET_REG, &i2c_reg) == -1) { if (env_debug) envd_log(LOG_ERR, "Error in writing to COMMAND reg. of DIMM FAN controller\n"); } } else { /* Get speed range value */ if (ioctl(fd, ADM1031_GET_FAN_FEATURE, &n) != -1) { fanp->speedrange = adm_speedrange_map[(n >> 6) & 0x03]; } else { fanp->speedrange = FAN_RANGE_DEFAULT; } } fanp->present = B_TRUE; fanp->fanstat = 0; fanp->cspeed = TACH_UNKNOWN; fanp->lspeed = TACH_UNKNOWN; fanp->conccnt = 0; fancnt++; } return (fancnt); } static int envd_setup_disks(void) { int ret, i, page_index, page_len; picl_nodehdl_t tnodeh; env_disk_t *diskp; uint_t vendor_id; uint_t device_id; uchar_t log_page[256]; /* * Check if the SCSi controller on the system is 1010 or 1030 */ if (ptree_get_node_by_path(SCSI_CONTROLLER_NODE_PATH, &tnodeh) != PICL_SUCCESS) { if (env_debug) envd_log(LOG_ERR, "On-Board SCSI controller not found in the system.\n"); monitor_disk_temp = 0; return (-1); } if ((ret = ptree_get_propval_by_name(tnodeh, VENDOR_ID, &vendor_id, sizeof (vendor_id))) != 0) { if (env_debug) envd_log(LOG_ERR, "Error in getting vendor-id for SCSI controller. ret = %d errno = 0x%d\n", ret, errno); monitor_disk_temp = 0; return (-1); } if ((ret = ptree_get_propval_by_name(tnodeh, DEVICE_ID, &device_id, sizeof (device_id))) != 0) { if (env_debug) envd_log(LOG_ERR, "Error in getting device-id for SCSI controller. ret = %d errno = 0x%d\n", ret, errno); monitor_disk_temp = 0; return (-1); } if (env_debug) envd_log(LOG_ERR, "vendor-id=0x%x device-id=0x%x\n", vendor_id, device_id); if ((vendor_id != LSI1030_VENDOR_ID) || (device_id != LSI1030_DEVICE_ID)) { monitor_disk_temp = 0; return (-1); } /* * We have found LSI1030 SCSi controller onboard. */ for (i = 0; (diskp = envd_disks[i]) != NULL; i++) { if (ptree_get_node_by_path(diskp->nodepath, &tnodeh) != PICL_SUCCESS) { diskp->present = B_FALSE; if (env_debug) envd_log(LOG_ERR, "DISK %d not found in the system.\n", diskp->id); continue; } diskp->fd = open(diskp->devfs_path, O_RDONLY); if (diskp->fd == -1) { diskp->present = B_FALSE; envd_log(LOG_ERR, "Error in opening %s errno = 0x%x\n", diskp->devfs_path, errno); continue; } diskp->present = B_TRUE; diskp->tpage_supported = B_FALSE; /* * Find out if the Temperature page is supported by the disk. */ ret = scsi_log_sense(diskp->fd, SUPPORTED_LPAGES, log_page, sizeof (log_page)); if (ret != 0) { continue; } page_len = ((log_page[2] << 8) & 0xFF00) | log_page[3]; for (page_index = LOGPAGEHDRSIZE; page_index < page_len + LOGPAGEHDRSIZE; page_index++) { switch (log_page[page_index]) { case TEMPERATURE_PAGE: diskp->tpage_supported = B_TRUE; if (env_debug) envd_log(LOG_ERR, "tpage supported for %s\n", diskp->nodepath); default: break; } } diskp->warning_tstamp = 0; diskp->shutdown_tstamp = 0; diskp->high_warning = disk_high_warn_temperature; diskp->low_warning = disk_low_warn_temperature; diskp->high_shutdown = disk_high_shutdown_temperature; diskp->low_shutdown = disk_low_shutdown_temperature; ret = get_disk_temp(diskp); } return (0); } /* * Open temperature sensor devices and initialize per sensor data structure. * Returns #sensors found. */ static int envd_setup_sensors(void) { env_sensor_t *sensorp; sensor_ctrl_blk_t *es_ptr; table_t *tblp; char path[PATH_MAX]; int sensorcnt = 0; int i, j, nentries; int16_t tmin = 0; picl_nodehdl_t tnodeh; for (i = 0; i < N_ENVD_SENSORS; ++i) { sensorp = &envd_sensors[i]; /* Initialize sensor's initial state */ sensorp->shutdown_initiated = B_FALSE; sensorp->warning_tstamp = 0; sensorp->shutdown_tstamp = 0; sensorp->error = 0; sensorp->crtbl = NULL; /* make sure cpu0/1 sensors are present */ if (sensorp->id == CPU0_SENSOR_ID) { if (ptree_get_node_by_path(CPU0_PATH, &tnodeh) != PICL_SUCCESS) { sensorp->present = B_FALSE; continue; } } if (sensorp->id == CPU1_SENSOR_ID) { if (ptree_get_node_by_path(CPU1_PATH, &tnodeh) != PICL_SUCCESS) { sensorp->present = B_FALSE; continue; } } (void) strcpy(path, "/devices"); (void) strlcat(path, sensorp->devfs_path, sizeof (path)); sensorp->fd = open(path, O_RDWR); if (sensorp->fd == -1) { envd_log(LOG_ERR, ENV_SENSOR_OPEN_FAIL, sensorp->name, sensorp->devfs_path, errno, strerror(errno)); sensorp->present = B_FALSE; continue; } sensorp->present = B_TRUE; sensorcnt++; /* * Get Tmin */ if (ioctl(sensorp->fd, ADM1031_GET_TEMP_MIN_RANGE, &tmin) != -1) { sensorp->tmin = TMIN(tmin); } else { sensorp->tmin = -1; } if (env_debug) envd_log(LOG_ERR, "Sensor %s tmin %d", sensorp->name, sensorp->tmin); /* * Create a correction table * if correction pairs are present in es * segment. */ es_ptr = sensorp->es_ptr; if (es_ptr == NULL) { continue; } nentries = es_ptr->correctionEntries; if (nentries <= 2) { if (env_debug) envd_log(LOG_CRIT, "sensor correction <2"); continue; } sensorp->crtbl = init_table(nentries); if (sensorp->crtbl == NULL) continue; tblp = sensorp->crtbl; tblp->xymap[0].x = (char)es_ptr->correctionPair[0].measured; tblp->xymap[0].y = (char)es_ptr->correctionPair[0].corrected; for (j = 1; j < nentries; ++j) { tblp->xymap[j].x = (char)es_ptr->correctionPair[j].measured; tblp->xymap[j].y = (char)es_ptr->correctionPair[j].corrected; if (tblp->xymap[j].x <= tblp->xymap[j - 1].x) { fini_table(tblp); sensorp->crtbl = NULL; envd_log(LOG_CRIT, ENV_FRU_BAD_ENVSEG, FRU_SEEPROM_NAME); break; } } if (env_debug) { envd_log(LOG_CRIT, "Sensor correction %s", sensorp->name); for (j = 0; j < nentries; j++) envd_log(LOG_CRIT, " %d %d", tblp->xymap[j].x, tblp->xymap[j].y); } } return (sensorcnt); } /* * Modify ADM Tmin/ranges depending what power level * we are from. */ static void updateadm_ranges(char *name, uchar_t cur_lpstate) { env_sensor_t *sensorp; fan_ctrl_blk_t *fanctl; uchar_t tmin; uchar_t trange; uint16_t tdata; int sysfd; uchar_t sys_id = SYS_HWM_ID; uint8_t mode; static uint16_t tsave[2] = {0, 0}; /* Index of saved Tmin/Trange for two sensors */ uint16_t tindex = 0; sensorp = sensor_lookup(name); if (sensorp == NULL) return; /* * If there is only one Control pairs then return */ fanctl = ((env_fan_t *)sensorp->fanp)->es_ptr; if (fanctl != NULL && fanctl->no_ctl_pairs <= 1) return; /* * if fan control specifies that ranges are same then * we skip re-programming adm chip. */ tmin = fanctl->fan_ctl_pairs[0].tMin; trange = fanctl->fan_ctl_pairs[0].tRange; if ((tmin == fanctl->fan_ctl_pairs[1].tMin) && (trange == fanctl->fan_ctl_pairs[1].tRange)) return; sysfd = open(hwm_devs[sys_id], O_RDWR); if (sysfd == -1) { if (env_debug) envd_log(LOG_ERR, ENV_ADM_OPEN_FAIL, hwm_devs[sys_id], errno, strerror(errno)); return; } tindex = ((strcmp(name, SENSOR_SYS_IN) == 0) ? 0 : 1); /* Read ADM default value only for the first time */ if (tsave[tindex] == 0) { if (ioctl(sensorp->fd, ADM1031_GET_TEMP_MIN_RANGE, &tsave[tindex]) == -1) { if (env_debug) envd_log(LOG_ERR, "read tminrange ioctl failed"); (void) close(sysfd); return; } } /* * Need to reinit ADM to manual mode for Tmin range to be * effective. */ mode = ADM1031_MANUAL_MODE; if (ioctl(sysfd, ADM1031_SET_MONITOR_MODE, &mode) == -1) { if (env_debug) envd_log(LOG_ERR, ENV_ADM_MANUAL_MODE); (void) close(sysfd); return; } if (cur_lpstate == 1) { /* * ADM 1031 Tmin/Trange register need to be reprogrammed. */ tdata = ((fanctl->fan_ctl_pairs[cur_lpstate].tMin / TMIN_UNITS) << TMIN_SHIFT); /* Need to pack tRange in ADM bits 2:0 */ switch (fanctl->fan_ctl_pairs[cur_lpstate].tRange) { case 5: break; case 10: tdata |= 1; break; case 20: tdata |= 2; break; case 40: tdata |= 3; break; case 80: tdata |= 4; break; } } else tdata = tsave[tindex]; if (ioctl(sensorp->fd, ADM1031_SET_TEMP_MIN_RANGE, &tdata) != -1) sensorp->tmin = TMIN(tdata); mode = ADM1031_AUTO_MODE; if (ioctl(sysfd, ADM1031_SET_MONITOR_MODE, &mode) == -1) { if (env_debug) envd_log(LOG_ERR, ENV_ADM_AUTO_MODE); } (void) close(sysfd); } /* ARGSUSED */ static void * pmthr(void *args) { pm_state_change_t pmstate; char physpath[PATH_MAX]; int pre_lpstate; pmstate.physpath = physpath; pmstate.size = sizeof (physpath); cur_lpstate = 0; pre_lpstate = 1; pm_fd = open(PM_DEVICE, O_RDWR); if (pm_fd == -1) { envd_log(LOG_ERR, PM_THREAD_EXITING, errno, strerror(errno)); return (NULL); } for (;;) { /* * Get PM state change events to check if the system * is in lowest power state and adjust ADM hardware * monitor's fan speed settings. * * To minimize polling, we use the blocking interface * to get the power state change event here. */ if (ioctl(pm_fd, PM_GET_STATE_CHANGE_WAIT, &pmstate) != 0) { if (errno != EINTR) break; continue; } do { if (env_debug) { envd_log(LOG_INFO, "pmstate event:0x%x flags:%x" "comp:%d oldval:%d newval:%d path:%s\n", pmstate.event, pmstate.flags, pmstate.component, pmstate.old_level, pmstate.new_level, pmstate.physpath); } cur_lpstate = (pmstate.flags & PSC_ALL_LOWEST) ? 1 : 0; } while (ioctl(pm_fd, PM_GET_STATE_CHANGE, &pmstate) == 0); /* * Change ADM ranges as per E* Requirements. Update * happens only for valid state changes. */ if (pre_lpstate != cur_lpstate) { pre_lpstate = cur_lpstate; updateadm_ranges(SENSOR_SYS_OUT, cur_lpstate); updateadm_ranges(SENSOR_SYS_IN, cur_lpstate); } } /* Not reached */ return (NULL); } /* * This function is used to reasonably predict the * state of the fan (ON/OFF) using tmin and current temperature. * * We know the fan is on if temp >= tmin and fan is off if * temp < (Tmin - Hysterisis). * * When the temperature is in between we don't know if the fan is on/off * because the temperature could be decreasing and not have crossed * Tmin - hysterisis and vice a versa. * * FAN ON * Tmin * ------------------------------------------- * * FAN ON/OFF * * -------------------------------------------- * Tmin - Hysterisis * FAN OFF * * To solve the problem of finding out if the fan is on/off in our gray region * we keep track of the last read tach and the current read tach. From * experimentation and from discussions with analog devices it is unlikely that * if the fans are on we will get a constant tach reading more than 5 times in * a row. This is not but the most fool proof approach but the best we can do. * * This routine implements the above logic for a sensor with an * associated fan. The caller garauntees sensorp and fanp are not null. */ static void check_fanstat(env_sensor_t *sensorp) { env_fan_t *fanp = sensorp->fanp; tempr_t temp; uint8_t fanspeed; if (get_raw_temperature(sensorp, &temp) == -1) return; if (temp < (sensorp->tmin - ADM_HYSTERISIS)) { fanp->fanstat = 0; /* Fan off */ fanp->lspeed = TACH_UNKNOWN; /* Reset Last read tach */ fanp->conccnt = 0; } else if (temp >= sensorp->tmin) { fanp->fanstat = 1; /* Fan on */ fanp->lspeed = TACH_UNKNOWN; fanp->conccnt = 0; } else { if (get_raw_fan_speed(fanp, &fanspeed) == -1) return; fanp->cspeed = fanspeed; /* * First time in the gray area * set last read speed to current speed */ if (fanp->lspeed == TACH_UNKNOWN) { fanp->lspeed = fanspeed; } else { if (fanp->lspeed != fanp->cspeed) { fanp->conccnt = 0; fanp->fanstat = 1; } else { fanp->conccnt++; if (fanp->conccnt >= N_SEQ_TACH) fanp->fanstat = 0; } fanp->lspeed = fanp->cspeed; } } } /* * There is an issue with the ADM1031 chip that causes the chip * to not update the tach register in case the fan stops. The * fans stop when the temperature measured (temp) drops below * Tmin - Hysterisis and turn on when the temp >= Tmin. * * Since the tach registers don't update and remain stuck at the * last read tach value our get_fan_speed function always returns * a non-zero RPM reading. * * To fix this we need to figure out when the fans will be on/off * depending on the current temperature. Currently we poll for * interrupts, we can use that loop to determine what the current * temperature is and if the fans should be on/off. * * We get current temperature and check the fans. */ static void monitor_fanstat(void) { env_sensor_t *sensorp; env_fan_t *fanp; int i; for (i = 0; i < N_ENVD_SENSORS; i++) { sensorp = &envd_sensors[i]; if (!sensorp) continue; fanp = sensorp->fanp; if (!(fanp && fanp->present)) continue; if (sensorp->tmin != -1) { check_fanstat(sensorp); } else { fanp->fanstat = 1; } } } static int handle_overtemp_interrupt(int hwm_id) { env_sensor_t *sensorp; tempr_t temp; uchar_t smap[MAX_SENSORS]; time_t ct; uchar_t i; char msgbuf[BUFSIZ]; char syscmd[BUFSIZ]; boolean_t return_flag; int ret; timespec_t to; pthread_mutex_t env_monitor_mutex = PTHREAD_MUTEX_INITIALIZER; pthread_cond_t env_monitor_cv = PTHREAD_COND_INITIALIZER; /* Clear Map of Sensor Entries */ (void) memset(smap, SENSOR_OK, sizeof (smap)); for (;;) { for (i = 0; i < N_ENVD_SENSORS; i++) { sensorp = &envd_sensors[i]; /* * Check whether the sensor belongs to the * interrupting ADM hardware monitor */ if (sensorp->hwm_id != hwm_id) continue; if (sensorp->present == B_FALSE) continue; /* * if shutdown is initiated then we simply loop * through the sensors until shutdown */ if (sensorp->shutdown_initiated == B_TRUE) continue; /* get current temp for this sensor */ if (get_temperature(sensorp, &temp) == -1) continue; sensorp->cur_temp = temp; if (env_debug) envd_log(LOG_ERR, "sensor name %s, cur temp %d, " "HW %d LW %d SD %d LS %d\n", sensorp->name, temp, sensorp->es_ptr->high_warning, (int)sensorp->es_ptr->low_warning, sensorp->es_ptr->high_shutdown, (int)sensorp->es_ptr->low_shutdown); if (TEMP_IN_WARNING_RANGE(sensorp->cur_temp, sensorp)) { /* * Log on warning atmost one second */ ct = (time_t)(gethrtime() / NANOSEC); if ((ct - sensorp->warning_tstamp) >= warning_interval) { envd_log(LOG_CRIT, ENV_WARNING_MSG, sensorp->name, temp, sensorp->es_ptr->low_warning, sensorp->es_ptr->high_warning); sensorp->warning_tstamp = ct; } smap[i] = SENSOR_WARN; } else { /* * We will fall in this caterory only if * Temperature drops/increases from warning * threshold. If so we set sensor map to * OK so that we can exit the loop if * shutdown not initiated. */ smap[i] = SENSOR_OK; } if (TEMP_IN_SHUTDOWN_RANGE(temp, sensorp) && !shutdown_override) { ct = (time_t)(gethrtime() / NANOSEC); if (sensorp->shutdown_tstamp == 0) sensorp->shutdown_tstamp = ct; if ((ct - sensorp->shutdown_tstamp) >= shutdown_interval) { sensorp->shutdown_initiated = B_TRUE; (void) snprintf(msgbuf, sizeof (msgbuf), ENV_SHUTDOWN_MSG, sensorp->name, temp, sensorp->es_ptr->low_shutdown, sensorp->es_ptr->high_shutdown); envd_log(LOG_ALERT, msgbuf); } if (system_shutdown_started == B_FALSE) { (void) snprintf(syscmd, sizeof (syscmd), "%s \"%s\"", SHUTDOWN_CMD, msgbuf); envd_log(LOG_ALERT, syscmd); system_shutdown_started = B_TRUE; (void) system(syscmd); } } else if (sensorp->shutdown_tstamp != 0) sensorp->shutdown_tstamp = 0; } /* * Sweep thorugh Sensor Map and if warnings OR shutdown * are not logged then return to caller. */ return_flag = B_TRUE; for (i = 0; i < N_ENVD_SENSORS; i++) if (smap[i] == SENSOR_WARN) return_flag = B_FALSE; if ((return_flag == B_TRUE) && (system_shutdown_started == B_FALSE)) { return (1); } wait_till_timeout: /* * We use pthread_cond_reltimedwait_np to sleep for * fixed interval of time. * earlier implementation used alarm() call which * fails in Multi threaded environment. If multiple * threads call alarm() only one of the threads is * sent the SIGALRM signal. */ (void) pthread_mutex_lock(&env_monitor_mutex); ret = pthread_cond_reltimedwait_np(&env_monitor_cv, &env_monitor_mutex, &to); to.tv_sec = SENSORPOLL_INTERVAL; to.tv_nsec = 0; if (ret != ETIMEDOUT) { (void) pthread_mutex_unlock(&env_monitor_mutex); goto wait_till_timeout; } (void) pthread_mutex_unlock(&env_monitor_mutex); } } /* * This is env thread which monitors the current temperature when * warning threshold is exceeded. The job is to make sure it does * not execced/decrease shutdown threshold. If it does it will start * forced shutdown to avoid reaching hardware poweroff via THERM interrupt. * For Enchilada there will be two threads, one for each ADM chip. */ static void * ovtemp_thr(void *args) { int fd; uint8_t stat[2]; int hwm_id = (int)args; int err; env_fan_t *fanp; timespec_t to; int ret; pthread_mutex_t env_monitor_mutex = PTHREAD_MUTEX_INITIALIZER; pthread_cond_t env_monitor_cv = PTHREAD_COND_INITIALIZER; fd = open(hwm_devs[hwm_id], O_RDWR); if (fd == -1) { envd_log(LOG_ERR, ENV_ADM_OPEN_FAIL, hwm_devs[hwm_id], errno, strerror(errno)); return (NULL); } if (env_debug) envd_log(LOG_ERR, "ovtemp thread for %s running...\n", hwm_devs[hwm_id]); for (;;) { /* * Sleep for specified seconds before issuing IOCTL * again. */ /* * We use pthread_cond_reltimedwait_np to sleep for * fixed interval of time. * earlier implementation used alarm() call which * fails in Multi threaded environment. If multiple * threads call alarm() only one of the threads is * sent the SIGALRM signal. */ (void) pthread_mutex_lock(&env_monitor_mutex); ret = pthread_cond_reltimedwait_np(&env_monitor_cv, &env_monitor_mutex, &to); to.tv_sec = INTERRUPTPOLL_INTERVAL; to.tv_nsec = 0; if (ret != ETIMEDOUT) { (void) pthread_mutex_unlock(&env_monitor_mutex); continue; } (void) pthread_mutex_unlock(&env_monitor_mutex); /* * Monitor the sensors to update fan status */ if (mon_fanstat) monitor_fanstat(); /* * Read ADM1031 two Status Registers to determine source * of Interrupts. */ if ((err = ioctl(fd, ADM1031_GET_STATUS_1, &stat[0])) != -1) err = ioctl(fd, ADM1031_GET_STATUS_2, &stat[1]); if (err == -1) { if (env_debug) envd_log(LOG_ERR, "OverTemp: Status Error"); continue; } if (env_debug) envd_log(LOG_ERR, "INTR %s, Stat1 %x, Stat2 %x", hwm_devs[hwm_id], stat[0], stat[1]); if (stat[0] & FANFAULT) { fanp = fan_lookup(hwm_fans[hwm_id][HWM_FAN1]); if (fanp && fanp->present) envd_log(LOG_ERR, ENV_FAN_FAULT, hwm_devs[hwm_id], hwm_fans[hwm_id][HWM_FAN1]); } if (stat[1] & FANFAULT) { fanp = fan_lookup(hwm_fans[hwm_id][HWM_FAN2]); if (fanp && fanp->present) envd_log(LOG_ERR, ENV_FAN_FAULT, hwm_devs[hwm_id], hwm_fans[hwm_id][HWM_FAN2]); } /* * Check respective Remote/Local High, Low before start * manual monitoring */ if ((stat[0] & STAT1MASK) || (stat[1] & STAT2MASK)) (void) handle_overtemp_interrupt(hwm_id); } /* end of for ever loop */ /*NOTREACHED*/ return (NULL); } static void * dimm_fan_thr(void *args) { char syscmd[BUFSIZ]; char msgbuf[BUFSIZ]; i2c_reg_t i2c_reg; timespec_t to; int ret; pthread_mutex_t env_monitor_mutex = PTHREAD_MUTEX_INITIALIZER; pthread_cond_t env_monitor_cv = PTHREAD_COND_INITIALIZER; #ifdef __lint args = args; #endif for (;;) { /* * Sleep for specified seconds before issuing IOCTL * again. */ (void) pthread_mutex_lock(&env_monitor_mutex); ret = pthread_cond_reltimedwait_np(&env_monitor_cv, &env_monitor_mutex, &to); to.tv_sec = INTERRUPTPOLL_INTERVAL; to.tv_nsec = 0; if (ret != ETIMEDOUT) { (void) pthread_mutex_unlock(&env_monitor_mutex); continue; } (void) pthread_mutex_unlock(&env_monitor_mutex); /* * We write to the comand register periodically * to inform the PIC firmware that Solaris is * Monitoring the dimm fan periodically. */ i2c_reg.reg_num = PIC16F819_COMMAND_REGISTER; i2c_reg.reg_value = PIC16F819_SW_AWARE_MODE; if (ioctl(envd_dimm_fan.fd, I2C_SET_REG, &i2c_reg) == -1) { if (env_debug) envd_log(LOG_ERR, "Error in writing to COMMAND reg. of DIMM FAN controller\n"); } /* * We initiate shutdown if fan status indicates * failure. */ if (is_dimm_fan_failed() != 0) { /* * Mark Dimm fan present as False so that we * do not WARN the user of the Fan failure * repeatedly. */ envd_dimm_fan.present = B_FALSE; (void) snprintf(msgbuf, sizeof (msgbuf), ENV_DIMM_FAN_FAILURE_SHUTDOWN_MSG, ENV_DIMM_FAN, dimm_fan_rpm_string, dimm_fan_status_string, dimm_fan_command_string, dimm_fan_debug_string); envd_log(LOG_ALERT, msgbuf); if (system_shutdown_started == B_FALSE) { system_shutdown_started = B_TRUE; (void) snprintf(syscmd, sizeof (syscmd), "%s \"%s\"", SHUTDOWN_CMD, msgbuf); envd_log(LOG_ALERT, syscmd); (void) system(syscmd); } } } /*NOTREACHED*/ return (NULL); } static int scsi_log_sense(int fd, uchar_t page_code, uchar_t *pagebuf, uint16_t pagelen) { struct uscsi_cmd ucmd_buf; uchar_t cdb_buf[CDB_GROUP1]; struct scsi_extended_sense sense_buf; int ret_val; bzero((void *)&cdb_buf, sizeof (cdb_buf)); bzero((void *)&ucmd_buf, sizeof (ucmd_buf)); bzero((void *)&sense_buf, sizeof (sense_buf)); cdb_buf[0] = SCMD_LOG_SENSE_G1; cdb_buf[2] = (0x01 << 6) | page_code; cdb_buf[7] = (uchar_t)((pagelen & 0xFF00) >> 8); cdb_buf[8] = (uchar_t)(pagelen & 0x00FF); ucmd_buf.uscsi_cdb = (char *)cdb_buf; ucmd_buf.uscsi_cdblen = sizeof (cdb_buf); ucmd_buf.uscsi_bufaddr = (caddr_t)pagebuf; ucmd_buf.uscsi_buflen = pagelen; ucmd_buf.uscsi_rqbuf = (caddr_t)&sense_buf; ucmd_buf.uscsi_rqlen = sizeof (struct scsi_extended_sense); ucmd_buf.uscsi_flags = USCSI_RQENABLE | USCSI_READ | USCSI_SILENT; ucmd_buf.uscsi_timeout = 60; ret_val = ioctl(fd, USCSICMD, ucmd_buf); if (ret_val == 0 && ucmd_buf.uscsi_status == 0) { if (env_debug) envd_log(LOG_ERR, "log sense command for page_code 0x%x succeeded\n", page_code); return (ret_val); } if (env_debug) envd_log(LOG_ERR, "log sense command failed.ret_val = 0x%x status = 0x%x errno = 0x%x\n", ret_val, ucmd_buf.uscsi_status, errno); return (1); } static int get_disk_temp(env_disk_t *diskp) { int ret; uchar_t tpage[256]; ret = scsi_log_sense(diskp->fd, TEMPERATURE_PAGE, tpage, sizeof (tpage)); if (ret != 0) { diskp->current_temp = DISK_INVALID_TEMP; diskp->ref_temp = DISK_INVALID_TEMP; return (-1); } /* * For the current temperature verify that the parameter * length is 0x02 and the parameter code is 0x00 * Temperature value of 255(0xFF) is considered INVALID. */ if ((tpage[7] == 0x02) && (tpage[4] == 0x00) && (tpage[5] == 0x00)) { if (tpage[9] == 0xFF) { diskp->current_temp = DISK_INVALID_TEMP; return (-1); } else { diskp->current_temp = tpage[9]; } } /* * For the reference temperature verify that the parameter * length is 0x02 and the parameter code is 0x01 * Temperature value of 255(0xFF) is considered INVALID. */ if ((tpage[13] == 0x02) && (tpage[10] == 0x00) && (tpage[11] == 0x01)) { if (tpage[15] == 0xFF) { diskp->ref_temp = DISK_INVALID_TEMP; } else { diskp->ref_temp = tpage[15]; } } return (0); } /* ARGSUSED */ static void * disk_temp_thr(void *args) { char syscmd[BUFSIZ]; char msgbuf[BUFSIZ]; timespec_t to; int ret, i; env_disk_t *diskp; pthread_mutex_t env_monitor_mutex = PTHREAD_MUTEX_INITIALIZER; pthread_cond_t env_monitor_cv = PTHREAD_COND_INITIALIZER; pm_state_change_t pmstate; int idle_time; int disk_pm_fd; time_t ct; disk_pm_fd = open(PM_DEVICE, O_RDWR); if (disk_pm_fd == -1) { envd_log(LOG_ERR, DISK_TEMP_THREAD_EXITING, errno, strerror(errno)); return (NULL); } for (;;) { /* * Sleep for specified seconds before issuing IOCTL * again. */ (void) pthread_mutex_lock(&env_monitor_mutex); ret = pthread_cond_reltimedwait_np(&env_monitor_cv, &env_monitor_mutex, &to); to.tv_sec = disk_scan_interval; to.tv_nsec = 0; if (ret != ETIMEDOUT) { (void) pthread_mutex_unlock(&env_monitor_mutex); continue; } (void) pthread_mutex_unlock(&env_monitor_mutex); for (i = 0; (diskp = envd_disks[i]) != NULL; i++) { if (diskp->present == B_FALSE) continue; if (diskp->tpage_supported == B_FALSE) continue; /* * If the disk temperature is above the warning threshold * continue monitoring until the temperature drops below * warning threshold. * if the temperature is in the NORMAL range monitor only * when the disk is BUSY. * We do not want to read the disk temperature if the disk is * is idling. The reason for this is disk will never get into * lowest power mode if we scan the disk temperature * peridoically. To avoid this situation we first determine * the idle_time of the disk. If the disk has been IDLE since * we scanned the temperature last time we will not read the * temperature. */ if (!DISK_TEMP_IN_WARNING_RANGE(diskp->current_temp, diskp)) { pmstate.physpath = diskp->physpath; pmstate.size = strlen(diskp->physpath); pmstate.component = 0; if ((idle_time = ioctl(disk_pm_fd, PM_GET_TIME_IDLE, &pmstate)) == -1) { if (errno != EINTR) { if (env_debug) envd_log(LOG_ERR, "ioctl PM_GET_TIME_IDLE failed for DISK0. errno=0x%x\n", errno); continue; } continue; } if (idle_time >= (disk_scan_interval/2)) { if (env_debug) { envd_log(LOG_ERR, "%s idle time = %d\n", diskp->name, idle_time); } continue; } } ret = get_disk_temp(diskp); if (ret != 0) continue; if (env_debug) { envd_log(LOG_ERR, "%s temp = %d ref. temp = %d\n", diskp->name, diskp->current_temp, diskp->ref_temp); } /* * If this disk already triggered system shutdown, don't * log any more shutdown/warning messages for it. */ if (diskp->shutdown_initiated) continue; /* * Check for the temperature in warning and shutdown range * and take appropriate action. */ if (DISK_TEMP_IN_WARNING_RANGE(diskp->current_temp, diskp)) { /* * Check if the temperature has been in warning * range during last disk_warning_duration interval. * If so, the temperature is truly in warning * range and we need to log a warning message, * but no more than once every disk_warning_interval * seconds. */ time_t wtstamp = diskp->warning_tstamp; ct = (time_t)(gethrtime() / NANOSEC); if (diskp->warning_start == 0) diskp->warning_start = ct; if (((ct - diskp->warning_start) >= disk_warning_duration) && (wtstamp == 0 || (ct - wtstamp) >= disk_warning_interval)) { envd_log(LOG_CRIT, ENV_WARNING_MSG, diskp->name, diskp->current_temp, diskp->low_warning, diskp->high_warning); diskp->warning_tstamp = ct; } } else if (diskp->warning_start != 0) diskp->warning_start = 0; if (!shutdown_override && DISK_TEMP_IN_SHUTDOWN_RANGE(diskp->current_temp, diskp)) { ct = (time_t)(gethrtime() / NANOSEC); if (diskp->shutdown_tstamp == 0) diskp->shutdown_tstamp = ct; /* * Shutdown the system if the temperature remains * in the shutdown range for over disk_shutdown_interval * seconds. */ if ((ct - diskp->shutdown_tstamp) >= disk_shutdown_interval) { /* log error */ diskp->shutdown_initiated = B_TRUE; (void) snprintf(msgbuf, sizeof (msgbuf), ENV_SHUTDOWN_MSG, diskp->name, diskp->current_temp, diskp->low_shutdown, diskp->high_shutdown); envd_log(LOG_ALERT, msgbuf); /* shutdown the system (only once) */ if (system_shutdown_started == B_FALSE) { (void) snprintf(syscmd, sizeof (syscmd), "%s \"%s\"", shutdown_cmd, msgbuf); envd_log(LOG_ALERT, syscmd); system_shutdown_started = B_TRUE; (void) system(syscmd); } } } else if (diskp->shutdown_tstamp != 0) diskp->shutdown_tstamp = 0; } } /* end of forever loop */ } /* * Setup envrionmental monitor state and start threads to monitor * temperature and power management state. * Returns -1 on error, 0 if successful. */ static int envd_setup(void) { int ret; if (getenv("SUNW_piclenvd_debug") != NULL) env_debug = 1; if (pthread_attr_init(&thr_attr) != 0 || pthread_attr_setscope(&thr_attr, PTHREAD_SCOPE_SYSTEM) != 0) { return (-1); } ret = envd_es_setup(); if (ret < 0) { ovtemp_monitor = 0; pm_monitor = 0; } /* * Setup temperature sensors and fail if we can't open * at least one sensor. */ if (envd_setup_sensors() <= 0) { return (NULL); } /* * Setup fan device (don't fail even if we can't access * the fan as we can still monitor temeperature. */ (void) envd_setup_fans(); (void) envd_setup_disks(); /* If ES Segment setup failed,don't create thread */ if (ovtemp_monitor && ovtemp_thr1_created == B_FALSE) { if (pthread_create(&ovtemp_thr1_id, &thr_attr, ovtemp_thr, (void *)CPU_HWM_ID) != 0) envd_log(LOG_ERR, ENVTHR_THREAD_CREATE_FAILED); else ovtemp_thr1_created = B_TRUE; } if (ovtemp_monitor && ovtemp_thr2_created == B_FALSE) { if (pthread_create(&ovtemp_thr2_id, &thr_attr, ovtemp_thr, (void *)SYS_HWM_ID) != 0) envd_log(LOG_ERR, ENVTHR_THREAD_CREATE_FAILED); else ovtemp_thr2_created = B_TRUE; } if (envd_dimm_fan.present) { if (dimm_fan_thr_created == B_FALSE) { if (pthread_create(&dimm_fan_thr_id, &thr_attr, dimm_fan_thr, NULL) != 0) envd_log(LOG_ERR, ENVTHR_THREAD_CREATE_FAILED); else dimm_fan_thr_created = B_TRUE; } } /* * Create a thread to monitor PM state */ if (pm_monitor && pmthr_created == B_FALSE) { if (pthread_create(&pmthr_tid, &thr_attr, pmthr, NULL) != 0) envd_log(LOG_CRIT, PM_THREAD_CREATE_FAILED); else pmthr_created = B_TRUE; } if (monitor_disk_temp) { if (disk_temp_thr_created == B_FALSE) { if (pthread_create(&disk_temp_thr_id, &thr_attr, disk_temp_thr, NULL) != 0) envd_log(LOG_ERR, ENVTHR_THREAD_CREATE_FAILED); else disk_temp_thr_created = B_TRUE; } } return (0); } static void piclenvd_register(void) { picld_plugin_register(&my_reg_info); } static void piclenvd_init(void) { (void) env_picl_setup_tuneables(); /* * Setup the environmental data structures */ if (envd_setup() != 0) { envd_log(LOG_CRIT, ENVD_PLUGIN_INIT_FAILED); return; } /* * Now setup/populate PICL tree */ env_picl_setup(); } static void piclenvd_fini(void) { /* * Invoke env_picl_destroy() to remove any PICL nodes/properties * (including volatile properties) we created. Once this call * returns, there can't be any more calls from the PICL framework * to get current temperature or fan speed. */ env_picl_destroy(); envd_close_sensors(); envd_close_fans(); envd_es_destroy(); } /*VARARGS2*/ void envd_log(int pri, const char *fmt, ...) { va_list ap; va_start(ap, fmt); vsyslog(pri, fmt, ap); va_end(ap); } /* * Tunables support functions */ static env_tuneable_t * tuneable_lookup(picl_prophdl_t proph) { int i; env_tuneable_t *tuneablep = NULL; for (i = 0; i < ntuneables; i++) { tuneablep = &tuneables[i]; if (tuneablep->proph == proph) return (tuneablep); } return (NULL); } static int get_cpu_tach(ptree_rarg_t *parg, void *buf) { picl_prophdl_t proph; env_tuneable_t *tuneablep; int fd; int8_t cfg; proph = parg->proph; tuneablep = tuneable_lookup(proph); if (tuneablep == NULL) return (PICL_FAILURE); fd = open(CPU_HWM_DEVFS, O_RDWR); if (fd == -1) { return (PICL_FAILURE); } if (ioctl(fd, ADM1031_GET_CONFIG_2, &cfg) == -1) { return (PICL_FAILURE); } if ((cfg & TACH_ENABLE_MASK) == TACH_ENABLE_MASK) { *((int *)tuneablep->value) = ENABLE; } else { *((int *)tuneablep->value) = DISABLE; } (void) memcpy(buf, tuneablep->value, tuneablep->nbytes); (void) close(fd); return (PICL_SUCCESS); } static int set_cpu_tach(ptree_warg_t *parg, const void *buf) { picl_prophdl_t proph; env_tuneable_t *tuneablep; int fd, val; int8_t cfg; if (parg->cred.dc_euid != 0) return (PICL_PERMDENIED); proph = parg->proph; tuneablep = tuneable_lookup(proph); if (tuneablep == NULL) return (PICL_FAILURE); fd = open(CPU_HWM_DEVFS, O_RDWR); if (fd == -1) { return (PICL_FAILURE); } if (ioctl(fd, ADM1031_GET_CONFIG_2, &cfg) == -1) { return (PICL_FAILURE); } (void) memcpy(&val, (caddr_t)buf, sizeof (val)); if (val == ENABLE) { cfg |= TACH_ENABLE_MASK; } else if (val == DISABLE) { cfg &= ~TACH_ENABLE_MASK; } if (ioctl(fd, ADM1031_SET_CONFIG_2, &cfg) == -1) { return (PICL_FAILURE); } (void) close(fd); return (PICL_SUCCESS); } static int get_sys_tach(ptree_rarg_t *parg, void *buf) { picl_prophdl_t proph; env_tuneable_t *tuneablep; int fd; int8_t cfg; proph = parg->proph; tuneablep = tuneable_lookup(proph); if (tuneablep == NULL) return (PICL_FAILURE); fd = open(SYS_HWM_DEVFS, O_RDWR); if (fd == -1) { return (PICL_FAILURE); } if (ioctl(fd, ADM1031_GET_CONFIG_2, &cfg) == -1) { return (PICL_FAILURE); } if ((cfg & TACH_ENABLE_MASK) == TACH_ENABLE_MASK) { *((int *)tuneablep->value) = ENABLE; } else { *((int *)tuneablep->value) = DISABLE; } (void) memcpy(buf, tuneablep->value, tuneablep->nbytes); (void) close(fd); return (PICL_SUCCESS); } static int set_sys_tach(ptree_warg_t *parg, const void *buf) { picl_prophdl_t proph; env_tuneable_t *tuneablep; int fd, val; int8_t cfg; if (parg->cred.dc_euid != 0) return (PICL_PERMDENIED); proph = parg->proph; tuneablep = tuneable_lookup(proph); if (tuneablep == NULL) return (PICL_FAILURE); fd = open(SYS_HWM_DEVFS, O_RDWR); if (fd == -1) { return (PICL_FAILURE); } if (ioctl(fd, ADM1031_GET_CONFIG_2, &cfg) == -1) { return (PICL_FAILURE); } (void) memcpy(&val, buf, sizeof (val)); if (val == ENABLE) { cfg |= TACH_ENABLE_MASK; } else if (val == DISABLE) { cfg &= ~TACH_ENABLE_MASK; } if (ioctl(fd, ADM1031_SET_CONFIG_2, &cfg) == -1) { return (PICL_FAILURE); } (void) close(fd); return (PICL_SUCCESS); } static int get_monitor_cpu_mode(ptree_rarg_t *parg, void *buf) { picl_prophdl_t proph; env_tuneable_t *tuneablep; int fd; int8_t mmode; proph = parg->proph; tuneablep = tuneable_lookup(proph); if (tuneablep == NULL) return (PICL_FAILURE); fd = open(CPU_HWM_DEVFS, O_RDWR); if (fd == -1) { return (PICL_FAILURE); } if (ioctl(fd, ADM1031_GET_MONITOR_MODE, &mmode) == -1) { return (PICL_FAILURE); } if (mmode == ADM1031_AUTO_MODE) { *((int *)tuneablep->value) = ENABLE; } else { *((int *)tuneablep->value) = DISABLE; } (void) memcpy(buf, tuneablep->value, tuneablep->nbytes); (void) close(fd); return (PICL_SUCCESS); } static int set_monitor_cpu_mode(ptree_warg_t *parg, const void *buf) { picl_prophdl_t proph; env_tuneable_t *tuneablep; int fd, val; int8_t mmode; if (parg->cred.dc_euid != 0) return (PICL_PERMDENIED); proph = parg->proph; tuneablep = tuneable_lookup(proph); if (tuneablep == NULL) return (PICL_FAILURE); fd = open(CPU_HWM_DEVFS, O_RDWR); if (fd == -1) { return (PICL_FAILURE); } (void) memcpy(&val, buf, sizeof (val)); if (val == ENABLE) { mmode = ADM1031_AUTO_MODE; } else if (val == DISABLE) { mmode = ADM1031_MANUAL_MODE; } if (ioctl(fd, ADM1031_SET_MONITOR_MODE, &mmode) == -1) { return (PICL_FAILURE); } (void) close(fd); return (PICL_SUCCESS); } static int get_monitor_sys_mode(ptree_rarg_t *parg, void *buf) { picl_prophdl_t proph; env_tuneable_t *tuneablep; int fd; int8_t mmode; proph = parg->proph; tuneablep = tuneable_lookup(proph); if (tuneablep == NULL) return (PICL_FAILURE); fd = open(SYS_HWM_DEVFS, O_RDWR); if (fd == -1) { return (PICL_FAILURE); } if (ioctl(fd, ADM1031_GET_MONITOR_MODE, &mmode) == -1) { return (PICL_FAILURE); } if (mmode == ADM1031_AUTO_MODE) { *((int *)tuneablep->value) = ENABLE; } else { *((int *)tuneablep->value) = DISABLE; } (void) memcpy(buf, tuneablep->value, tuneablep->nbytes); (void) close(fd); return (PICL_SUCCESS); } static int set_monitor_sys_mode(ptree_warg_t *parg, const void *buf) { picl_prophdl_t proph; env_tuneable_t *tuneablep; int fd, val; int8_t mmode; if (parg->cred.dc_euid != 0) return (PICL_PERMDENIED); proph = parg->proph; tuneablep = tuneable_lookup(proph); if (tuneablep == NULL) return (PICL_FAILURE); fd = open(SYS_HWM_DEVFS, O_RDWR); if (fd == -1) { return (PICL_FAILURE); } (void) memcpy(&val, buf, sizeof (val)); if (val == ENABLE) { mmode = ADM1031_AUTO_MODE; } else if (val == DISABLE) { mmode = ADM1031_MANUAL_MODE; } if (ioctl(fd, ADM1031_SET_MONITOR_MODE, &mmode) == -1) { return (PICL_FAILURE); } (void) close(fd); return (PICL_SUCCESS); } static int get_string_val(ptree_rarg_t *parg, void *buf) { picl_prophdl_t proph; env_tuneable_t *tuneablep; proph = parg->proph; tuneablep = tuneable_lookup(proph); if (tuneablep == NULL) return (PICL_FAILURE); (void) memcpy(buf, (caddr_t)tuneablep->value, tuneablep->nbytes); return (PICL_SUCCESS); } static int set_string_val(ptree_warg_t *parg, const void *buf) { picl_prophdl_t proph; env_tuneable_t *tuneablep; if (parg->cred.dc_euid != 0) return (PICL_PERMDENIED); proph = parg->proph; tuneablep = tuneable_lookup(proph); if (tuneablep == NULL) return (PICL_FAILURE); (void) memcpy((caddr_t)tuneables->value, (caddr_t)buf, tuneables->nbytes); return (PICL_SUCCESS); } static int get_int_val(ptree_rarg_t *parg, void *buf) { picl_prophdl_t proph; env_tuneable_t *tuneablep; proph = parg->proph; tuneablep = tuneable_lookup(proph); if (tuneablep == NULL) return (PICL_FAILURE); (void) memcpy((int *)buf, (int *)tuneablep->value, tuneablep->nbytes); return (PICL_SUCCESS); } static int set_int_val(ptree_warg_t *parg, const void *buf) { picl_prophdl_t proph; env_tuneable_t *tuneablep; if (parg->cred.dc_euid != 0) return (PICL_PERMDENIED); proph = parg->proph; tuneablep = tuneable_lookup(proph); if (tuneablep == NULL) return (PICL_FAILURE); (void) memcpy((int *)tuneablep->value, (int *)buf, tuneablep->nbytes); return (PICL_SUCCESS); } int get_dimm_fan_speed(int fan_fd, fanspeed_t *fanspeedp) { int16_t dimm_fan_period; i2c_reg_t i2c_reg; /* * The dimm fan period is 16 bit value and we need to read * registers 2 and 3 to get the LSB and MSB values. */ i2c_reg.reg_num = PIC16F819_FAN_PERIOD_MSB_REGISTER; if (ioctl(fan_fd, I2C_GET_REG, &i2c_reg) == -1) { if (env_debug) envd_log(LOG_ERR, "Error in reading FAN_PERIOD MSB REGISTER\n"); return (-1); } dimm_fan_period = (i2c_reg.reg_value << 8); i2c_reg.reg_num = PIC16F819_FAN_PERIOD_LSB_REGISTER; if (ioctl(fan_fd, I2C_GET_REG, &i2c_reg) == -1) { if (env_debug) envd_log(LOG_ERR, "Error in reading FAN_PERIOD LSB REGISTER\n"); return (-1); } dimm_fan_period |= i2c_reg.reg_value; if (env_debug) envd_log(LOG_ERR, " dimm fan tach period is 0x%x\n", dimm_fan_period); if (dimm_fan_period == 0) { if (env_debug) envd_log(LOG_ERR, "dimm fan tach period read as zero. Illegal value.\n"); return (-1); } *fanspeedp = PIC16F819_FAN_TACH_TO_RPM(dimm_fan_period); return (0); } int is_dimm_fan_failed(void) { i2c_reg_t i2c_reg; fanspeed_t fan_speed; int retry_count; if (envd_dimm_fan.fd == -1) return (-1); /* * read register 1 to look at Fan fault bit. */ i2c_reg.reg_num = PIC16F819_STATUS_REGISTER; retry_count = MAX_RETRIES_FOR_PIC16F819_REG_READ; while (retry_count > 0) { if (ioctl(envd_dimm_fan.fd, I2C_GET_REG, &i2c_reg) == -1) { retry_count--; continue; } else break; } if (retry_count != MAX_RETRIES_FOR_PIC16F819_REG_READ) { if (env_debug) envd_log(LOG_ERR, "%d retries attempted in reading STATUS register.\n", (MAX_RETRIES_FOR_PIC16F819_REG_READ - retry_count)); } if (retry_count == 0) { (void) strncpy(dimm_fan_status_string, NOT_AVAILABLE, sizeof (dimm_fan_status_string)); (void) strncpy(dimm_fan_command_string, NOT_AVAILABLE, sizeof (dimm_fan_command_string)); (void) strncpy(dimm_fan_debug_string, NOT_AVAILABLE, sizeof (dimm_fan_debug_string)); (void) strncpy(dimm_fan_rpm_string, NOT_AVAILABLE, sizeof (dimm_fan_rpm_string)); return (-1); } if (env_debug) envd_log(LOG_ERR, "DIMM FAN STATUS reg = 0x%x\n", i2c_reg.reg_value); if (i2c_reg.reg_value & PIC16F819_FAN_FAILED) { (void) snprintf(dimm_fan_status_string, sizeof (dimm_fan_status_string), "0x%x", i2c_reg.reg_value); i2c_reg.reg_num = PIC16F819_DEBUG_REGISTER; if (ioctl(envd_dimm_fan.fd, I2C_GET_REG, &i2c_reg) == -1) { (void) strncpy(dimm_fan_debug_string, NOT_AVAILABLE, sizeof (dimm_fan_debug_string)); } else { (void) snprintf(dimm_fan_debug_string, sizeof (dimm_fan_debug_string), "0x%x", i2c_reg.reg_value); } i2c_reg.reg_num = PIC16F819_COMMAND_REGISTER; if (ioctl(envd_dimm_fan.fd, I2C_GET_REG, &i2c_reg) == -1) { (void) strncpy(dimm_fan_command_string, NOT_AVAILABLE, sizeof (dimm_fan_command_string)); } else { (void) snprintf(dimm_fan_command_string, sizeof (dimm_fan_command_string), "0x%x", i2c_reg.reg_value); } if (get_dimm_fan_speed(envd_dimm_fan.fd, &fan_speed) == -1) { (void) strncpy(dimm_fan_rpm_string, NOT_AVAILABLE, sizeof (dimm_fan_rpm_string)); } else { (void) snprintf(dimm_fan_rpm_string, sizeof (dimm_fan_rpm_string), "%d", fan_speed); } return (1); } else return (0); }