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