dmake: do not set MAKEFLAGS=k
[unleashed/tickless.git] / usr / src / lib / fm / topo / modules / common / ses / ses_facility.c
blob08a4a569eee0134502c8845fe379c4d6f9c63bed
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 * Facility node support for SES enclosures. We support the following facility
33 * nodes, based on the node type:
35 * bay
36 * indicator=ident
37 * indicator=fail
38 * indicator=ok2rm
39 * sensor=fault
41 * controller
42 * indicator=ident
43 * indicator=fail
45 * fan
46 * indicator=ident
47 * indicator=fail
48 * sensor=speed
49 * sensor=fault
51 * psu
52 * indicator=ident
53 * indicator=fail
54 * sensor=status
56 * ses-enclosure
57 * indicator=ident
58 * indicator=fail
59 * sensor=fault
60 * sensor=<name> (temperature)
61 * sensor=<name> (voltage)
62 * sensor=<name> (current)
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.
70 #include "ses.h"
71 #include "disk.h"
73 #include <string.h>
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 **);
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
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;
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
101 #define TOPO_METH_SES_READING_PROP "propname"
102 #define TOPO_METH_SES_READING_MULT "multiplier"
104 #define TOPO_METH_SES_STATE_PROP "propname"
106 #define TOPO_METH_SES_MODE_PROP "property-name"
107 #define TOPO_METH_SES_MODE_ALTPROP "alternate-property"
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 }
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 },
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.
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)
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);
146 if (vers > TOPO_METH_SES_MODE_VERSION)
147 return (topo_mod_seterrno(mod, ETOPO_METHOD_VERNEW));
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));
156 if (nvlist_lookup_string(args, TOPO_METH_SES_MODE_ALTPROP,
157 &altprop) != 0)
158 altprop = NULL;
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);
165 verify((props = ses_node_props(np)) != NULL);
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;
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;
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;
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;
201 tp->set_snaptime = 0;
202 nvlist_free(nvl);
203 } else {
204 /* get operation */
205 if (nvlist_lookup_boolean_value(props,
206 propname, &current) != 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;
213 if (altprop != NULL && nvlist_lookup_boolean_value(props,
214 altprop, &altcurrent) == 0)
215 current |= altcurrent;
217 mode = current ? TOPO_LED_STATE_ON : TOPO_LED_STATE_OFF;
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;
231 ses_node_unlock(mod, tn);
232 *out = nvl;
233 return (0);
235 error:
236 ses_node_unlock(mod, tn);
237 return (-1);
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).
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)
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;
257 if (vers > TOPO_METH_SES_MODE_VERSION)
258 return (topo_mod_seterrno(mod, ETOPO_METHOD_VERNEW));
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));
268 if (nvlist_lookup_double(args, TOPO_METH_SES_READING_MULT,
269 &multiplier) != 0)
270 multiplier = 1;
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);
277 verify((props = ses_node_props(np)) != NULL);
279 if (nvlist_lookup_uint64(props, prop, &current) == 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));
290 ses_node_unlock(mod, tn);
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));
302 *out = nvl;
303 return (0);
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.
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)
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;
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));
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);
337 verify((props = ses_node_props(np)) != NULL);
339 if (nvlist_lookup_uint64(props, SES_PROP_STATUS_CODE, &status) != 0)
340 status = SES_ESC_UNSUPPORTED;
342 state = 0;
343 if (nvlist_lookup_string(args, TOPO_METH_SES_STATE_PROP,
344 &prop) == 0) {
345 /* discrete (fault) sensor */
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;
372 ses_node_unlock(mod, tn);
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));
384 *out = nvl;
385 return (0);
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:
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)
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.
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)
408 nvlist_t *nvl, *props;
409 boolean_t value;
410 uint32_t state;
411 ses_node_t *np;
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);
418 verify((props = ses_node_props(np)) != NULL);
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;
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;
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;
437 ses_node_unlock(mod, tn);
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));
449 *out = nvl;
450 return (0);
454 * Create a facility node, either a sensor or an indicator.
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)
460 tnode_t *tn;
461 topo_pgroup_info_t pgi;
462 int err;
463 ses_enum_target_t *stp = topo_node_getspecific(pnode);
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);
471 stp->set_refcount++;
472 topo_node_setspecific(tn, stp);
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;
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);
487 * We need the node-id property for each facility node.
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;
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);
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);
511 return (tn);
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.
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)
523 tnode_t *tn;
524 int err;
525 nvlist_t *nvl;
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);
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);
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);
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));
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);
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);
578 nvlist_free(nvl);
579 return (0);
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)
586 tnode_t *tn;
587 int err;
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);
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);
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);
614 return (tn);
618 * Add an analog (threshold) sensor to the enclosure. This is used for fan
619 * speed, voltage, current, and temperature sensors.
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)
625 tnode_t *tn;
626 int err;
627 nvlist_t *nvl;
629 if ((tn = ses_add_sensor_common(mod, pnode, nodeid, name,
630 TOPO_SENSOR_CLASS_THRESHOLD, sdp->sd_type)) == NULL)
631 return (-1);
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);
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);
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);
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);
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);
682 nvlist_free(nvl);
683 return (0);
687 * Add a discrete sensor for simple boolean values. This is used to indicate
688 * externally-detected failures for fans, bays, and enclosures.
690 static int
691 ses_add_discrete(topo_mod_t *mod, tnode_t *pnode, uint64_t nodeid,
692 const char *name, const char *prop)
694 tnode_t *tn;
695 int err;
696 nvlist_t *nvl;
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);
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);
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);
722 nvlist_free(nvl);
723 return (0);
726 /*ARGSUSED*/
727 static int
728 ses_add_psu_status(topo_mod_t *mod, tnode_t *pnode, uint64_t nodeid)
730 tnode_t *tn;
731 int err;
732 nvlist_t *nvl;
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);
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);
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);
757 nvlist_free(nvl);
758 return (0);
761 /*ARGSUSED*/
763 ses_node_enum_facility(topo_mod_t *mod, tnode_t *tn, topo_version_t vers,
764 nvlist_t *in, nvlist_t **out)
766 ses_node_t *np;
767 nvlist_t *props;
768 uint64_t type, nodeid;
769 ses_sensor_desc_t sd = { 0 };
771 if ((np = ses_node_lock(mod, tn)) == NULL)
772 return (-1);
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);
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);
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.
790 if (ses_add_indicator(mod, tn, nodeid, TOPO_LED_TYPE_LOCATE, "ident",
791 SES_PROP_IDENT, NULL) != 0)
792 goto error;
794 switch (type) {
795 case SES_ET_DEVICE:
796 case SES_ET_ARRAY_DEVICE:
798 * Disks support an additional 'ok2rm' indicator, as well as
799 * externally detected 'fail' sensor.
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;
811 case SES_ET_COOLING:
813 * Add the fan speed sensor, and a discrete sensor for
814 * detecting failure.
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;
827 case SES_ET_POWER_SUPPLY:
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.
836 if (ses_add_indicator(mod, tn, nodeid, TOPO_LED_TYPE_SERVICE,
837 "fail", SES_PROP_FAIL, NULL) != 0)
838 goto error;
840 if (ses_add_psu_status(mod, tn, nodeid) != 0)
841 goto error;
842 break;
844 default:
845 return (0);
848 ses_node_unlock(mod, tn);
849 return (0);
851 error:
852 ses_node_unlock(mod, tn);
853 return (-1);
857 * Add enclosure-wide sensors (temperature, voltage, and current) beneath the
858 * given aggregate.
860 static int
861 ses_add_enclosure_sensors(topo_mod_t *mod, tnode_t *tn, ses_node_t *agg,
862 uint64_t type)
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;
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;
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;
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;
897 default:
898 return (0);
901 aprops = ses_node_props(agg);
903 for (child = ses_node_child(agg); child != NULL;
904 child = ses_node_sibling(child)) {
906 * The only tricky part here is getting the name for the
907 * sensor, where we follow the algorithm of the standard
908 * elements.
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;
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;
925 len = strlen(desc);
926 while (len > 0 && desc[len - 1] == ' ')
927 len--;
929 (void) snprintf(rawname, sizeof (rawname),
930 "%.*s %llu", len, desc, index);
933 if ((name = disk_auth_clean(mod, rawname)) == NULL)
934 return (-1);
936 if (ses_add_sensor(mod, tn, nodeid, name, &sd) != 0) {
937 topo_mod_strfree(mod, name);
938 return (-1);
941 topo_mod_strfree(mod, name);
944 return (0);
947 /*ARGSUSED*/
949 ses_enc_enum_facility(topo_mod_t *mod, tnode_t *tn, topo_version_t vers,
950 nvlist_t *in, nvlist_t **out)
952 ses_node_t *np, *agg;
953 nvlist_t *aprops;
954 uint64_t type, nodeid;
956 if ((np = ses_node_lock(mod, tn)) == NULL)
957 return (-1);
959 assert(ses_node_type(np) == SES_NODE_ENCLOSURE);
960 nodeid = ses_node_id(np);
963 * 'ident' and 'fail' LEDs, and 'fault' sensor.
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;
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.
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;
984 verify((aprops = ses_node_props(agg)) != NULL);
985 if (nvlist_lookup_uint64(aprops, SES_PROP_ELEMENT_TYPE,
986 &type) != 0)
987 continue;
989 if (ses_add_enclosure_sensors(mod, tn, agg, type) != 0)
990 goto error;
993 ses_node_unlock(mod, tn);
994 return (0);
996 error:
997 ses_node_unlock(mod, tn);
998 return (-1);