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