9455 Expose drive speed and temperature on disk topo node
[unleashed.git] / usr / src / lib / fm / topo / modules / common / ses / ses_facility.c
blob2c4a1314e3e681af8a2a1206feecbe955eb40404
1 /*
2 * CDDL HEADER START
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.
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.
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]
19 * CDDL HEADER END
23 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
28 * Copyright 2015 Nexenta Systems, Inc. All rights reserved.
32 * Copyright (c) 2018, Joyent, Inc.
36 * Facility node support for SES enclosures. We support the following facility
37 * nodes, based on the node type:
39 * bay
40 * indicator=ident
41 * indicator=fail
42 * indicator=ok2rm
43 * sensor=fault
45 * controller
46 * indicator=ident
47 * indicator=fail
49 * fan
50 * indicator=ident
51 * indicator=fail
52 * sensor=speed
53 * sensor=fault
55 * psu
56 * indicator=ident
57 * indicator=fail
58 * sensor=status
60 * ses-enclosure
61 * indicator=ident
62 * indicator=fail
63 * sensor=fault
64 * sensor=<name> (temperature)
65 * sensor=<name> (voltage)
66 * sensor=<name> (current)
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.
74 #include "ses.h"
75 #include "disk.h"
77 #include <string.h>
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 **);
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
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;
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
105 #define TOPO_METH_SES_READING_PROP "propname"
106 #define TOPO_METH_SES_READING_MULT "multiplier"
108 #define TOPO_METH_SES_STATE_PROP "propname"
110 #define TOPO_METH_SES_MODE_PROP "property-name"
111 #define TOPO_METH_SES_MODE_ALTPROP "alternate-property"
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 }
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 },
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.
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)
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);
150 if (vers > TOPO_METH_SES_MODE_VERSION)
151 return (topo_mod_seterrno(mod, ETOPO_METHOD_VERNEW));
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));
160 if (nvlist_lookup_string(args, TOPO_METH_SES_MODE_ALTPROP,
161 &altprop) != 0)
162 altprop = NULL;
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);
169 verify((props = ses_node_props(np)) != NULL);
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;
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;
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;
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;
205 tp->set_snaptime = 0;
206 nvlist_free(nvl);
207 } else {
208 /* get operation */
209 if (nvlist_lookup_boolean_value(props,
210 propname, &current) != 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;
217 if (altprop != NULL && nvlist_lookup_boolean_value(props,
218 altprop, &altcurrent) == 0)
219 current |= altcurrent;
221 mode = current ? TOPO_LED_STATE_ON : TOPO_LED_STATE_OFF;
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;
235 ses_node_unlock(mod, tn);
236 *out = nvl;
237 return (0);
239 error:
240 ses_node_unlock(mod, tn);
241 return (-1);
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).
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)
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;
261 if (vers > TOPO_METH_SES_MODE_VERSION)
262 return (topo_mod_seterrno(mod, ETOPO_METHOD_VERNEW));
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));
272 if (nvlist_lookup_double(args, TOPO_METH_SES_READING_MULT,
273 &multiplier) != 0)
274 multiplier = 1;
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);
281 verify((props = ses_node_props(np)) != NULL);
283 if (nvlist_lookup_uint64(props, prop, &current) == 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));
294 ses_node_unlock(mod, tn);
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));
306 *out = nvl;
307 return (0);
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.
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)
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;
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));
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);
341 verify((props = ses_node_props(np)) != NULL);
343 if (nvlist_lookup_uint64(props, SES_PROP_STATUS_CODE, &status) != 0)
344 status = SES_ESC_UNSUPPORTED;
346 state = 0;
347 if (nvlist_lookup_string(args, TOPO_METH_SES_STATE_PROP,
348 &prop) == 0) {
349 /* discrete (fault) sensor */
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;
376 ses_node_unlock(mod, tn);
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));
388 *out = nvl;
389 return (0);
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:
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)
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.
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)
412 nvlist_t *nvl, *props;
413 boolean_t value;
414 uint32_t state;
415 ses_node_t *np;
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);
422 verify((props = ses_node_props(np)) != NULL);
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;
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;
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;
441 ses_node_unlock(mod, tn);
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));
453 *out = nvl;
454 return (0);
458 * Create a facility node, either a sensor or an indicator.
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)
464 tnode_t *tn;
465 topo_pgroup_info_t pgi;
466 int err;
467 ses_enum_target_t *stp = topo_node_getspecific(pnode);
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);
475 stp->set_refcount++;
476 topo_node_setspecific(tn, stp);
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;
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);
491 * We need the node-id property for each facility node.
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;
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);
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);
515 return (tn);
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.
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)
527 tnode_t *tn;
528 int err;
529 nvlist_t *nvl;
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);
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);
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);
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));
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);
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);
582 nvlist_free(nvl);
583 return (0);
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)
590 tnode_t *tn;
591 int err;
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);
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);
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);
618 return (tn);
622 * Add an analog (threshold) sensor to the enclosure. This is used for fan
623 * speed, voltage, current, and temperature sensors.
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)
629 tnode_t *tn;
630 int err;
631 nvlist_t *nvl;
633 if ((tn = ses_add_sensor_common(mod, pnode, nodeid, name,
634 TOPO_SENSOR_CLASS_THRESHOLD, sdp->sd_type)) == NULL)
635 return (-1);
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);
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);
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);
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);
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);
686 nvlist_free(nvl);
687 return (0);
691 * Add a discrete sensor for simple boolean values. This is used to indicate
692 * externally-detected failures for fans, bays, and enclosures.
694 static int
695 ses_add_discrete(topo_mod_t *mod, tnode_t *pnode, uint64_t nodeid,
696 const char *name, const char *prop)
698 tnode_t *tn;
699 int err;
700 nvlist_t *nvl;
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);
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);
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);
726 nvlist_free(nvl);
727 return (0);
730 /*ARGSUSED*/
731 static int
732 ses_add_psu_status(topo_mod_t *mod, tnode_t *pnode, uint64_t nodeid)
734 tnode_t *tn;
735 int err;
736 nvlist_t *nvl;
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);
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);
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);
761 nvlist_free(nvl);
762 return (0);
765 /*ARGSUSED*/
767 ses_node_enum_facility(topo_mod_t *mod, tnode_t *tn, topo_version_t vers,
768 nvlist_t *in, nvlist_t **out)
770 ses_node_t *np;
771 nvlist_t *props;
772 uint64_t type, nodeid;
773 ses_sensor_desc_t sd = { 0 };
775 if ((np = ses_node_lock(mod, tn)) == NULL)
776 return (-1);
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);
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);
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.
794 if (ses_add_indicator(mod, tn, nodeid, TOPO_LED_TYPE_LOCATE, "ident",
795 SES_PROP_IDENT, NULL) != 0)
796 goto error;
798 switch (type) {
799 case SES_ET_DEVICE:
800 case SES_ET_ARRAY_DEVICE:
802 * Disks support an additional 'ok2rm' indicator, as well as
803 * externally detected 'fail' sensor.
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;
815 case SES_ET_COOLING:
817 * Add the fan speed sensor, and a discrete sensor for
818 * detecting failure.
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;
831 case SES_ET_POWER_SUPPLY:
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.
840 if (ses_add_indicator(mod, tn, nodeid, TOPO_LED_TYPE_SERVICE,
841 "fail", SES_PROP_FAIL, NULL) != 0)
842 goto error;
844 if (ses_add_psu_status(mod, tn, nodeid) != 0)
845 goto error;
846 break;
848 default:
849 return (0);
852 ses_node_unlock(mod, tn);
853 return (0);
855 error:
856 ses_node_unlock(mod, tn);
857 return (-1);
861 * Add enclosure-wide sensors (temperature, voltage, and current) beneath the
862 * given aggregate.
864 static int
865 ses_add_enclosure_sensors(topo_mod_t *mod, tnode_t *tn, ses_node_t *agg,
866 uint64_t type)
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;
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;
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;
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;
901 default:
902 return (0);
905 aprops = ses_node_props(agg);
907 for (child = ses_node_child(agg); child != NULL;
908 child = ses_node_sibling(child)) {
910 * The only tricky part here is getting the name for the
911 * sensor, where we follow the algorithm of the standard
912 * elements.
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;
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;
929 len = strlen(desc);
930 while (len > 0 && desc[len - 1] == ' ')
931 len--;
933 (void) snprintf(rawname, sizeof (rawname),
934 "%.*s %llu", len, desc, index);
937 if ((name = topo_mod_clean_str(mod, rawname)) == NULL)
938 return (-1);
940 if (ses_add_sensor(mod, tn, nodeid, name, &sd) != 0) {
941 topo_mod_strfree(mod, name);
942 return (-1);
945 topo_mod_strfree(mod, name);
948 return (0);
951 /*ARGSUSED*/
953 ses_enc_enum_facility(topo_mod_t *mod, tnode_t *tn, topo_version_t vers,
954 nvlist_t *in, nvlist_t **out)
956 ses_node_t *np, *agg;
957 nvlist_t *aprops;
958 uint64_t type, nodeid;
960 if ((np = ses_node_lock(mod, tn)) == NULL)
961 return (-1);
963 assert(ses_node_type(np) == SES_NODE_ENCLOSURE);
964 nodeid = ses_node_id(np);
967 * 'ident' and 'fail' LEDs, and 'fault' sensor.
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;
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.
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;
988 verify((aprops = ses_node_props(agg)) != NULL);
989 if (nvlist_lookup_uint64(aprops, SES_PROP_ELEMENT_TYPE,
990 &type) != 0)
991 continue;
993 if (ses_add_enclosure_sensors(mod, tn, agg, type) != 0)
994 goto error;
997 ses_node_unlock(mod, tn);
998 return (0);
1000 error:
1001 ses_node_unlock(mod, tn);
1002 return (-1);