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 |= 345 TOPO_SENSOR_THRESH_LOWER_NONCRIT_GOING_HIGH; 346 if (nvlist_lookup_boolean_value(props, 347 SES_PROP_WARN_OVER, &value) == 0 && value) 348 state |= 349 TOPO_SENSOR_THRESH_UPPER_NONCRIT_GOING_HIGH; 350 if (nvlist_lookup_boolean_value(props, 351 SES_PROP_CRIT_UNDER, &value) == 0 && value) 352 state |= 353 TOPO_SENSOR_THRESH_LOWER_CRIT_GOING_HIGH; 354 if (nvlist_lookup_boolean_value(props, 355 SES_PROP_CRIT_OVER, &value) == 0 && value) 356 state |= 357 TOPO_SENSOR_THRESH_UPPER_CRIT_GOING_HIGH; 358 } 359 360 nvl = NULL; 361 if (topo_mod_nvalloc(mod, &nvl, NV_UNIQUE_NAME) != 0 || 362 nvlist_add_string(nvl, TOPO_PROP_VAL_NAME, 363 TOPO_SENSOR_STATE) != 0 || 364 nvlist_add_uint32(nvl, TOPO_PROP_VAL_TYPE, TOPO_TYPE_UINT32) != 0 || 365 nvlist_add_uint32(nvl, TOPO_PROP_VAL_VAL, state) != 0) { 366 nvlist_free(nvl); 367 return (topo_mod_seterrno(mod, EMOD_NOMEM)); 368 } 369 370 *out = nvl; 371 return (0); 372 } 373 374 /* 375 * Read the status of a PSU. This is such a specialized operation that it has 376 * its own method instead of trying to piggyback on ses_sensor_state(). We 377 * use the following mapping to get to the standard topo power supply states: 378 * 379 * acfail -> INPUT_LOST 380 * dcfail -> INPUT_LOST 381 * undervoltage -> INPUT_RANGE 382 * overvoltage -> INPUT_RANGE_PRES 383 * overcurrent -> INPUT_RANGE_PRES 384 * overtemp -> (none) 385 * 386 * If we ever have a need for reading overtemp, we can expand the topo 387 * representation for power supplies, but at the moment this seems unnecessary. 388 */ 389 /*ARGSUSED*/ 390 static int 391 ses_psu_state(topo_mod_t *mod, tnode_t *tn, topo_version_t vers, 392 nvlist_t *in, nvlist_t **out) 393 { 394 nvlist_t *nvl, *props; 395 boolean_t value; 396 uint32_t state; 397 ses_node_t *np; 398 399 if ((np = ses_node_get(mod, tn)) == NULL) { 400 topo_mod_dprintf(mod, "failed to lookup ses node in 'mode' " 401 "method\n"); 402 return (-1); 403 } 404 verify((props = ses_node_props(np)) != NULL); 405 406 state = 0; 407 if ((nvlist_lookup_boolean_value(props, SES_PSU_PROP_DC_FAIL, 408 &value) == 0 && value) || 409 (nvlist_lookup_boolean_value(props, SES_PSU_PROP_AC_FAIL, 410 &value) == 0 && value)) 411 state |= TOPO_SENSOR_STATE_POWER_SUPPLY_INPUT_LOST; 412 413 if (nvlist_lookup_boolean_value(props, SES_PSU_PROP_DC_UNDER_VOLTAGE, 414 &value) == 0 && value) 415 state |= TOPO_SENSOR_STATE_POWER_SUPPLY_INPUT_RANGE; 416 417 if ((nvlist_lookup_boolean_value(props, SES_PSU_PROP_DC_OVER_VOLTAGE, 418 &value) == 0 && value) || 419 (nvlist_lookup_boolean_value(props, SES_PSU_PROP_DC_OVER_CURRENT, 420 &value) == 0 && value)) 421 state |= TOPO_SENSOR_STATE_POWER_SUPPLY_INPUT_RANGE_PRES; 422 423 nvl = NULL; 424 if (topo_mod_nvalloc(mod, &nvl, NV_UNIQUE_NAME) != 0 || 425 nvlist_add_string(nvl, TOPO_PROP_VAL_NAME, 426 TOPO_SENSOR_STATE) != 0 || 427 nvlist_add_uint32(nvl, TOPO_PROP_VAL_TYPE, TOPO_TYPE_UINT32) != 0 || 428 nvlist_add_uint32(nvl, TOPO_PROP_VAL_VAL, state) != 0) { 429 nvlist_free(nvl); 430 return (topo_mod_seterrno(mod, EMOD_NOMEM)); 431 } 432 433 *out = nvl; 434 return (0); 435 } 436 437 /* 438 * Create a facility node, either a sensor or an indicator. 439 */ 440 static tnode_t * 441 ses_add_fac_common(topo_mod_t *mod, tnode_t *pnode, const char *name, 442 const char *type, uint64_t nodeid) 443 { 444 tnode_t *tn; 445 topo_pgroup_info_t pgi; 446 int err; 447 ses_enum_target_t *stp = topo_node_getspecific(pnode); 448 449 if ((tn = topo_node_facbind(mod, pnode, name, type)) == NULL) { 450 topo_mod_dprintf(mod, "failed to bind facility node %s\n", 451 name); 452 return (NULL); 453 } 454 455 stp->set_refcount++; 456 topo_node_setspecific(tn, stp); 457 458 pgi.tpi_name = TOPO_PGROUP_FACILITY; 459 pgi.tpi_namestab = TOPO_STABILITY_PRIVATE; 460 pgi.tpi_datastab = TOPO_STABILITY_PRIVATE; 461 pgi.tpi_version = 1; 462 463 if (topo_pgroup_create(tn, &pgi, &err) != 0) { 464 topo_mod_dprintf(mod, "failed to create facility property " 465 "group: %s\n", topo_strerror(err)); 466 topo_node_unbind(tn); 467 return (NULL); 468 } 469 470 /* 471 * We need the node-id property for each facility node. 472 */ 473 pgi.tpi_name = TOPO_PGROUP_SES; 474 pgi.tpi_namestab = TOPO_STABILITY_PRIVATE; 475 pgi.tpi_datastab = TOPO_STABILITY_PRIVATE; 476 pgi.tpi_version = TOPO_VERSION; 477 478 if (topo_pgroup_create(tn, &pgi, &err) != 0) { 479 topo_mod_dprintf(mod, "failed to create ses property " 480 "group: %s\n", topo_strerror(err)); 481 topo_node_unbind(tn); 482 return (NULL); 483 } 484 485 if (topo_prop_set_uint64(tn, TOPO_PGROUP_SES, 486 TOPO_PROP_NODE_ID, TOPO_PROP_IMMUTABLE, 487 nodeid, &err) != 0) { 488 topo_mod_dprintf(mod, 489 "failed to create property %s: %s\n", 490 TOPO_PROP_NODE_ID, topo_strerror(err)); 491 topo_node_unbind(tn); 492 return (NULL); 493 } 494 495 return (tn); 496 } 497 498 /* 499 * Add an indicator. This can be represented by a single property, or by the 500 * union of two elements when SES is capable of distinguishing between 501 * requested failure and detected failure. 502 */ 503 static int 504 ses_add_indicator(topo_mod_t *mod, tnode_t *pnode, uint64_t nodeid, 505 int type, const char *name, const char *propname, const char *altprop) 506 { 507 tnode_t *tn; 508 int err; 509 nvlist_t *nvl; 510 511 /* create facility node and add methods */ 512 if ((tn = ses_add_fac_common(mod, pnode, name, 513 TOPO_FAC_TYPE_INDICATOR, nodeid)) == NULL) 514 return (-1); 515 516 if (topo_method_register(mod, tn, ses_indicator_methods) < 0) { 517 topo_mod_dprintf(mod, "failed to register facility methods\n"); 518 topo_node_unbind(tn); 519 return (-1); 520 } 521 522 /* set standard properties */ 523 if (topo_prop_set_uint32(tn, TOPO_PGROUP_FACILITY, 524 TOPO_FACILITY_TYPE, TOPO_PROP_IMMUTABLE, type, &err) != 0) { 525 topo_mod_dprintf(mod, 526 "failed to set facility node properties: %s\n", 527 topo_strerror(err)); 528 topo_node_unbind(tn); 529 return (-1); 530 } 531 532 /* 'mode' property */ 533 nvl = NULL; 534 if (topo_mod_nvalloc(mod, &nvl, NV_UNIQUE_NAME) != 0 || 535 nvlist_add_string(nvl, TOPO_METH_SES_MODE_PROP, 536 propname) != 0 || 537 (altprop != NULL && nvlist_add_string(nvl, 538 TOPO_METH_SES_MODE_ALTPROP, altprop) != 0)) { 539 nvlist_free(nvl); 540 topo_mod_dprintf(mod, "failed to setup method arguments\n"); 541 topo_node_unbind(tn); 542 return (topo_mod_seterrno(mod, EMOD_NOMEM)); 543 } 544 545 if (topo_prop_method_register(tn, TOPO_PGROUP_FACILITY, 546 TOPO_LED_MODE, TOPO_TYPE_UINT32, "ses_indicator_mode", 547 nvl, &err) != 0) { 548 nvlist_free(nvl); 549 topo_mod_dprintf(mod, "failed to register reading method: %s\n", 550 topo_strerror(err)); 551 return (-1); 552 } 553 554 if (topo_prop_setmutable(tn, TOPO_PGROUP_FACILITY, 555 TOPO_LED_MODE, &err) != 0) { 556 nvlist_free(nvl); 557 topo_mod_dprintf(mod, "failed to set property as mutable: %s\n", 558 topo_strerror(err)); 559 return (-1); 560 } 561 562 nvlist_free(nvl); 563 return (0); 564 } 565 566 static tnode_t * 567 ses_add_sensor_common(topo_mod_t *mod, tnode_t *pnode, uint64_t nodeid, 568 const char *name, const char *class, int type) 569 { 570 tnode_t *tn; 571 int err; 572 573 /* create facility node and add methods */ 574 if ((tn = ses_add_fac_common(mod, pnode, name, 575 TOPO_FAC_TYPE_SENSOR, nodeid)) == NULL) 576 return (NULL); 577 578 if (topo_method_register(mod, tn, ses_sensor_methods) < 0) { 579 topo_mod_dprintf(mod, "failed to register facility methods\n"); 580 topo_node_unbind(tn); 581 return (NULL); 582 } 583 584 /* set standard properties */ 585 if (topo_prop_set_string(tn, TOPO_PGROUP_FACILITY, 586 TOPO_SENSOR_CLASS, TOPO_PROP_IMMUTABLE, 587 class, &err) != 0 || 588 topo_prop_set_uint32(tn, TOPO_PGROUP_FACILITY, 589 TOPO_FACILITY_TYPE, TOPO_PROP_IMMUTABLE, 590 type, &err) != 0) { 591 topo_mod_dprintf(mod, 592 "failed to set facility node properties: %s\n", 593 topo_strerror(err)); 594 topo_node_unbind(tn); 595 return (NULL); 596 } 597 598 return (tn); 599 } 600 601 /* 602 * Add an analog (threshold) sensor to the enclosure. This is used for fan 603 * speed, voltage, current, and temperature sensors. 604 */ 605 static int 606 ses_add_sensor(topo_mod_t *mod, tnode_t *pnode, uint64_t nodeid, 607 const char *name, const ses_sensor_desc_t *sdp) 608 { 609 tnode_t *tn; 610 int err; 611 nvlist_t *nvl; 612 613 if ((tn = ses_add_sensor_common(mod, pnode, nodeid, name, 614 TOPO_SENSOR_CLASS_THRESHOLD, sdp->sd_type)) == NULL) 615 return (-1); 616 617 if (topo_prop_set_uint32(tn, TOPO_PGROUP_FACILITY, 618 TOPO_SENSOR_UNITS, TOPO_PROP_IMMUTABLE, sdp->sd_units, &err) != 0) { 619 topo_mod_dprintf(mod, 620 "failed to set facility node properties: %s\n", 621 topo_strerror(err)); 622 topo_node_unbind(tn); 623 return (-1); 624 } 625 626 /* 'reading' property */ 627 nvl = NULL; 628 if (topo_mod_nvalloc(mod, &nvl, NV_UNIQUE_NAME) != 0 || 629 nvlist_add_string(nvl, TOPO_METH_SES_READING_PROP, 630 sdp->sd_propname) != 0 || 631 (sdp->sd_multiplier != 0 && 632 nvlist_add_double(nvl, TOPO_METH_SES_READING_MULT, 633 sdp->sd_multiplier) != 0)) { 634 nvlist_free(nvl); 635 topo_mod_dprintf(mod, "failed to setup method arguments\n"); 636 topo_node_unbind(tn); 637 return (-1); 638 } 639 640 if (topo_prop_method_register(tn, TOPO_PGROUP_FACILITY, 641 TOPO_SENSOR_READING, TOPO_TYPE_DOUBLE, "ses_sensor_reading", 642 nvl, &err) != 0) { 643 nvlist_free(nvl); 644 topo_mod_dprintf(mod, "failed to register reading method: %s\n", 645 topo_strerror(err)); 646 return (-1); 647 } 648 649 nvlist_free(nvl); 650 if (topo_mod_nvalloc(mod, &nvl, NV_UNIQUE_NAME) != 0) { 651 topo_mod_dprintf(mod, "failed to setup method arguments\n"); 652 topo_node_unbind(tn); 653 return (-1); 654 } 655 656 /* 'state' property */ 657 if (topo_prop_method_register(tn, TOPO_PGROUP_FACILITY, 658 TOPO_SENSOR_STATE, TOPO_TYPE_UINT32, "ses_sensor_state", 659 nvl, &err) != 0) { 660 nvlist_free(nvl); 661 topo_mod_dprintf(mod, "failed to register state method: %s\n", 662 topo_strerror(err)); 663 return (-1); 664 } 665 666 nvlist_free(nvl); 667 return (0); 668 } 669 670 /* 671 * Add a discrete sensor for simple boolean values. This is used to indicate 672 * externally-detected failures for fans, bays, and enclosures. 673 */ 674 static int 675 ses_add_discrete(topo_mod_t *mod, tnode_t *pnode, uint64_t nodeid, 676 const char *name, const char *prop) 677 { 678 tnode_t *tn; 679 int err; 680 nvlist_t *nvl; 681 682 if ((tn = ses_add_sensor_common(mod, pnode, nodeid, name, 683 TOPO_SENSOR_CLASS_DISCRETE, 684 TOPO_SENSOR_TYPE_GENERIC_STATE)) == NULL) 685 return (-1); 686 687 nvl = NULL; 688 if (topo_mod_nvalloc(mod, &nvl, NV_UNIQUE_NAME) != 0 || 689 nvlist_add_string(nvl, TOPO_METH_SES_STATE_PROP, prop) != 0) { 690 nvlist_free(nvl); 691 topo_mod_dprintf(mod, "failed to setup method arguments\n"); 692 topo_node_unbind(tn); 693 return (-1); 694 } 695 696 /* 'state' property */ 697 if (topo_prop_method_register(tn, TOPO_PGROUP_FACILITY, 698 TOPO_SENSOR_STATE, TOPO_TYPE_UINT32, "ses_sensor_state", 699 nvl, &err) != 0) { 700 nvlist_free(nvl); 701 topo_mod_dprintf(mod, "failed to register state method: %s\n", 702 topo_strerror(err)); 703 return (-1); 704 } 705 706 nvlist_free(nvl); 707 return (0); 708 } 709 710 /*ARGSUSED*/ 711 static int 712 ses_add_psu_status(topo_mod_t *mod, tnode_t *pnode, uint64_t nodeid) 713 { 714 tnode_t *tn; 715 int err; 716 nvlist_t *nvl; 717 718 /* create facility node and add methods */ 719 if ((tn = ses_add_sensor_common(mod, pnode, nodeid, "status", 720 TOPO_SENSOR_CLASS_DISCRETE, 721 TOPO_SENSOR_TYPE_POWER_SUPPLY)) == NULL) 722 return (-1); 723 724 if (topo_mod_nvalloc(mod, &nvl, NV_UNIQUE_NAME) != 0) { 725 nvlist_free(nvl); 726 topo_mod_dprintf(mod, "failed to setup method arguments\n"); 727 topo_node_unbind(tn); 728 return (-1); 729 } 730 731 /* 'state' property */ 732 if (topo_prop_method_register(tn, TOPO_PGROUP_FACILITY, 733 TOPO_SENSOR_STATE, TOPO_TYPE_UINT32, "ses_psu_state", 734 nvl, &err) != 0) { 735 nvlist_free(nvl); 736 topo_mod_dprintf(mod, "failed to register state method: %s\n", 737 topo_strerror(err)); 738 return (-1); 739 } 740 741 nvlist_free(nvl); 742 return (0); 743 } 744 745 /*ARGSUSED*/ 746 int 747 ses_node_enum_facility(topo_mod_t *mod, tnode_t *tn, topo_version_t vers, 748 nvlist_t *in, nvlist_t **out) 749 { 750 ses_node_t *np; 751 nvlist_t *props; 752 uint64_t type, nodeid; 753 ses_sensor_desc_t sd = { 0 }; 754 755 if ((np = ses_node_get(mod, tn)) == NULL) 756 return (-1); 757 758 assert(ses_node_type(np) == SES_NODE_ELEMENT); 759 nodeid = ses_node_id(np); 760 verify((props = ses_node_props(np)) != NULL); 761 verify(nvlist_lookup_uint64(props, SES_PROP_ELEMENT_TYPE, &type) == 0); 762 763 if (type != SES_ET_DEVICE && type != SES_ET_ARRAY_DEVICE && 764 type != SES_ET_COOLING && type != SES_ET_POWER_SUPPLY) 765 return (0); 766 767 /* 768 * Every element supports an 'ident' indicator. All elements also 769 * support a 'fail' indicator, but the properties used to represent 770 * this condition differs between elements. 771 */ 772 if (ses_add_indicator(mod, tn, nodeid, TOPO_LED_TYPE_LOCATE, "ident", 773 SES_PROP_IDENT, NULL) != 0) 774 return (-1); 775 776 switch (type) { 777 case SES_ET_DEVICE: 778 case SES_ET_ARRAY_DEVICE: 779 /* 780 * Disks support an additional 'ok2rm' indicator, as well as 781 * externally detected 'fail' sensor. 782 */ 783 if (ses_add_indicator(mod, tn, nodeid, TOPO_LED_TYPE_SERVICE, 784 "fail", SES_DEV_PROP_FAULT_RQSTD, 785 SES_DEV_PROP_FAULT_SENSED) != 0 || 786 ses_add_indicator(mod, tn, nodeid, TOPO_LED_TYPE_OK2RM, 787 "ok2rm", SES_PROP_RMV, SES_PROP_RMV) != 0 || 788 ses_add_discrete(mod, tn, nodeid, "fault", 789 SES_DEV_PROP_FAULT_SENSED) != 0) 790 return (-1); 791 break; 792 793 case SES_ET_COOLING: 794 /* 795 * Add the fan speed sensor, and a discrete sensor for 796 * detecting failure. 797 */ 798 sd.sd_type = TOPO_SENSOR_TYPE_THRESHOLD_STATE; 799 sd.sd_units = TOPO_SENSOR_UNITS_RPM; 800 sd.sd_propname = SES_COOLING_PROP_FAN_SPEED; 801 if (ses_add_indicator(mod, tn, nodeid, TOPO_LED_TYPE_SERVICE, 802 "fail", SES_PROP_FAIL, NULL) != 0 || 803 ses_add_sensor(mod, tn, nodeid, "speed", &sd) != 0 || 804 ses_add_discrete(mod, tn, nodeid, "fault", 805 SES_PROP_OFF) != 0) 806 return (-1); 807 break; 808 809 case SES_ET_POWER_SUPPLY: 810 /* 811 * For power supplies, we have a number of different sensors: 812 * acfail, dcfail, overtemp, undervoltate, overvoltage, 813 * and overcurrent. Rather than expose these all as individual 814 * sensors, we lump them together into a 'status' sensor of 815 * type TOPO_SENSOR_TYPE_POWER_SUPPLY and export the 816 * appropriate status flags as defined by the libtopo standard. 817 */ 818 if (ses_add_indicator(mod, tn, nodeid, TOPO_LED_TYPE_SERVICE, 819 "fail", SES_PROP_FAIL, NULL) != 0) 820 return (-1); 821 822 if (ses_add_psu_status(mod, tn, nodeid) != 0) 823 return (-1); 824 break; 825 826 default: 827 return (0); 828 } 829 830 return (0); 831 } 832 833 /* 834 * Add enclosure-wide sensors (temperature, voltage, and current) beneath the 835 * given aggregate. 836 */ 837 static int 838 ses_add_enclosure_sensors(topo_mod_t *mod, tnode_t *tn, ses_node_t *agg, 839 uint64_t type) 840 { 841 ses_node_t *child; 842 const char *defaultname; 843 char *desc, *name; 844 char rawname[64]; 845 nvlist_t *props, *aprops; 846 uint64_t index, nodeid; 847 ses_sensor_desc_t sd = { 0 }; 848 size_t len; 849 850 switch (type) { 851 case SES_ET_TEMPERATURE_SENSOR: 852 sd.sd_type = TOPO_SENSOR_TYPE_TEMP; 853 sd.sd_units = TOPO_SENSOR_UNITS_DEGREES_C; 854 sd.sd_propname = SES_TEMP_PROP_TEMP; 855 defaultname = "temperature"; 856 break; 857 858 case SES_ET_VOLTAGE_SENSOR: 859 sd.sd_type = TOPO_SENSOR_TYPE_VOLTAGE; 860 sd.sd_units = TOPO_SENSOR_UNITS_VOLTS; 861 sd.sd_propname = SES_VS_PROP_VOLTAGE_MV; 862 sd.sd_multiplier = 0.001; 863 defaultname = "voltage"; 864 break; 865 866 case SES_ET_CURRENT_SENSOR: 867 sd.sd_type = TOPO_SENSOR_TYPE_CURRENT; 868 sd.sd_units = TOPO_SENSOR_UNITS_AMPS; 869 sd.sd_propname = SES_CS_PROP_CURRENT_MA; 870 sd.sd_multiplier = 0.001; 871 defaultname = "current"; 872 break; 873 874 default: 875 return (0); 876 } 877 878 aprops = ses_node_props(agg); 879 880 for (child = ses_node_child(agg); child != NULL; 881 child = ses_node_sibling(child)) { 882 /* 883 * The only tricky part here is getting the name for the 884 * sensor, where we follow the algorithm of the standard 885 * elements. 886 */ 887 props = ses_node_props(child); 888 nodeid = ses_node_id(child); 889 if (nvlist_lookup_uint64(props, SES_PROP_ELEMENT_CLASS_INDEX, 890 &index) != 0) 891 continue; 892 893 if (nvlist_lookup_string(props, SES_PROP_DESCRIPTION, 894 &desc) == 0 && desc[0] != '\0') { 895 (void) strlcpy(rawname, desc, sizeof (rawname)); 896 } else { 897 if (nvlist_lookup_string(aprops, 898 SES_PROP_CLASS_DESCRIPTION, &desc) != 0 || 899 desc[0] == '\0') 900 desc = (char *)defaultname; 901 902 len = strlen(desc); 903 while (len > 0 && desc[len - 1] == ' ') 904 len--; 905 906 (void) snprintf(rawname, sizeof (rawname), 907 "%.*s %llu", len, desc, index); 908 } 909 910 if ((name = disk_auth_clean(mod, rawname)) == NULL) 911 return (-1); 912 913 if (ses_add_sensor(mod, tn, nodeid, name, &sd) != 0) { 914 topo_mod_strfree(mod, name); 915 return (-1); 916 } 917 918 topo_mod_strfree(mod, name); 919 } 920 921 return (0); 922 } 923 924 /*ARGSUSED*/ 925 int 926 ses_enc_enum_facility(topo_mod_t *mod, tnode_t *tn, topo_version_t vers, 927 nvlist_t *in, nvlist_t **out) 928 { 929 ses_node_t *np, *agg; 930 nvlist_t *aprops; 931 uint64_t type, nodeid; 932 933 if ((np = ses_node_get(mod, tn)) == NULL) 934 return (-1); 935 936 assert(ses_node_type(np) == SES_NODE_ENCLOSURE); 937 nodeid = ses_node_id(np); 938 939 /* 940 * 'ident' and 'fail' LEDs, and 'fault' sensor. 941 */ 942 if (ses_add_indicator(mod, tn, nodeid, TOPO_LED_TYPE_LOCATE, "ident", 943 SES_PROP_IDENT, NULL) != 0 || 944 ses_add_indicator(mod, tn, nodeid, TOPO_LED_TYPE_SERVICE, "fail", 945 SES_PROP_FAIL_REQ, SES_PROP_FAIL) != 0 || 946 ses_add_discrete(mod, tn, nodeid, "fault", SES_PROP_FAIL) != 0) 947 return (-1); 948 949 /* 950 * Environmental sensors (temperature, voltage, current). We have no 951 * way of knowing if any of these sensors correspond to a particular 952 * element, so we just attach them to the enclosure as a whole. In the 953 * future, some vendor-specific libses plugin knowledge could let us 954 * make this correlation clearer. 955 */ 956 for (agg = ses_node_child(np); agg != NULL; 957 agg = ses_node_sibling(agg)) { 958 if (ses_node_type(agg) != SES_NODE_AGGREGATE) 959 continue; 960 961 verify((aprops = ses_node_props(agg)) != NULL); 962 if (nvlist_lookup_uint64(aprops, SES_PROP_ELEMENT_TYPE, 963 &type) != 0) 964 continue; 965 966 if (ses_add_enclosure_sensors(mod, tn, agg, type) != 0) 967 return (-1); 968 } 969 970 return (0); 971 } 972