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