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 * Facility node support for SES enclosures. We support the following facility 33 * nodes, based on the node type: 34 * 35 * bay 36 * indicator=ident 37 * indicator=fail 38 * indicator=ok2rm 39 * sensor=fault 40 * 41 * controller 42 * indicator=ident 43 * indicator=fail 44 * 45 * fan 46 * indicator=ident 47 * indicator=fail 48 * sensor=speed 49 * sensor=fault 50 * 51 * psu 52 * indicator=ident 53 * indicator=fail 54 * sensor=status 55 * 56 * ses-enclosure 57 * indicator=ident 58 * indicator=fail 59 * sensor=fault 60 * sensor=<name> (temperature) 61 * sensor=<name> (voltage) 62 * sensor=<name> (current) 63 * 64 * Most of these are handled by a single method that supports getting and 65 * setting boolean properties on the node. The fan speed sensor requires a 66 * special handler, while the analog enclosure sensors all have similar 67 * behavior and can be grouped together using a common method. 68 */ 69 70 #include "ses.h" 71 #include "disk.h" 72 73 #include <string.h> 74 75 static int ses_indicator_mode(topo_mod_t *, tnode_t *, topo_version_t, 76 nvlist_t *, nvlist_t **); 77 static int ses_sensor_reading(topo_mod_t *, tnode_t *, topo_version_t, 78 nvlist_t *, nvlist_t **); 79 static int ses_sensor_state(topo_mod_t *, tnode_t *, topo_version_t, 80 nvlist_t *, nvlist_t **); 81 static int ses_psu_state(topo_mod_t *, tnode_t *, topo_version_t, 82 nvlist_t *, nvlist_t **); 83 84 #define SES_SUPP_WARN_UNDER 0x01 85 #define SES_SUPP_WARN_OVER 0x02 86 #define SES_SUPP_CRIT_UNDER 0x04 87 #define SES_SUPP_CRIT_OVER 0x08 88 89 typedef struct ses_sensor_desc { 90 int sd_type; 91 int sd_units; 92 const char *sd_propname; 93 double sd_multiplier; 94 } ses_sensor_desc_t; 95 96 #define TOPO_METH_SES_MODE_VERSION 0 97 #define TOPO_METH_SES_READING_VERSION 0 98 #define TOPO_METH_SES_STATE_VERSION 0 99 #define TOPO_METH_SES_PSU_VERSION 0 100 101 #define TOPO_METH_SES_READING_PROP "propname" 102 #define TOPO_METH_SES_READING_MULT "multiplier" 103 104 #define TOPO_METH_SES_STATE_PROP "propname" 105 106 #define TOPO_METH_SES_MODE_PROP "property-name" 107 #define TOPO_METH_SES_MODE_ALTPROP "alternate-property" 108 109 static const topo_method_t ses_indicator_methods[] = { 110 { "ses_indicator_mode", TOPO_PROP_METH_DESC, 111 TOPO_METH_SES_MODE_VERSION, TOPO_STABILITY_INTERNAL, 112 ses_indicator_mode } 113 }; 114 115 static const topo_method_t ses_sensor_methods[] = { 116 { "ses_sensor_reading", TOPO_PROP_METH_DESC, 117 TOPO_METH_SES_READING_VERSION, TOPO_STABILITY_INTERNAL, 118 ses_sensor_reading }, 119 { "ses_sensor_state", TOPO_PROP_METH_DESC, 120 TOPO_METH_SES_STATE_VERSION, TOPO_STABILITY_INTERNAL, 121 ses_sensor_state }, 122 { "ses_psu_state", TOPO_PROP_METH_DESC, 123 TOPO_METH_SES_PSU_VERSION, TOPO_STABILITY_INTERNAL, 124 ses_psu_state }, 125 }; 126 127 /* 128 * Get or set an indicator. This method is invoked with arguments indicating 129 * the property to query to retrieve the value. Some elements (enclosures and 130 * devices) support a request property that is distinct from an array-detected 131 * property. Either of these conditions will result in the indicator being 132 * lit, so we have to check both properties. 133 */ 134 static int 135 ses_indicator_mode(topo_mod_t *mod, tnode_t *tn, topo_version_t vers, 136 nvlist_t *in, nvlist_t **out) 137 { 138 ses_node_t *np; 139 nvlist_t *args, *pargs, *props; 140 char *propname, *altprop; 141 uint32_t mode; 142 boolean_t current, altcurrent; 143 nvlist_t *nvl; 144 ses_enum_target_t *tp = topo_node_getspecific(tn); 145 146 if (vers > TOPO_METH_SES_MODE_VERSION) 147 return (topo_mod_seterrno(mod, ETOPO_METHOD_VERNEW)); 148 149 if (nvlist_lookup_nvlist(in, TOPO_PROP_ARGS, &args) != 0 || 150 nvlist_lookup_string(args, TOPO_METH_SES_MODE_PROP, 151 &propname) != 0) { 152 topo_mod_dprintf(mod, "invalid arguments to 'mode' method\n"); 153 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); 154 } 155 156 if (nvlist_lookup_string(args, TOPO_METH_SES_MODE_ALTPROP, 157 &altprop) != 0) 158 altprop = NULL; 159 160 if ((np = ses_node_lock(mod, tn)) == NULL) { 161 topo_mod_dprintf(mod, "failed to lookup ses node in 'mode' " 162 "method\n"); 163 return (-1); 164 } 165 verify((props = ses_node_props(np)) != NULL); 166 167 if (nvlist_lookup_nvlist(in, TOPO_PROP_PARGS, &pargs) == 0 && 168 nvlist_exists(pargs, TOPO_PROP_VAL_VAL)) { 169 /* set operation */ 170 if (nvlist_lookup_uint32(pargs, TOPO_PROP_VAL_VAL, 171 &mode) != 0) { 172 topo_mod_dprintf(mod, "invalid type for indicator " 173 "mode property"); 174 (void) topo_mod_seterrno(mod, EMOD_NVL_INVAL); 175 goto error; 176 } 177 178 if (mode != TOPO_LED_STATE_OFF && mode != TOPO_LED_STATE_ON) { 179 topo_mod_dprintf(mod, "invalid indicator mode %d\n", 180 mode); 181 (void) topo_mod_seterrno(mod, EMOD_NVL_INVAL); 182 goto error; 183 } 184 185 nvl = NULL; 186 if (topo_mod_nvalloc(mod, &nvl, NV_UNIQUE_NAME) != 0 || 187 nvlist_add_boolean_value(nvl, propname, 188 mode == TOPO_LED_STATE_ON ? B_TRUE : B_FALSE) != 0) { 189 nvlist_free(nvl); 190 (void) topo_mod_seterrno(mod, EMOD_NOMEM); 191 goto error; 192 } 193 194 if (ses_node_ctl(np, SES_CTL_OP_SETPROP, nvl) != 0) { 195 topo_mod_dprintf(mod, "failed to set indicator: %s\n", 196 ses_errmsg()); 197 nvlist_free(nvl); 198 goto error; 199 } 200 201 tp->set_snaptime = 0; 202 nvlist_free(nvl); 203 } else { 204 /* get operation */ 205 if (nvlist_lookup_boolean_value(props, 206 propname, ¤t) != 0) { 207 topo_mod_dprintf(mod, "failed to lookup %s in node " 208 "properties\n", propname); 209 (void) topo_mod_seterrno(mod, EMOD_METHOD_NOTSUP); 210 goto error; 211 } 212 213 if (altprop != NULL && nvlist_lookup_boolean_value(props, 214 altprop, &altcurrent) == 0) 215 current |= altcurrent; 216 217 mode = current ? TOPO_LED_STATE_ON : TOPO_LED_STATE_OFF; 218 } 219 220 nvl = NULL; 221 if (topo_mod_nvalloc(mod, &nvl, NV_UNIQUE_NAME) != 0 || 222 nvlist_add_string(nvl, TOPO_PROP_VAL_NAME, 223 TOPO_LED_MODE) != 0 || 224 nvlist_add_uint32(nvl, TOPO_PROP_VAL_TYPE, TOPO_TYPE_UINT32) != 0 || 225 nvlist_add_uint32(nvl, TOPO_PROP_VAL_VAL, mode) != 0) { 226 nvlist_free(nvl); 227 (void) topo_mod_seterrno(mod, EMOD_NOMEM); 228 goto error; 229 } 230 231 ses_node_unlock(mod, tn); 232 *out = nvl; 233 return (0); 234 235 error: 236 ses_node_unlock(mod, tn); 237 return (-1); 238 } 239 240 /* 241 * Read the given sensor value. This just looks up the value in the node 242 * properties, and multiplies by a fixed value (determined when the method is 243 * instantiated). 244 */ 245 static int 246 ses_sensor_reading(topo_mod_t *mod, tnode_t *tn, topo_version_t vers, 247 nvlist_t *in, nvlist_t **out) 248 { 249 ses_node_t *np; 250 nvlist_t *args, *props; 251 char *prop; 252 double raw, multiplier; 253 uint64_t current; 254 int64_t scurrent; 255 nvlist_t *nvl; 256 257 if (vers > TOPO_METH_SES_MODE_VERSION) 258 return (topo_mod_seterrno(mod, ETOPO_METHOD_VERNEW)); 259 260 if (nvlist_lookup_nvlist(in, TOPO_PROP_ARGS, &args) != 0 || 261 nvlist_lookup_string(args, TOPO_METH_SES_READING_PROP, 262 &prop) != 0) { 263 topo_mod_dprintf(mod, 264 "invalid arguments to 'reading' method\n"); 265 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); 266 } 267 268 if (nvlist_lookup_double(args, TOPO_METH_SES_READING_MULT, 269 &multiplier) != 0) 270 multiplier = 1; 271 272 if ((np = ses_node_lock(mod, tn)) == NULL) { 273 topo_mod_dprintf(mod, "failed to lookup ses node in 'mode' " 274 "method\n"); 275 return (-1); 276 } 277 verify((props = ses_node_props(np)) != NULL); 278 279 if (nvlist_lookup_uint64(props, prop, ¤t) == 0) { 280 raw = (double)current; 281 } else if (nvlist_lookup_int64(props, prop, &scurrent) == 0) { 282 raw = (double)scurrent; 283 } else { 284 topo_mod_dprintf(mod, "failed to lookup %s in node " 285 "properties\n", prop); 286 ses_node_unlock(mod, tn); 287 return (topo_mod_seterrno(mod, EMOD_METHOD_NOTSUP)); 288 } 289 290 ses_node_unlock(mod, tn); 291 292 nvl = NULL; 293 if (topo_mod_nvalloc(mod, &nvl, NV_UNIQUE_NAME) != 0 || 294 nvlist_add_string(nvl, TOPO_PROP_VAL_NAME, 295 TOPO_SENSOR_READING) != 0 || 296 nvlist_add_uint32(nvl, TOPO_PROP_VAL_TYPE, TOPO_TYPE_DOUBLE) != 0 || 297 nvlist_add_double(nvl, TOPO_PROP_VAL_VAL, raw * multiplier) != 0) { 298 nvlist_free(nvl); 299 return (topo_mod_seterrno(mod, EMOD_NOMEM)); 300 } 301 302 *out = nvl; 303 return (0); 304 } 305 306 /* 307 * Returns the current sensor state. This can be invoked for one of two 308 * different types of sensors: threshold or discrete sensors. For discrete 309 * sensors, we expect a name of a boolean property and indicate 310 * asserted/deasserted based on that. For threshold sensors, we check for the 311 * standard warning/critical properties and translate that into the appropriate 312 * topo state. 313 */ 314 /*ARGSUSED*/ 315 static int 316 ses_sensor_state(topo_mod_t *mod, tnode_t *tn, topo_version_t vers, 317 nvlist_t *in, nvlist_t **out) 318 { 319 nvlist_t *nvl, *args, *props; 320 boolean_t value; 321 uint64_t status; 322 uint32_t state; 323 ses_node_t *np; 324 char *prop; 325 326 if (nvlist_lookup_nvlist(in, TOPO_PROP_ARGS, &args) != 0) { 327 topo_mod_dprintf(mod, 328 "invalid arguments to 'state' method\n"); 329 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); 330 } 331 332 if ((np = ses_node_lock(mod, tn)) == NULL) { 333 topo_mod_dprintf(mod, "failed to lookup ses node in 'mode' " 334 "method\n"); 335 return (-1); 336 } 337 verify((props = ses_node_props(np)) != NULL); 338 339 if (nvlist_lookup_uint64(props, SES_PROP_STATUS_CODE, &status) != 0) 340 status = SES_ESC_UNSUPPORTED; 341 342 state = 0; 343 if (nvlist_lookup_string(args, TOPO_METH_SES_STATE_PROP, 344 &prop) == 0) { 345 /* discrete (fault) sensor */ 346 347 if (status == SES_ESC_UNRECOVERABLE) 348 state |= TOPO_SENSOR_STATE_GENERIC_FAIL_NONRECOV; 349 else if (status == SES_ESC_CRITICAL) 350 state |= TOPO_SENSOR_STATE_GENERIC_FAIL_CRITICAL; 351 else if (nvlist_lookup_boolean_value(props, prop, 352 &value) == 0 && value) 353 state |= TOPO_SENSOR_STATE_GENERIC_FAIL_NONRECOV; 354 else 355 state |= TOPO_SENSOR_STATE_GENERIC_FAIL_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_FAILURE)) == 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_FAIL) != 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