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