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 (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 22 /* 23 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 /* 28 * Facility node support for SES enclosures. We support the following facility 29 * nodes, based on the node type: 30 * 31 * bay 32 * indicator=ident 33 * indicator=fail 34 * indicator=ok2rm 35 * sensor=fault 36 * 37 * controller 38 * indicator=ident 39 * indicator=fail 40 * 41 * fan 42 * indicator=ident 43 * indicator=fail 44 * sensor=speed 45 * sensor=fault 46 * 47 * psu 48 * indicator=ident 49 * indicator=fail 50 * sensor=status 51 * 52 * ses-enclosure 53 * indicator=ident 54 * indicator=fail 55 * sensor=fault 56 * sensor=<name> (temperature) 57 * sensor=<name> (voltage) 58 * sensor=<name> (current) 59 * 60 * Most of these are handled by a single method that supports getting and 61 * setting boolean properties on the node. The fan speed sensor requires a 62 * special handler, while the analog enclosure sensors all have similar 63 * behavior and can be grouped together using a common method. 64 */ 65 66 #include "ses.h" 67 #include "disk.h" 68 69 #include <string.h> 70 71 static int ses_indicator_mode(topo_mod_t *, tnode_t *, topo_version_t, 72 nvlist_t *, nvlist_t **); 73 static int ses_sensor_reading(topo_mod_t *, tnode_t *, topo_version_t, 74 nvlist_t *, nvlist_t **); 75 static int ses_sensor_state(topo_mod_t *, tnode_t *, topo_version_t, 76 nvlist_t *, nvlist_t **); 77 static int ses_psu_state(topo_mod_t *, tnode_t *, topo_version_t, 78 nvlist_t *, nvlist_t **); 79 80 #define SES_SUPP_WARN_UNDER 0x01 81 #define SES_SUPP_WARN_OVER 0x02 82 #define SES_SUPP_CRIT_UNDER 0x04 83 #define SES_SUPP_CRIT_OVER 0x08 84 85 typedef struct ses_sensor_desc { 86 int sd_type; 87 int sd_units; 88 const char *sd_propname; 89 double sd_multiplier; 90 } ses_sensor_desc_t; 91 92 #define TOPO_METH_SES_MODE_VERSION 0 93 #define TOPO_METH_SES_READING_VERSION 0 94 #define TOPO_METH_SES_STATE_VERSION 0 95 #define TOPO_METH_SES_PSU_VERSION 0 96 97 #define TOPO_METH_SES_READING_PROP "propname" 98 #define TOPO_METH_SES_READING_MULT "multiplier" 99 100 #define TOPO_METH_SES_STATE_PROP "propname" 101 102 #define TOPO_METH_SES_MODE_PROP "property-name" 103 #define TOPO_METH_SES_MODE_ALTPROP "alternate-property" 104 105 static const topo_method_t ses_indicator_methods[] = { 106 { "ses_indicator_mode", TOPO_PROP_METH_DESC, 107 TOPO_METH_SES_MODE_VERSION, TOPO_STABILITY_INTERNAL, 108 ses_indicator_mode } 109 }; 110 111 static const topo_method_t ses_sensor_methods[] = { 112 { "ses_sensor_reading", TOPO_PROP_METH_DESC, 113 TOPO_METH_SES_READING_VERSION, TOPO_STABILITY_INTERNAL, 114 ses_sensor_reading }, 115 { "ses_sensor_state", TOPO_PROP_METH_DESC, 116 TOPO_METH_SES_STATE_VERSION, TOPO_STABILITY_INTERNAL, 117 ses_sensor_state }, 118 { "ses_psu_state", TOPO_PROP_METH_DESC, 119 TOPO_METH_SES_PSU_VERSION, TOPO_STABILITY_INTERNAL, 120 ses_psu_state }, 121 }; 122 123 /* 124 * Get or set an indicator. This method is invoked with arguments indicating 125 * the property to query to retrieve the value. Some elements (enclosures and 126 * devices) support a request property that is distinct from an array-detected 127 * property. Either of these conditions will result in the indicator being 128 * lit, so we have to check both properties. 129 */ 130 static int 131 ses_indicator_mode(topo_mod_t *mod, tnode_t *tn, topo_version_t vers, 132 nvlist_t *in, nvlist_t **out) 133 { 134 ses_node_t *np; 135 nvlist_t *args, *pargs, *props; 136 char *propname, *altprop; 137 uint32_t mode; 138 boolean_t current, altcurrent; 139 nvlist_t *nvl; 140 141 if (vers > TOPO_METH_SES_MODE_VERSION) 142 return (topo_mod_seterrno(mod, ETOPO_METHOD_VERNEW)); 143 144 if (nvlist_lookup_nvlist(in, TOPO_PROP_ARGS, &args) != 0 || 145 nvlist_lookup_string(args, TOPO_METH_SES_MODE_PROP, 146 &propname) != 0) { 147 topo_mod_dprintf(mod, "invalid arguments to 'mode' method\n"); 148 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); 149 } 150 151 if (nvlist_lookup_string(args, TOPO_METH_SES_MODE_ALTPROP, 152 &altprop) != 0) 153 altprop = NULL; 154 155 if ((np = ses_node_get(mod, tn)) == NULL) { 156 topo_mod_dprintf(mod, "failed to lookup ses node in 'mode' " 157 "method\n"); 158 return (-1); 159 } 160 verify((props = ses_node_props(np)) != NULL); 161 162 if (nvlist_lookup_nvlist(in, TOPO_PROP_PARGS, &pargs) == 0 && 163 nvlist_exists(pargs, TOPO_PROP_VAL_VAL)) { 164 /* set operation */ 165 if (nvlist_lookup_uint32(pargs, TOPO_PROP_VAL_VAL, 166 &mode) != 0) { 167 topo_mod_dprintf(mod, "invalid type for indicator " 168 "mode property"); 169 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); 170 } 171 172 if (mode != TOPO_LED_STATE_OFF && mode != TOPO_LED_STATE_ON) { 173 topo_mod_dprintf(mod, "invalid indicator mode %d\n", 174 mode); 175 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); 176 } 177 178 nvl = NULL; 179 if (topo_mod_nvalloc(mod, &nvl, NV_UNIQUE_NAME) != 0 || 180 nvlist_add_boolean_value(nvl, propname, 181 mode == TOPO_LED_STATE_ON ? B_TRUE : B_FALSE) != 0) { 182 nvlist_free(nvl); 183 return (topo_mod_seterrno(mod, EMOD_NOMEM)); 184 } 185 186 if (ses_node_ctl(np, SES_CTL_OP_SETPROP, nvl) != 0) { 187 topo_mod_dprintf(mod, "failed to set indicator: %s\n", 188 ses_errmsg()); 189 nvlist_free(nvl); 190 return (-1); 191 } 192 193 nvlist_free(nvl); 194 } else { 195 /* get operation */ 196 if (nvlist_lookup_boolean_value(props, 197 propname, ¤t) != 0) { 198 topo_mod_dprintf(mod, "failed to lookup %s in node " 199 "properties\n", propname); 200 return (topo_mod_seterrno(mod, EMOD_METHOD_NOTSUP)); 201 } 202 203 if (altprop != NULL && nvlist_lookup_boolean_value(props, 204 altprop, &altcurrent) == 0) 205 current |= altcurrent; 206 207 mode = current ? TOPO_LED_STATE_ON : TOPO_LED_STATE_OFF; 208 } 209 210 nvl = NULL; 211 if (topo_mod_nvalloc(mod, &nvl, NV_UNIQUE_NAME) != 0 || 212 nvlist_add_string(nvl, TOPO_PROP_VAL_NAME, 213 TOPO_LED_MODE) != 0 || 214 nvlist_add_uint32(nvl, TOPO_PROP_VAL_TYPE, TOPO_TYPE_UINT32) != 0 || 215 nvlist_add_uint32(nvl, TOPO_PROP_VAL_VAL, mode) != 0) { 216 nvlist_free(nvl); 217 return (topo_mod_seterrno(mod, EMOD_NOMEM)); 218 } 219 220 *out = nvl; 221 return (0); 222 } 223 224 /* 225 * Read the given sensor value. This just looks up the value in the node 226 * properties, and multiplies by a fixed value (determined when the method is 227 * instantiated). 228 */ 229 static int 230 ses_sensor_reading(topo_mod_t *mod, tnode_t *tn, topo_version_t vers, 231 nvlist_t *in, nvlist_t **out) 232 { 233 ses_node_t *np; 234 nvlist_t *args, *props; 235 char *prop; 236 double raw, multiplier; 237 uint64_t current; 238 int64_t scurrent; 239 nvlist_t *nvl; 240 241 if (vers > TOPO_METH_SES_MODE_VERSION) 242 return (topo_mod_seterrno(mod, ETOPO_METHOD_VERNEW)); 243 244 if (nvlist_lookup_nvlist(in, TOPO_PROP_ARGS, &args) != 0 || 245 nvlist_lookup_string(args, TOPO_METH_SES_READING_PROP, 246 &prop) != 0) { 247 topo_mod_dprintf(mod, 248 "invalid arguments to 'reading' method\n"); 249 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); 250 } 251 252 if (nvlist_lookup_double(args, TOPO_METH_SES_READING_MULT, 253 &multiplier) != 0) 254 multiplier = 1; 255 256 if ((np = ses_node_get(mod, tn)) == NULL) { 257 topo_mod_dprintf(mod, "failed to lookup ses node in 'mode' " 258 "method\n"); 259 return (-1); 260 } 261 verify((props = ses_node_props(np)) != NULL); 262 263 if (nvlist_lookup_uint64(props, prop, ¤t) == 0) { 264 raw = (double)current; 265 } else if (nvlist_lookup_int64(props, prop, &scurrent) == 0) { 266 raw = (double)scurrent; 267 } else { 268 topo_mod_dprintf(mod, "failed to lookup %s in node " 269 "properties\n", prop); 270 return (topo_mod_seterrno(mod, EMOD_METHOD_NOTSUP)); 271 } 272 273 nvl = NULL; 274 if (topo_mod_nvalloc(mod, &nvl, NV_UNIQUE_NAME) != 0 || 275 nvlist_add_string(nvl, TOPO_PROP_VAL_NAME, 276 TOPO_SENSOR_READING) != 0 || 277 nvlist_add_uint32(nvl, TOPO_PROP_VAL_TYPE, TOPO_TYPE_DOUBLE) != 0 || 278 nvlist_add_double(nvl, TOPO_PROP_VAL_VAL, raw * multiplier) != 0) { 279 nvlist_free(nvl); 280 return (topo_mod_seterrno(mod, EMOD_NOMEM)); 281 } 282 283 *out = nvl; 284 return (0); 285 } 286 287 /* 288 * Returns the current sensor state. This can be invoked for one of two 289 * different types of sensors: threshold or discrete sensors. For discrete 290 * sensors, we expect a name of a boolean property and indicate 291 * asserted/deasserted based on that. For threshold sensors, we check for the 292 * standard warning/critical properties and translate that into the appropriate 293 * topo state. 294 */ 295 /*ARGSUSED*/ 296 static int 297 ses_sensor_state(topo_mod_t *mod, tnode_t *tn, topo_version_t vers, 298 nvlist_t *in, nvlist_t **out) 299 { 300 nvlist_t *nvl, *args, *props; 301 boolean_t value, asserted; 302 uint64_t status; 303 uint32_t state; 304 ses_node_t *np; 305 char *prop; 306 307 if ((np = ses_node_get(mod, tn)) == NULL) { 308 topo_mod_dprintf(mod, "failed to lookup ses node in 'mode' " 309 "method\n"); 310 return (-1); 311 } 312 verify((props = ses_node_props(np)) != NULL); 313 314 if (nvlist_lookup_nvlist(in, TOPO_PROP_ARGS, &args) != 0) { 315 topo_mod_dprintf(mod, 316 "invalid arguments to 'state' method\n"); 317 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); 318 } 319 320 if (nvlist_lookup_uint64(props, SES_PROP_STATUS_CODE, &status) != 0) 321 status = SES_ESC_UNSUPPORTED; 322 323 state = 0; 324 if (nvlist_lookup_string(args, TOPO_METH_SES_STATE_PROP, 325 &prop) == 0) { 326 /* discrete (fault) sensor */ 327 328 asserted = B_FALSE; 329 if (nvlist_lookup_boolean_value(props, prop, &value) == 0 && 330 value) 331 asserted = B_TRUE; 332 if (status == SES_ESC_UNRECOVERABLE || 333 status == SES_ESC_CRITICAL) 334 asserted = B_TRUE; 335 336 if (asserted) 337 state |= TOPO_SENSOR_STATE_GENERIC_STATE_ASSERTED; 338 else 339 state |= TOPO_SENSOR_STATE_GENERIC_STATE_DEASSERTED; 340 } else { 341 /* threshold sensor */ 342 if (nvlist_lookup_boolean_value(props, 343 SES_PROP_WARN_UNDER, &value) == 0 && value) 344 state |= TOPO_SENSOR_STATE_THRESH_LOWER_NONCRIT; 345 if (nvlist_lookup_boolean_value(props, 346 SES_PROP_WARN_OVER, &value) == 0 && value) 347 state |= TOPO_SENSOR_STATE_THRESH_UPPER_NONCRIT; 348 if (nvlist_lookup_boolean_value(props, 349 SES_PROP_CRIT_UNDER, &value) == 0 && value) 350 state |= TOPO_SENSOR_STATE_THRESH_LOWER_CRIT; 351 if (nvlist_lookup_boolean_value(props, 352 SES_PROP_CRIT_OVER, &value) == 0 && value) 353 state |= TOPO_SENSOR_STATE_THRESH_UPPER_CRIT; 354 } 355 356 nvl = NULL; 357 if (topo_mod_nvalloc(mod, &nvl, NV_UNIQUE_NAME) != 0 || 358 nvlist_add_string(nvl, TOPO_PROP_VAL_NAME, 359 TOPO_SENSOR_STATE) != 0 || 360 nvlist_add_uint32(nvl, TOPO_PROP_VAL_TYPE, TOPO_TYPE_UINT32) != 0 || 361 nvlist_add_uint32(nvl, TOPO_PROP_VAL_VAL, state) != 0) { 362 nvlist_free(nvl); 363 return (topo_mod_seterrno(mod, EMOD_NOMEM)); 364 } 365 366 *out = nvl; 367 return (0); 368 } 369 370 /* 371 * Read the status of a PSU. This is such a specialized operation that it has 372 * its own method instead of trying to piggyback on ses_sensor_state(). We 373 * use the following mapping to get to the standard topo power supply states: 374 * 375 * acfail -> INPUT_LOST 376 * dcfail -> INPUT_LOST 377 * undervoltage -> INPUT_RANGE 378 * overvoltage -> INPUT_RANGE_PRES 379 * overcurrent -> INPUT_RANGE_PRES 380 * overtemp -> (none) 381 * 382 * If we ever have a need for reading overtemp, we can expand the topo 383 * representation for power supplies, but at the moment this seems unnecessary. 384 */ 385 /*ARGSUSED*/ 386 static int 387 ses_psu_state(topo_mod_t *mod, tnode_t *tn, topo_version_t vers, 388 nvlist_t *in, nvlist_t **out) 389 { 390 nvlist_t *nvl, *props; 391 boolean_t value; 392 uint32_t state; 393 ses_node_t *np; 394 395 if ((np = ses_node_get(mod, tn)) == NULL) { 396 topo_mod_dprintf(mod, "failed to lookup ses node in 'mode' " 397 "method\n"); 398 return (-1); 399 } 400 verify((props = ses_node_props(np)) != NULL); 401 402 state = 0; 403 if ((nvlist_lookup_boolean_value(props, SES_PSU_PROP_DC_FAIL, 404 &value) == 0 && value) || 405 (nvlist_lookup_boolean_value(props, SES_PSU_PROP_AC_FAIL, 406 &value) == 0 && value)) 407 state |= TOPO_SENSOR_STATE_POWER_SUPPLY_INPUT_LOST; 408 409 if (nvlist_lookup_boolean_value(props, SES_PSU_PROP_DC_UNDER_VOLTAGE, 410 &value) == 0 && value) 411 state |= TOPO_SENSOR_STATE_POWER_SUPPLY_INPUT_RANGE; 412 413 if ((nvlist_lookup_boolean_value(props, SES_PSU_PROP_DC_OVER_VOLTAGE, 414 &value) == 0 && value) || 415 (nvlist_lookup_boolean_value(props, SES_PSU_PROP_DC_OVER_CURRENT, 416 &value) == 0 && value)) 417 state |= TOPO_SENSOR_STATE_POWER_SUPPLY_INPUT_RANGE_PRES; 418 419 nvl = NULL; 420 if (topo_mod_nvalloc(mod, &nvl, NV_UNIQUE_NAME) != 0 || 421 nvlist_add_string(nvl, TOPO_PROP_VAL_NAME, 422 TOPO_SENSOR_STATE) != 0 || 423 nvlist_add_uint32(nvl, TOPO_PROP_VAL_TYPE, TOPO_TYPE_UINT32) != 0 || 424 nvlist_add_uint32(nvl, TOPO_PROP_VAL_VAL, state) != 0) { 425 nvlist_free(nvl); 426 return (topo_mod_seterrno(mod, EMOD_NOMEM)); 427 } 428 429 *out = nvl; 430 return (0); 431 } 432 433 /* 434 * Create a facility node, either a sensor or an indicator. 435 */ 436 static tnode_t * 437 ses_add_fac_common(topo_mod_t *mod, tnode_t *pnode, const char *name, 438 const char *type, uint64_t nodeid) 439 { 440 tnode_t *tn; 441 topo_pgroup_info_t pgi; 442 int err; 443 ses_enum_target_t *stp = topo_node_getspecific(pnode); 444 445 if ((tn = topo_node_facbind(mod, pnode, name, type)) == NULL) { 446 topo_mod_dprintf(mod, "failed to bind facility node %s\n", 447 name); 448 return (NULL); 449 } 450 451 stp->set_refcount++; 452 topo_node_setspecific(tn, stp); 453 454 pgi.tpi_name = TOPO_PGROUP_FACILITY; 455 pgi.tpi_namestab = TOPO_STABILITY_PRIVATE; 456 pgi.tpi_datastab = TOPO_STABILITY_PRIVATE; 457 pgi.tpi_version = 1; 458 459 if (topo_pgroup_create(tn, &pgi, &err) != 0) { 460 topo_mod_dprintf(mod, "failed to create facility property " 461 "group: %s\n", topo_strerror(err)); 462 topo_node_unbind(tn); 463 return (NULL); 464 } 465 466 /* 467 * We need the node-id property for each facility node. 468 */ 469 pgi.tpi_name = TOPO_PGROUP_SES; 470 pgi.tpi_namestab = TOPO_STABILITY_PRIVATE; 471 pgi.tpi_datastab = TOPO_STABILITY_PRIVATE; 472 pgi.tpi_version = TOPO_VERSION; 473 474 if (topo_pgroup_create(tn, &pgi, &err) != 0) { 475 topo_mod_dprintf(mod, "failed to create ses property " 476 "group: %s\n", topo_strerror(err)); 477 topo_node_unbind(tn); 478 return (NULL); 479 } 480 481 if (topo_prop_set_uint64(tn, TOPO_PGROUP_SES, 482 TOPO_PROP_NODE_ID, TOPO_PROP_IMMUTABLE, 483 nodeid, &err) != 0) { 484 topo_mod_dprintf(mod, 485 "failed to create property %s: %s\n", 486 TOPO_PROP_NODE_ID, topo_strerror(err)); 487 topo_node_unbind(tn); 488 return (NULL); 489 } 490 491 return (tn); 492 } 493 494 /* 495 * Add an indicator. This can be represented by a single property, or by the 496 * union of two elements when SES is capable of distinguishing between 497 * requested failure and detected failure. 498 */ 499 static int 500 ses_add_indicator(topo_mod_t *mod, tnode_t *pnode, uint64_t nodeid, 501 int type, const char *name, const char *propname, const char *altprop) 502 { 503 tnode_t *tn; 504 int err; 505 nvlist_t *nvl; 506 507 /* create facility node and add methods */ 508 if ((tn = ses_add_fac_common(mod, pnode, name, 509 TOPO_FAC_TYPE_INDICATOR, nodeid)) == NULL) 510 return (-1); 511 512 if (topo_method_register(mod, tn, ses_indicator_methods) < 0) { 513 topo_mod_dprintf(mod, "failed to register facility methods\n"); 514 topo_node_unbind(tn); 515 return (-1); 516 } 517 518 /* set standard properties */ 519 if (topo_prop_set_uint32(tn, TOPO_PGROUP_FACILITY, 520 TOPO_FACILITY_TYPE, TOPO_PROP_IMMUTABLE, type, &err) != 0) { 521 topo_mod_dprintf(mod, 522 "failed to set facility node properties: %s\n", 523 topo_strerror(err)); 524 topo_node_unbind(tn); 525 return (-1); 526 } 527 528 /* 'mode' property */ 529 nvl = NULL; 530 if (topo_mod_nvalloc(mod, &nvl, NV_UNIQUE_NAME) != 0 || 531 nvlist_add_string(nvl, TOPO_METH_SES_MODE_PROP, 532 propname) != 0 || 533 (altprop != NULL && nvlist_add_string(nvl, 534 TOPO_METH_SES_MODE_ALTPROP, altprop) != 0)) { 535 nvlist_free(nvl); 536 topo_mod_dprintf(mod, "failed to setup method arguments\n"); 537 topo_node_unbind(tn); 538 return (topo_mod_seterrno(mod, EMOD_NOMEM)); 539 } 540 541 if (topo_prop_method_register(tn, TOPO_PGROUP_FACILITY, 542 TOPO_LED_MODE, TOPO_TYPE_UINT32, "ses_indicator_mode", 543 nvl, &err) != 0) { 544 nvlist_free(nvl); 545 topo_mod_dprintf(mod, "failed to register reading method: %s\n", 546 topo_strerror(err)); 547 return (-1); 548 } 549 550 if (topo_prop_setmutable(tn, TOPO_PGROUP_FACILITY, 551 TOPO_LED_MODE, &err) != 0) { 552 nvlist_free(nvl); 553 topo_mod_dprintf(mod, "failed to set property as mutable: %s\n", 554 topo_strerror(err)); 555 return (-1); 556 } 557 558 nvlist_free(nvl); 559 return (0); 560 } 561 562 static tnode_t * 563 ses_add_sensor_common(topo_mod_t *mod, tnode_t *pnode, uint64_t nodeid, 564 const char *name, const char *class, int type) 565 { 566 tnode_t *tn; 567 int err; 568 569 /* create facility node and add methods */ 570 if ((tn = ses_add_fac_common(mod, pnode, name, 571 TOPO_FAC_TYPE_SENSOR, nodeid)) == NULL) 572 return (NULL); 573 574 if (topo_method_register(mod, tn, ses_sensor_methods) < 0) { 575 topo_mod_dprintf(mod, "failed to register facility methods\n"); 576 topo_node_unbind(tn); 577 return (NULL); 578 } 579 580 /* set standard properties */ 581 if (topo_prop_set_string(tn, TOPO_PGROUP_FACILITY, 582 TOPO_SENSOR_CLASS, TOPO_PROP_IMMUTABLE, 583 class, &err) != 0 || 584 topo_prop_set_uint32(tn, TOPO_PGROUP_FACILITY, 585 TOPO_FACILITY_TYPE, TOPO_PROP_IMMUTABLE, 586 type, &err) != 0) { 587 topo_mod_dprintf(mod, 588 "failed to set facility node properties: %s\n", 589 topo_strerror(err)); 590 topo_node_unbind(tn); 591 return (NULL); 592 } 593 594 return (tn); 595 } 596 597 /* 598 * Add an analog (threshold) sensor to the enclosure. This is used for fan 599 * speed, voltage, current, and temperature sensors. 600 */ 601 static int 602 ses_add_sensor(topo_mod_t *mod, tnode_t *pnode, uint64_t nodeid, 603 const char *name, const ses_sensor_desc_t *sdp) 604 { 605 tnode_t *tn; 606 int err; 607 nvlist_t *nvl; 608 609 if ((tn = ses_add_sensor_common(mod, pnode, nodeid, name, 610 TOPO_SENSOR_CLASS_THRESHOLD, sdp->sd_type)) == NULL) 611 return (-1); 612 613 if (topo_prop_set_uint32(tn, TOPO_PGROUP_FACILITY, 614 TOPO_SENSOR_UNITS, TOPO_PROP_IMMUTABLE, sdp->sd_units, &err) != 0) { 615 topo_mod_dprintf(mod, 616 "failed to set facility node properties: %s\n", 617 topo_strerror(err)); 618 topo_node_unbind(tn); 619 return (-1); 620 } 621 622 /* 'reading' property */ 623 nvl = NULL; 624 if (topo_mod_nvalloc(mod, &nvl, NV_UNIQUE_NAME) != 0 || 625 nvlist_add_string(nvl, TOPO_METH_SES_READING_PROP, 626 sdp->sd_propname) != 0 || 627 (sdp->sd_multiplier != 0 && 628 nvlist_add_double(nvl, TOPO_METH_SES_READING_MULT, 629 sdp->sd_multiplier) != 0)) { 630 nvlist_free(nvl); 631 topo_mod_dprintf(mod, "failed to setup method arguments\n"); 632 topo_node_unbind(tn); 633 return (-1); 634 } 635 636 if (topo_prop_method_register(tn, TOPO_PGROUP_FACILITY, 637 TOPO_SENSOR_READING, TOPO_TYPE_DOUBLE, "ses_sensor_reading", 638 nvl, &err) != 0) { 639 nvlist_free(nvl); 640 topo_mod_dprintf(mod, "failed to register reading method: %s\n", 641 topo_strerror(err)); 642 return (-1); 643 } 644 645 nvlist_free(nvl); 646 if (topo_mod_nvalloc(mod, &nvl, NV_UNIQUE_NAME) != 0) { 647 topo_mod_dprintf(mod, "failed to setup method arguments\n"); 648 topo_node_unbind(tn); 649 return (-1); 650 } 651 652 /* 'state' property */ 653 if (topo_prop_method_register(tn, TOPO_PGROUP_FACILITY, 654 TOPO_SENSOR_STATE, TOPO_TYPE_UINT32, "ses_sensor_state", 655 nvl, &err) != 0) { 656 nvlist_free(nvl); 657 topo_mod_dprintf(mod, "failed to register state method: %s\n", 658 topo_strerror(err)); 659 return (-1); 660 } 661 662 nvlist_free(nvl); 663 return (0); 664 } 665 666 /* 667 * Add a discrete sensor for simple boolean values. This is used to indicate 668 * externally-detected failures for fans, bays, and enclosures. 669 */ 670 static int 671 ses_add_discrete(topo_mod_t *mod, tnode_t *pnode, uint64_t nodeid, 672 const char *name, const char *prop) 673 { 674 tnode_t *tn; 675 int err; 676 nvlist_t *nvl; 677 678 if ((tn = ses_add_sensor_common(mod, pnode, nodeid, name, 679 TOPO_SENSOR_CLASS_DISCRETE, 680 TOPO_SENSOR_TYPE_GENERIC_STATE)) == NULL) 681 return (-1); 682 683 nvl = NULL; 684 if (topo_mod_nvalloc(mod, &nvl, NV_UNIQUE_NAME) != 0 || 685 nvlist_add_string(nvl, TOPO_METH_SES_STATE_PROP, prop) != 0) { 686 nvlist_free(nvl); 687 topo_mod_dprintf(mod, "failed to setup method arguments\n"); 688 topo_node_unbind(tn); 689 return (-1); 690 } 691 692 /* 'state' property */ 693 if (topo_prop_method_register(tn, TOPO_PGROUP_FACILITY, 694 TOPO_SENSOR_STATE, TOPO_TYPE_UINT32, "ses_sensor_state", 695 nvl, &err) != 0) { 696 nvlist_free(nvl); 697 topo_mod_dprintf(mod, "failed to register state method: %s\n", 698 topo_strerror(err)); 699 return (-1); 700 } 701 702 nvlist_free(nvl); 703 return (0); 704 } 705 706 /*ARGSUSED*/ 707 static int 708 ses_add_psu_status(topo_mod_t *mod, tnode_t *pnode, uint64_t nodeid) 709 { 710 tnode_t *tn; 711 int err; 712 nvlist_t *nvl; 713 714 /* create facility node and add methods */ 715 if ((tn = ses_add_sensor_common(mod, pnode, nodeid, "status", 716 TOPO_SENSOR_CLASS_DISCRETE, 717 TOPO_SENSOR_TYPE_POWER_SUPPLY)) == NULL) 718 return (-1); 719 720 if (topo_mod_nvalloc(mod, &nvl, NV_UNIQUE_NAME) != 0) { 721 nvlist_free(nvl); 722 topo_mod_dprintf(mod, "failed to setup method arguments\n"); 723 topo_node_unbind(tn); 724 return (-1); 725 } 726 727 /* 'state' property */ 728 if (topo_prop_method_register(tn, TOPO_PGROUP_FACILITY, 729 TOPO_SENSOR_STATE, TOPO_TYPE_UINT32, "ses_psu_state", 730 nvl, &err) != 0) { 731 nvlist_free(nvl); 732 topo_mod_dprintf(mod, "failed to register state method: %s\n", 733 topo_strerror(err)); 734 return (-1); 735 } 736 737 nvlist_free(nvl); 738 return (0); 739 } 740 741 /*ARGSUSED*/ 742 int 743 ses_node_enum_facility(topo_mod_t *mod, tnode_t *tn, topo_version_t vers, 744 nvlist_t *in, nvlist_t **out) 745 { 746 ses_node_t *np; 747 nvlist_t *props; 748 uint64_t type, nodeid; 749 ses_sensor_desc_t sd = { 0 }; 750 751 if ((np = ses_node_get(mod, tn)) == NULL) 752 return (-1); 753 754 assert(ses_node_type(np) == SES_NODE_ELEMENT); 755 nodeid = ses_node_id(np); 756 verify((props = ses_node_props(np)) != NULL); 757 verify(nvlist_lookup_uint64(props, SES_PROP_ELEMENT_TYPE, &type) == 0); 758 759 if (type != SES_ET_DEVICE && type != SES_ET_ARRAY_DEVICE && 760 type != SES_ET_COOLING && type != SES_ET_POWER_SUPPLY) 761 return (0); 762 763 /* 764 * Every element supports an 'ident' indicator. All elements also 765 * support a 'fail' indicator, but the properties used to represent 766 * this condition differs between elements. 767 */ 768 if (ses_add_indicator(mod, tn, nodeid, TOPO_LED_TYPE_LOCATE, "ident", 769 SES_PROP_IDENT, NULL) != 0) 770 return (-1); 771 772 switch (type) { 773 case SES_ET_DEVICE: 774 case SES_ET_ARRAY_DEVICE: 775 /* 776 * Disks support an additional 'ok2rm' indicator, as well as 777 * externally detected 'fail' sensor. 778 */ 779 if (ses_add_indicator(mod, tn, nodeid, TOPO_LED_TYPE_SERVICE, 780 "fail", SES_DEV_PROP_FAULT_RQSTD, 781 SES_DEV_PROP_FAULT_SENSED) != 0 || 782 ses_add_indicator(mod, tn, nodeid, TOPO_LED_TYPE_OK2RM, 783 "ok2rm", SES_PROP_RMV, SES_PROP_RMV) != 0 || 784 ses_add_discrete(mod, tn, nodeid, "fault", 785 SES_DEV_PROP_FAULT_SENSED) != 0) 786 return (-1); 787 break; 788 789 case SES_ET_COOLING: 790 /* 791 * Add the fan speed sensor, and a discrete sensor for 792 * detecting failure. 793 */ 794 sd.sd_type = TOPO_SENSOR_TYPE_THRESHOLD_STATE; 795 sd.sd_units = TOPO_SENSOR_UNITS_RPM; 796 sd.sd_propname = SES_COOLING_PROP_FAN_SPEED; 797 if (ses_add_indicator(mod, tn, nodeid, TOPO_LED_TYPE_SERVICE, 798 "fail", SES_PROP_FAIL, NULL) != 0 || 799 ses_add_sensor(mod, tn, nodeid, "speed", &sd) != 0 || 800 ses_add_discrete(mod, tn, nodeid, "fault", 801 SES_PROP_OFF) != 0) 802 return (-1); 803 break; 804 805 case SES_ET_POWER_SUPPLY: 806 /* 807 * For power supplies, we have a number of different sensors: 808 * acfail, dcfail, overtemp, undervoltate, overvoltage, 809 * and overcurrent. Rather than expose these all as individual 810 * sensors, we lump them together into a 'status' sensor of 811 * type TOPO_SENSOR_TYPE_POWER_SUPPLY and export the 812 * appropriate status flags as defined by the libtopo standard. 813 */ 814 if (ses_add_indicator(mod, tn, nodeid, TOPO_LED_TYPE_SERVICE, 815 "fail", SES_PROP_FAIL, NULL) != 0) 816 return (-1); 817 818 if (ses_add_psu_status(mod, tn, nodeid) != 0) 819 return (-1); 820 break; 821 822 default: 823 return (0); 824 } 825 826 return (0); 827 } 828 829 /* 830 * Add enclosure-wide sensors (temperature, voltage, and current) beneath the 831 * given aggregate. 832 */ 833 static int 834 ses_add_enclosure_sensors(topo_mod_t *mod, tnode_t *tn, ses_node_t *agg, 835 uint64_t type) 836 { 837 ses_node_t *child; 838 const char *defaultname; 839 char *desc, *name; 840 char rawname[64]; 841 nvlist_t *props, *aprops; 842 uint64_t index, nodeid; 843 ses_sensor_desc_t sd = { 0 }; 844 size_t len; 845 846 switch (type) { 847 case SES_ET_TEMPERATURE_SENSOR: 848 sd.sd_type = TOPO_SENSOR_TYPE_TEMP; 849 sd.sd_units = TOPO_SENSOR_UNITS_DEGREES_C; 850 sd.sd_propname = SES_TEMP_PROP_TEMP; 851 defaultname = "temperature"; 852 break; 853 854 case SES_ET_VOLTAGE_SENSOR: 855 sd.sd_type = TOPO_SENSOR_TYPE_VOLTAGE; 856 sd.sd_units = TOPO_SENSOR_UNITS_VOLTS; 857 sd.sd_propname = SES_VS_PROP_VOLTAGE_MV; 858 sd.sd_multiplier = 0.001; 859 defaultname = "voltage"; 860 break; 861 862 case SES_ET_CURRENT_SENSOR: 863 sd.sd_type = TOPO_SENSOR_TYPE_CURRENT; 864 sd.sd_units = TOPO_SENSOR_UNITS_AMPS; 865 sd.sd_propname = SES_CS_PROP_CURRENT_MA; 866 sd.sd_multiplier = 0.001; 867 defaultname = "current"; 868 break; 869 870 default: 871 return (0); 872 } 873 874 aprops = ses_node_props(agg); 875 876 for (child = ses_node_child(agg); child != NULL; 877 child = ses_node_sibling(child)) { 878 /* 879 * The only tricky part here is getting the name for the 880 * sensor, where we follow the algorithm of the standard 881 * elements. 882 */ 883 props = ses_node_props(child); 884 nodeid = ses_node_id(child); 885 if (nvlist_lookup_uint64(props, SES_PROP_ELEMENT_CLASS_INDEX, 886 &index) != 0) 887 continue; 888 889 if (nvlist_lookup_string(props, SES_PROP_DESCRIPTION, 890 &desc) == 0 && desc[0] != '\0') { 891 (void) strlcpy(rawname, desc, sizeof (rawname)); 892 } else { 893 if (nvlist_lookup_string(aprops, 894 SES_PROP_CLASS_DESCRIPTION, &desc) != 0 || 895 desc[0] == '\0') 896 desc = (char *)defaultname; 897 898 len = strlen(desc); 899 while (len > 0 && desc[len - 1] == ' ') 900 len--; 901 902 (void) snprintf(rawname, sizeof (rawname), 903 "%.*s %llu", len, desc, index); 904 } 905 906 if ((name = disk_auth_clean(mod, rawname)) == NULL) 907 return (-1); 908 909 if (ses_add_sensor(mod, tn, nodeid, name, &sd) != 0) { 910 topo_mod_strfree(mod, name); 911 return (-1); 912 } 913 914 topo_mod_strfree(mod, name); 915 } 916 917 return (0); 918 } 919 920 /*ARGSUSED*/ 921 int 922 ses_enc_enum_facility(topo_mod_t *mod, tnode_t *tn, topo_version_t vers, 923 nvlist_t *in, nvlist_t **out) 924 { 925 ses_node_t *np, *agg; 926 nvlist_t *aprops; 927 uint64_t type, nodeid; 928 929 if ((np = ses_node_get(mod, tn)) == NULL) 930 return (-1); 931 932 assert(ses_node_type(np) == SES_NODE_ENCLOSURE); 933 nodeid = ses_node_id(np); 934 935 /* 936 * 'ident' and 'fail' LEDs, and 'fault' sensor. 937 */ 938 if (ses_add_indicator(mod, tn, nodeid, TOPO_LED_TYPE_LOCATE, "ident", 939 SES_PROP_IDENT, NULL) != 0 || 940 ses_add_indicator(mod, tn, nodeid, TOPO_LED_TYPE_SERVICE, "fail", 941 SES_PROP_FAIL_REQ, SES_PROP_FAIL) != 0 || 942 ses_add_discrete(mod, tn, nodeid, "fault", SES_PROP_FAIL) != 0) 943 return (-1); 944 945 /* 946 * Environmental sensors (temperature, voltage, current). We have no 947 * way of knowing if any of these sensors correspond to a particular 948 * element, so we just attach them to the enclosure as a whole. In the 949 * future, some vendor-specific libses plugin knowledge could let us 950 * make this correlation clearer. 951 */ 952 for (agg = ses_node_child(np); agg != NULL; 953 agg = ses_node_sibling(agg)) { 954 if (ses_node_type(agg) != SES_NODE_AGGREGATE) 955 continue; 956 957 verify((aprops = ses_node_props(agg)) != NULL); 958 if (nvlist_lookup_uint64(aprops, SES_PROP_ELEMENT_TYPE, 959 &type) != 0) 960 continue; 961 962 if (ses_add_enclosure_sensors(mod, tn, agg, type) != 0) 963 return (-1); 964 } 965 966 return (0); 967 } 968