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 2009 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_lock(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 (void) topo_mod_seterrno(mod, EMOD_NVL_INVAL); 170 goto error; 171 } 172 173 if (mode != TOPO_LED_STATE_OFF && mode != TOPO_LED_STATE_ON) { 174 topo_mod_dprintf(mod, "invalid indicator mode %d\n", 175 mode); 176 (void) topo_mod_seterrno(mod, EMOD_NVL_INVAL); 177 goto error; 178 } 179 180 nvl = NULL; 181 if (topo_mod_nvalloc(mod, &nvl, NV_UNIQUE_NAME) != 0 || 182 nvlist_add_boolean_value(nvl, propname, 183 mode == TOPO_LED_STATE_ON ? B_TRUE : B_FALSE) != 0) { 184 nvlist_free(nvl); 185 (void) topo_mod_seterrno(mod, EMOD_NOMEM); 186 goto error; 187 } 188 189 if (ses_node_ctl(np, SES_CTL_OP_SETPROP, nvl) != 0) { 190 topo_mod_dprintf(mod, "failed to set indicator: %s\n", 191 ses_errmsg()); 192 nvlist_free(nvl); 193 goto error; 194 } 195 196 nvlist_free(nvl); 197 } else { 198 /* get operation */ 199 if (nvlist_lookup_boolean_value(props, 200 propname, ¤t) != 0) { 201 topo_mod_dprintf(mod, "failed to lookup %s in node " 202 "properties\n", propname); 203 (void) topo_mod_seterrno(mod, EMOD_METHOD_NOTSUP); 204 goto error; 205 } 206 207 if (altprop != NULL && nvlist_lookup_boolean_value(props, 208 altprop, &altcurrent) == 0) 209 current |= altcurrent; 210 211 mode = current ? TOPO_LED_STATE_ON : TOPO_LED_STATE_OFF; 212 } 213 214 nvl = NULL; 215 if (topo_mod_nvalloc(mod, &nvl, NV_UNIQUE_NAME) != 0 || 216 nvlist_add_string(nvl, TOPO_PROP_VAL_NAME, 217 TOPO_LED_MODE) != 0 || 218 nvlist_add_uint32(nvl, TOPO_PROP_VAL_TYPE, TOPO_TYPE_UINT32) != 0 || 219 nvlist_add_uint32(nvl, TOPO_PROP_VAL_VAL, mode) != 0) { 220 nvlist_free(nvl); 221 (void) topo_mod_seterrno(mod, EMOD_NOMEM); 222 goto error; 223 } 224 225 ses_node_unlock(mod, tn); 226 *out = nvl; 227 return (0); 228 229 error: 230 ses_node_unlock(mod, tn); 231 return (-1); 232 } 233 234 /* 235 * Read the given sensor value. This just looks up the value in the node 236 * properties, and multiplies by a fixed value (determined when the method is 237 * instantiated). 238 */ 239 static int 240 ses_sensor_reading(topo_mod_t *mod, tnode_t *tn, topo_version_t vers, 241 nvlist_t *in, nvlist_t **out) 242 { 243 ses_node_t *np; 244 nvlist_t *args, *props; 245 char *prop; 246 double raw, multiplier; 247 uint64_t current; 248 int64_t scurrent; 249 nvlist_t *nvl; 250 251 if (vers > TOPO_METH_SES_MODE_VERSION) 252 return (topo_mod_seterrno(mod, ETOPO_METHOD_VERNEW)); 253 254 if (nvlist_lookup_nvlist(in, TOPO_PROP_ARGS, &args) != 0 || 255 nvlist_lookup_string(args, TOPO_METH_SES_READING_PROP, 256 &prop) != 0) { 257 topo_mod_dprintf(mod, 258 "invalid arguments to 'reading' method\n"); 259 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); 260 } 261 262 if (nvlist_lookup_double(args, TOPO_METH_SES_READING_MULT, 263 &multiplier) != 0) 264 multiplier = 1; 265 266 if ((np = ses_node_lock(mod, tn)) == NULL) { 267 topo_mod_dprintf(mod, "failed to lookup ses node in 'mode' " 268 "method\n"); 269 return (-1); 270 } 271 verify((props = ses_node_props(np)) != NULL); 272 273 if (nvlist_lookup_uint64(props, prop, ¤t) == 0) { 274 raw = (double)current; 275 } else if (nvlist_lookup_int64(props, prop, &scurrent) == 0) { 276 raw = (double)scurrent; 277 } else { 278 topo_mod_dprintf(mod, "failed to lookup %s in node " 279 "properties\n", prop); 280 ses_node_unlock(mod, tn); 281 return (topo_mod_seterrno(mod, EMOD_METHOD_NOTSUP)); 282 } 283 284 ses_node_unlock(mod, tn); 285 286 nvl = NULL; 287 if (topo_mod_nvalloc(mod, &nvl, NV_UNIQUE_NAME) != 0 || 288 nvlist_add_string(nvl, TOPO_PROP_VAL_NAME, 289 TOPO_SENSOR_READING) != 0 || 290 nvlist_add_uint32(nvl, TOPO_PROP_VAL_TYPE, TOPO_TYPE_DOUBLE) != 0 || 291 nvlist_add_double(nvl, TOPO_PROP_VAL_VAL, raw * multiplier) != 0) { 292 nvlist_free(nvl); 293 return (topo_mod_seterrno(mod, EMOD_NOMEM)); 294 } 295 296 *out = nvl; 297 return (0); 298 } 299 300 /* 301 * Returns the current sensor state. This can be invoked for one of two 302 * different types of sensors: threshold or discrete sensors. For discrete 303 * sensors, we expect a name of a boolean property and indicate 304 * asserted/deasserted based on that. For threshold sensors, we check for the 305 * standard warning/critical properties and translate that into the appropriate 306 * topo state. 307 */ 308 /*ARGSUSED*/ 309 static int 310 ses_sensor_state(topo_mod_t *mod, tnode_t *tn, topo_version_t vers, 311 nvlist_t *in, nvlist_t **out) 312 { 313 nvlist_t *nvl, *args, *props; 314 boolean_t value; 315 uint64_t status; 316 uint32_t state; 317 ses_node_t *np; 318 char *prop; 319 320 if (nvlist_lookup_nvlist(in, TOPO_PROP_ARGS, &args) != 0) { 321 topo_mod_dprintf(mod, 322 "invalid arguments to 'state' method\n"); 323 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); 324 } 325 326 if ((np = ses_node_lock(mod, tn)) == NULL) { 327 topo_mod_dprintf(mod, "failed to lookup ses node in 'mode' " 328 "method\n"); 329 return (-1); 330 } 331 verify((props = ses_node_props(np)) != NULL); 332 333 if (nvlist_lookup_uint64(props, SES_PROP_STATUS_CODE, &status) != 0) 334 status = SES_ESC_UNSUPPORTED; 335 336 state = 0; 337 if (nvlist_lookup_string(args, TOPO_METH_SES_STATE_PROP, 338 &prop) == 0) { 339 /* discrete (fault) sensor */ 340 341 if (status == SES_ESC_UNRECOVERABLE) 342 state |= TOPO_SENSOR_STATE_GENERIC_FAIL_NONRECOV; 343 else if (status == SES_ESC_CRITICAL) 344 state |= TOPO_SENSOR_STATE_GENERIC_FAIL_CRITICAL; 345 else if (nvlist_lookup_boolean_value(props, prop, 346 &value) == 0 && value) 347 state |= TOPO_SENSOR_STATE_GENERIC_FAIL_NONRECOV; 348 else 349 state |= TOPO_SENSOR_STATE_GENERIC_FAIL_DEASSERTED; 350 } else { 351 /* threshold sensor */ 352 if (nvlist_lookup_boolean_value(props, 353 SES_PROP_WARN_UNDER, &value) == 0 && value) 354 state |= TOPO_SENSOR_STATE_THRESH_LOWER_NONCRIT; 355 if (nvlist_lookup_boolean_value(props, 356 SES_PROP_WARN_OVER, &value) == 0 && value) 357 state |= TOPO_SENSOR_STATE_THRESH_UPPER_NONCRIT; 358 if (nvlist_lookup_boolean_value(props, 359 SES_PROP_CRIT_UNDER, &value) == 0 && value) 360 state |= TOPO_SENSOR_STATE_THRESH_LOWER_CRIT; 361 if (nvlist_lookup_boolean_value(props, 362 SES_PROP_CRIT_OVER, &value) == 0 && value) 363 state |= TOPO_SENSOR_STATE_THRESH_UPPER_CRIT; 364 } 365 366 ses_node_unlock(mod, tn); 367 368 nvl = NULL; 369 if (topo_mod_nvalloc(mod, &nvl, NV_UNIQUE_NAME) != 0 || 370 nvlist_add_string(nvl, TOPO_PROP_VAL_NAME, 371 TOPO_SENSOR_STATE) != 0 || 372 nvlist_add_uint32(nvl, TOPO_PROP_VAL_TYPE, TOPO_TYPE_UINT32) != 0 || 373 nvlist_add_uint32(nvl, TOPO_PROP_VAL_VAL, state) != 0) { 374 nvlist_free(nvl); 375 return (topo_mod_seterrno(mod, EMOD_NOMEM)); 376 } 377 378 *out = nvl; 379 return (0); 380 } 381 382 /* 383 * Read the status of a PSU. This is such a specialized operation that it has 384 * its own method instead of trying to piggyback on ses_sensor_state(). We 385 * use the following mapping to get to the standard topo power supply states: 386 * 387 * acfail -> INPUT_LOST 388 * dcfail -> INPUT_LOST 389 * undervoltage -> INPUT_RANGE 390 * overvoltage -> INPUT_RANGE_PRES 391 * overcurrent -> INPUT_RANGE_PRES 392 * overtemp -> (none) 393 * 394 * If we ever have a need for reading overtemp, we can expand the topo 395 * representation for power supplies, but at the moment this seems unnecessary. 396 */ 397 /*ARGSUSED*/ 398 static int 399 ses_psu_state(topo_mod_t *mod, tnode_t *tn, topo_version_t vers, 400 nvlist_t *in, nvlist_t **out) 401 { 402 nvlist_t *nvl, *props; 403 boolean_t value; 404 uint32_t state; 405 ses_node_t *np; 406 407 if ((np = ses_node_lock(mod, tn)) == NULL) { 408 topo_mod_dprintf(mod, "failed to lookup ses node in 'mode' " 409 "method\n"); 410 return (-1); 411 } 412 verify((props = ses_node_props(np)) != NULL); 413 414 state = 0; 415 if ((nvlist_lookup_boolean_value(props, SES_PSU_PROP_DC_FAIL, 416 &value) == 0 && value) || 417 (nvlist_lookup_boolean_value(props, SES_PSU_PROP_AC_FAIL, 418 &value) == 0 && value)) 419 state |= TOPO_SENSOR_STATE_POWER_SUPPLY_INPUT_LOST; 420 421 if (nvlist_lookup_boolean_value(props, SES_PSU_PROP_DC_UNDER_VOLTAGE, 422 &value) == 0 && value) 423 state |= TOPO_SENSOR_STATE_POWER_SUPPLY_INPUT_RANGE; 424 425 if ((nvlist_lookup_boolean_value(props, SES_PSU_PROP_DC_OVER_VOLTAGE, 426 &value) == 0 && value) || 427 (nvlist_lookup_boolean_value(props, SES_PSU_PROP_DC_OVER_CURRENT, 428 &value) == 0 && value)) 429 state |= TOPO_SENSOR_STATE_POWER_SUPPLY_INPUT_RANGE_PRES; 430 431 ses_node_unlock(mod, tn); 432 433 nvl = NULL; 434 if (topo_mod_nvalloc(mod, &nvl, NV_UNIQUE_NAME) != 0 || 435 nvlist_add_string(nvl, TOPO_PROP_VAL_NAME, 436 TOPO_SENSOR_STATE) != 0 || 437 nvlist_add_uint32(nvl, TOPO_PROP_VAL_TYPE, TOPO_TYPE_UINT32) != 0 || 438 nvlist_add_uint32(nvl, TOPO_PROP_VAL_VAL, state) != 0) { 439 nvlist_free(nvl); 440 return (topo_mod_seterrno(mod, EMOD_NOMEM)); 441 } 442 443 *out = nvl; 444 return (0); 445 } 446 447 /* 448 * Create a facility node, either a sensor or an indicator. 449 */ 450 static tnode_t * 451 ses_add_fac_common(topo_mod_t *mod, tnode_t *pnode, const char *name, 452 const char *type, uint64_t nodeid) 453 { 454 tnode_t *tn; 455 topo_pgroup_info_t pgi; 456 int err; 457 ses_enum_target_t *stp = topo_node_getspecific(pnode); 458 459 if ((tn = topo_node_facbind(mod, pnode, name, type)) == NULL) { 460 topo_mod_dprintf(mod, "failed to bind facility node %s\n", 461 name); 462 return (NULL); 463 } 464 465 stp->set_refcount++; 466 topo_node_setspecific(tn, stp); 467 468 pgi.tpi_name = TOPO_PGROUP_FACILITY; 469 pgi.tpi_namestab = TOPO_STABILITY_PRIVATE; 470 pgi.tpi_datastab = TOPO_STABILITY_PRIVATE; 471 pgi.tpi_version = 1; 472 473 if (topo_pgroup_create(tn, &pgi, &err) != 0) { 474 topo_mod_dprintf(mod, "failed to create facility property " 475 "group: %s\n", topo_strerror(err)); 476 topo_node_unbind(tn); 477 return (NULL); 478 } 479 480 /* 481 * We need the node-id property for each facility node. 482 */ 483 pgi.tpi_name = TOPO_PGROUP_SES; 484 pgi.tpi_namestab = TOPO_STABILITY_PRIVATE; 485 pgi.tpi_datastab = TOPO_STABILITY_PRIVATE; 486 pgi.tpi_version = TOPO_VERSION; 487 488 if (topo_pgroup_create(tn, &pgi, &err) != 0) { 489 topo_mod_dprintf(mod, "failed to create ses property " 490 "group: %s\n", topo_strerror(err)); 491 topo_node_unbind(tn); 492 return (NULL); 493 } 494 495 if (topo_prop_set_uint64(tn, TOPO_PGROUP_SES, 496 TOPO_PROP_NODE_ID, TOPO_PROP_IMMUTABLE, 497 nodeid, &err) != 0) { 498 topo_mod_dprintf(mod, 499 "failed to create property %s: %s\n", 500 TOPO_PROP_NODE_ID, topo_strerror(err)); 501 topo_node_unbind(tn); 502 return (NULL); 503 } 504 505 return (tn); 506 } 507 508 /* 509 * Add an indicator. This can be represented by a single property, or by the 510 * union of two elements when SES is capable of distinguishing between 511 * requested failure and detected failure. 512 */ 513 static int 514 ses_add_indicator(topo_mod_t *mod, tnode_t *pnode, uint64_t nodeid, 515 int type, const char *name, const char *propname, const char *altprop) 516 { 517 tnode_t *tn; 518 int err; 519 nvlist_t *nvl; 520 521 /* create facility node and add methods */ 522 if ((tn = ses_add_fac_common(mod, pnode, name, 523 TOPO_FAC_TYPE_INDICATOR, nodeid)) == NULL) 524 return (-1); 525 526 if (topo_method_register(mod, tn, ses_indicator_methods) < 0) { 527 topo_mod_dprintf(mod, "failed to register facility methods\n"); 528 topo_node_unbind(tn); 529 return (-1); 530 } 531 532 /* set standard properties */ 533 if (topo_prop_set_uint32(tn, TOPO_PGROUP_FACILITY, 534 TOPO_FACILITY_TYPE, TOPO_PROP_IMMUTABLE, type, &err) != 0) { 535 topo_mod_dprintf(mod, 536 "failed to set facility node properties: %s\n", 537 topo_strerror(err)); 538 topo_node_unbind(tn); 539 return (-1); 540 } 541 542 /* 'mode' property */ 543 nvl = NULL; 544 if (topo_mod_nvalloc(mod, &nvl, NV_UNIQUE_NAME) != 0 || 545 nvlist_add_string(nvl, TOPO_METH_SES_MODE_PROP, 546 propname) != 0 || 547 (altprop != NULL && nvlist_add_string(nvl, 548 TOPO_METH_SES_MODE_ALTPROP, altprop) != 0)) { 549 nvlist_free(nvl); 550 topo_mod_dprintf(mod, "failed to setup method arguments\n"); 551 topo_node_unbind(tn); 552 return (topo_mod_seterrno(mod, EMOD_NOMEM)); 553 } 554 555 if (topo_prop_method_register(tn, TOPO_PGROUP_FACILITY, 556 TOPO_LED_MODE, TOPO_TYPE_UINT32, "ses_indicator_mode", 557 nvl, &err) != 0) { 558 nvlist_free(nvl); 559 topo_mod_dprintf(mod, "failed to register reading method: %s\n", 560 topo_strerror(err)); 561 return (-1); 562 } 563 564 if (topo_prop_setmutable(tn, TOPO_PGROUP_FACILITY, 565 TOPO_LED_MODE, &err) != 0) { 566 nvlist_free(nvl); 567 topo_mod_dprintf(mod, "failed to set property as mutable: %s\n", 568 topo_strerror(err)); 569 return (-1); 570 } 571 572 nvlist_free(nvl); 573 return (0); 574 } 575 576 static tnode_t * 577 ses_add_sensor_common(topo_mod_t *mod, tnode_t *pnode, uint64_t nodeid, 578 const char *name, const char *class, int type) 579 { 580 tnode_t *tn; 581 int err; 582 583 /* create facility node and add methods */ 584 if ((tn = ses_add_fac_common(mod, pnode, name, 585 TOPO_FAC_TYPE_SENSOR, nodeid)) == NULL) 586 return (NULL); 587 588 if (topo_method_register(mod, tn, ses_sensor_methods) < 0) { 589 topo_mod_dprintf(mod, "failed to register facility methods\n"); 590 topo_node_unbind(tn); 591 return (NULL); 592 } 593 594 /* set standard properties */ 595 if (topo_prop_set_string(tn, TOPO_PGROUP_FACILITY, 596 TOPO_SENSOR_CLASS, TOPO_PROP_IMMUTABLE, 597 class, &err) != 0 || 598 topo_prop_set_uint32(tn, TOPO_PGROUP_FACILITY, 599 TOPO_FACILITY_TYPE, TOPO_PROP_IMMUTABLE, 600 type, &err) != 0) { 601 topo_mod_dprintf(mod, 602 "failed to set facility node properties: %s\n", 603 topo_strerror(err)); 604 topo_node_unbind(tn); 605 return (NULL); 606 } 607 608 return (tn); 609 } 610 611 /* 612 * Add an analog (threshold) sensor to the enclosure. This is used for fan 613 * speed, voltage, current, and temperature sensors. 614 */ 615 static int 616 ses_add_sensor(topo_mod_t *mod, tnode_t *pnode, uint64_t nodeid, 617 const char *name, const ses_sensor_desc_t *sdp) 618 { 619 tnode_t *tn; 620 int err; 621 nvlist_t *nvl; 622 623 if ((tn = ses_add_sensor_common(mod, pnode, nodeid, name, 624 TOPO_SENSOR_CLASS_THRESHOLD, sdp->sd_type)) == NULL) 625 return (-1); 626 627 if (topo_prop_set_uint32(tn, TOPO_PGROUP_FACILITY, 628 TOPO_SENSOR_UNITS, TOPO_PROP_IMMUTABLE, sdp->sd_units, &err) != 0) { 629 topo_mod_dprintf(mod, 630 "failed to set facility node properties: %s\n", 631 topo_strerror(err)); 632 topo_node_unbind(tn); 633 return (-1); 634 } 635 636 /* 'reading' property */ 637 nvl = NULL; 638 if (topo_mod_nvalloc(mod, &nvl, NV_UNIQUE_NAME) != 0 || 639 nvlist_add_string(nvl, TOPO_METH_SES_READING_PROP, 640 sdp->sd_propname) != 0 || 641 (sdp->sd_multiplier != 0 && 642 nvlist_add_double(nvl, TOPO_METH_SES_READING_MULT, 643 sdp->sd_multiplier) != 0)) { 644 nvlist_free(nvl); 645 topo_mod_dprintf(mod, "failed to setup method arguments\n"); 646 topo_node_unbind(tn); 647 return (-1); 648 } 649 650 if (topo_prop_method_register(tn, TOPO_PGROUP_FACILITY, 651 TOPO_SENSOR_READING, TOPO_TYPE_DOUBLE, "ses_sensor_reading", 652 nvl, &err) != 0) { 653 nvlist_free(nvl); 654 topo_mod_dprintf(mod, "failed to register reading method: %s\n", 655 topo_strerror(err)); 656 return (-1); 657 } 658 659 nvlist_free(nvl); 660 if (topo_mod_nvalloc(mod, &nvl, NV_UNIQUE_NAME) != 0) { 661 topo_mod_dprintf(mod, "failed to setup method arguments\n"); 662 topo_node_unbind(tn); 663 return (-1); 664 } 665 666 /* 'state' property */ 667 if (topo_prop_method_register(tn, TOPO_PGROUP_FACILITY, 668 TOPO_SENSOR_STATE, TOPO_TYPE_UINT32, "ses_sensor_state", 669 nvl, &err) != 0) { 670 nvlist_free(nvl); 671 topo_mod_dprintf(mod, "failed to register state method: %s\n", 672 topo_strerror(err)); 673 return (-1); 674 } 675 676 nvlist_free(nvl); 677 return (0); 678 } 679 680 /* 681 * Add a discrete sensor for simple boolean values. This is used to indicate 682 * externally-detected failures for fans, bays, and enclosures. 683 */ 684 static int 685 ses_add_discrete(topo_mod_t *mod, tnode_t *pnode, uint64_t nodeid, 686 const char *name, const char *prop) 687 { 688 tnode_t *tn; 689 int err; 690 nvlist_t *nvl; 691 692 if ((tn = ses_add_sensor_common(mod, pnode, nodeid, name, 693 TOPO_SENSOR_CLASS_DISCRETE, 694 TOPO_SENSOR_TYPE_GENERIC_FAILURE)) == NULL) 695 return (-1); 696 697 nvl = NULL; 698 if (topo_mod_nvalloc(mod, &nvl, NV_UNIQUE_NAME) != 0 || 699 nvlist_add_string(nvl, TOPO_METH_SES_STATE_PROP, prop) != 0) { 700 nvlist_free(nvl); 701 topo_mod_dprintf(mod, "failed to setup method arguments\n"); 702 topo_node_unbind(tn); 703 return (-1); 704 } 705 706 /* 'state' property */ 707 if (topo_prop_method_register(tn, TOPO_PGROUP_FACILITY, 708 TOPO_SENSOR_STATE, TOPO_TYPE_UINT32, "ses_sensor_state", 709 nvl, &err) != 0) { 710 nvlist_free(nvl); 711 topo_mod_dprintf(mod, "failed to register state method: %s\n", 712 topo_strerror(err)); 713 return (-1); 714 } 715 716 nvlist_free(nvl); 717 return (0); 718 } 719 720 /*ARGSUSED*/ 721 static int 722 ses_add_psu_status(topo_mod_t *mod, tnode_t *pnode, uint64_t nodeid) 723 { 724 tnode_t *tn; 725 int err; 726 nvlist_t *nvl; 727 728 /* create facility node and add methods */ 729 if ((tn = ses_add_sensor_common(mod, pnode, nodeid, "status", 730 TOPO_SENSOR_CLASS_DISCRETE, 731 TOPO_SENSOR_TYPE_POWER_SUPPLY)) == NULL) 732 return (-1); 733 734 if (topo_mod_nvalloc(mod, &nvl, NV_UNIQUE_NAME) != 0) { 735 nvlist_free(nvl); 736 topo_mod_dprintf(mod, "failed to setup method arguments\n"); 737 topo_node_unbind(tn); 738 return (-1); 739 } 740 741 /* 'state' property */ 742 if (topo_prop_method_register(tn, TOPO_PGROUP_FACILITY, 743 TOPO_SENSOR_STATE, TOPO_TYPE_UINT32, "ses_psu_state", 744 nvl, &err) != 0) { 745 nvlist_free(nvl); 746 topo_mod_dprintf(mod, "failed to register state method: %s\n", 747 topo_strerror(err)); 748 return (-1); 749 } 750 751 nvlist_free(nvl); 752 return (0); 753 } 754 755 /*ARGSUSED*/ 756 int 757 ses_node_enum_facility(topo_mod_t *mod, tnode_t *tn, topo_version_t vers, 758 nvlist_t *in, nvlist_t **out) 759 { 760 ses_node_t *np; 761 nvlist_t *props; 762 uint64_t type, nodeid; 763 ses_sensor_desc_t sd = { 0 }; 764 765 if ((np = ses_node_lock(mod, tn)) == NULL) 766 return (-1); 767 768 assert(ses_node_type(np) == SES_NODE_ELEMENT); 769 nodeid = ses_node_id(np); 770 verify((props = ses_node_props(np)) != NULL); 771 verify(nvlist_lookup_uint64(props, SES_PROP_ELEMENT_TYPE, &type) == 0); 772 773 if (type != SES_ET_DEVICE && type != SES_ET_ARRAY_DEVICE && 774 type != SES_ET_COOLING && type != SES_ET_POWER_SUPPLY) { 775 ses_node_unlock(mod, tn); 776 return (0); 777 } 778 779 /* 780 * Every element supports an 'ident' indicator. All elements also 781 * support a 'fail' indicator, but the properties used to represent 782 * this condition differs between elements. 783 */ 784 if (ses_add_indicator(mod, tn, nodeid, TOPO_LED_TYPE_LOCATE, "ident", 785 SES_PROP_IDENT, NULL) != 0) 786 goto error; 787 788 switch (type) { 789 case SES_ET_DEVICE: 790 case SES_ET_ARRAY_DEVICE: 791 /* 792 * Disks support an additional 'ok2rm' indicator, as well as 793 * externally detected 'fail' sensor. 794 */ 795 if (ses_add_indicator(mod, tn, nodeid, TOPO_LED_TYPE_SERVICE, 796 "fail", SES_DEV_PROP_FAULT_RQSTD, 797 SES_DEV_PROP_FAULT_SENSED) != 0 || 798 ses_add_indicator(mod, tn, nodeid, TOPO_LED_TYPE_OK2RM, 799 "ok2rm", SES_PROP_RMV, SES_PROP_RMV) != 0 || 800 ses_add_discrete(mod, tn, nodeid, "fault", 801 SES_DEV_PROP_FAULT_SENSED) != 0) 802 goto error; 803 break; 804 805 case SES_ET_COOLING: 806 /* 807 * Add the fan speed sensor, and a discrete sensor for 808 * detecting failure. 809 */ 810 sd.sd_type = TOPO_SENSOR_TYPE_THRESHOLD_STATE; 811 sd.sd_units = TOPO_SENSOR_UNITS_RPM; 812 sd.sd_propname = SES_COOLING_PROP_FAN_SPEED; 813 if (ses_add_indicator(mod, tn, nodeid, TOPO_LED_TYPE_SERVICE, 814 "fail", SES_PROP_FAIL, NULL) != 0 || 815 ses_add_sensor(mod, tn, nodeid, "speed", &sd) != 0 || 816 ses_add_discrete(mod, tn, nodeid, "fault", 817 SES_PROP_OFF) != 0) 818 goto error; 819 break; 820 821 case SES_ET_POWER_SUPPLY: 822 /* 823 * For power supplies, we have a number of different sensors: 824 * acfail, dcfail, overtemp, undervoltate, overvoltage, 825 * and overcurrent. Rather than expose these all as individual 826 * sensors, we lump them together into a 'status' sensor of 827 * type TOPO_SENSOR_TYPE_POWER_SUPPLY and export the 828 * appropriate status flags as defined by the libtopo standard. 829 */ 830 if (ses_add_indicator(mod, tn, nodeid, TOPO_LED_TYPE_SERVICE, 831 "fail", SES_PROP_FAIL, NULL) != 0) 832 goto error; 833 834 if (ses_add_psu_status(mod, tn, nodeid) != 0) 835 goto error; 836 break; 837 838 default: 839 return (0); 840 } 841 842 ses_node_unlock(mod, tn); 843 return (0); 844 845 error: 846 ses_node_unlock(mod, tn); 847 return (-1); 848 } 849 850 /* 851 * Add enclosure-wide sensors (temperature, voltage, and current) beneath the 852 * given aggregate. 853 */ 854 static int 855 ses_add_enclosure_sensors(topo_mod_t *mod, tnode_t *tn, ses_node_t *agg, 856 uint64_t type) 857 { 858 ses_node_t *child; 859 const char *defaultname; 860 char *desc, *name; 861 char rawname[64]; 862 nvlist_t *props, *aprops; 863 uint64_t index, nodeid; 864 ses_sensor_desc_t sd = { 0 }; 865 size_t len; 866 867 switch (type) { 868 case SES_ET_TEMPERATURE_SENSOR: 869 sd.sd_type = TOPO_SENSOR_TYPE_TEMP; 870 sd.sd_units = TOPO_SENSOR_UNITS_DEGREES_C; 871 sd.sd_propname = SES_TEMP_PROP_TEMP; 872 defaultname = "temperature"; 873 break; 874 875 case SES_ET_VOLTAGE_SENSOR: 876 sd.sd_type = TOPO_SENSOR_TYPE_VOLTAGE; 877 sd.sd_units = TOPO_SENSOR_UNITS_VOLTS; 878 sd.sd_propname = SES_VS_PROP_VOLTAGE_MV; 879 sd.sd_multiplier = 0.001; 880 defaultname = "voltage"; 881 break; 882 883 case SES_ET_CURRENT_SENSOR: 884 sd.sd_type = TOPO_SENSOR_TYPE_CURRENT; 885 sd.sd_units = TOPO_SENSOR_UNITS_AMPS; 886 sd.sd_propname = SES_CS_PROP_CURRENT_MA; 887 sd.sd_multiplier = 0.001; 888 defaultname = "current"; 889 break; 890 891 default: 892 return (0); 893 } 894 895 aprops = ses_node_props(agg); 896 897 for (child = ses_node_child(agg); child != NULL; 898 child = ses_node_sibling(child)) { 899 /* 900 * The only tricky part here is getting the name for the 901 * sensor, where we follow the algorithm of the standard 902 * elements. 903 */ 904 props = ses_node_props(child); 905 nodeid = ses_node_id(child); 906 if (nvlist_lookup_uint64(props, SES_PROP_ELEMENT_CLASS_INDEX, 907 &index) != 0) 908 continue; 909 910 if (nvlist_lookup_string(props, SES_PROP_DESCRIPTION, 911 &desc) == 0 && desc[0] != '\0') { 912 (void) strlcpy(rawname, desc, sizeof (rawname)); 913 } else { 914 if (nvlist_lookup_string(aprops, 915 SES_PROP_CLASS_DESCRIPTION, &desc) != 0 || 916 desc[0] == '\0') 917 desc = (char *)defaultname; 918 919 len = strlen(desc); 920 while (len > 0 && desc[len - 1] == ' ') 921 len--; 922 923 (void) snprintf(rawname, sizeof (rawname), 924 "%.*s %llu", len, desc, index); 925 } 926 927 if ((name = disk_auth_clean(mod, rawname)) == NULL) 928 return (-1); 929 930 if (ses_add_sensor(mod, tn, nodeid, name, &sd) != 0) { 931 topo_mod_strfree(mod, name); 932 return (-1); 933 } 934 935 topo_mod_strfree(mod, name); 936 } 937 938 return (0); 939 } 940 941 /*ARGSUSED*/ 942 int 943 ses_enc_enum_facility(topo_mod_t *mod, tnode_t *tn, topo_version_t vers, 944 nvlist_t *in, nvlist_t **out) 945 { 946 ses_node_t *np, *agg; 947 nvlist_t *aprops; 948 uint64_t type, nodeid; 949 950 if ((np = ses_node_lock(mod, tn)) == NULL) 951 return (-1); 952 953 assert(ses_node_type(np) == SES_NODE_ENCLOSURE); 954 nodeid = ses_node_id(np); 955 956 /* 957 * 'ident' and 'fail' LEDs, and 'fault' sensor. 958 */ 959 if (ses_add_indicator(mod, tn, nodeid, TOPO_LED_TYPE_LOCATE, "ident", 960 SES_PROP_IDENT, NULL) != 0 || 961 ses_add_indicator(mod, tn, nodeid, TOPO_LED_TYPE_SERVICE, "fail", 962 SES_PROP_FAIL_REQ, SES_PROP_FAIL) != 0 || 963 ses_add_discrete(mod, tn, nodeid, "fault", SES_PROP_FAIL) != 0) 964 goto error; 965 966 /* 967 * Environmental sensors (temperature, voltage, current). We have no 968 * way of knowing if any of these sensors correspond to a particular 969 * element, so we just attach them to the enclosure as a whole. In the 970 * future, some vendor-specific libses plugin knowledge could let us 971 * make this correlation clearer. 972 */ 973 for (agg = ses_node_child(np); agg != NULL; 974 agg = ses_node_sibling(agg)) { 975 if (ses_node_type(agg) != SES_NODE_AGGREGATE) 976 continue; 977 978 verify((aprops = ses_node_props(agg)) != NULL); 979 if (nvlist_lookup_uint64(aprops, SES_PROP_ELEMENT_TYPE, 980 &type) != 0) 981 continue; 982 983 if (ses_add_enclosure_sensors(mod, tn, agg, type) != 0) 984 goto error; 985 } 986 987 ses_node_unlock(mod, tn); 988 return (0); 989 990 error: 991 ses_node_unlock(mod, tn); 992 return (-1); 993 } 994