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