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