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