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 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 */
25 /*
26 * Copyright (c) 2018, Joyent, Inc.
27 */
28
29 #include <pthread.h>
30 #include <assert.h>
31 #include <errno.h>
32 #include <dirent.h>
33 #include <fnmatch.h>
34 #include <limits.h>
35 #include <alloca.h>
36 #include <unistd.h>
37 #include <stdio.h>
38 #include <strings.h>
39
40 #include <topo_mod.h>
41
42 #include <topo_error.h>
43 #include <topo_module.h>
44 #include <topo_subr.h>
45 #include <topo_tree.h>
46
47 topo_imethod_t *
topo_method_lookup(tnode_t * node,const char * name)48 topo_method_lookup(tnode_t *node, const char *name)
49 {
50 topo_imethod_t *mp;
51
52 for (mp = topo_list_next(&node->tn_methods); mp != NULL;
53 mp = topo_list_next(mp)) {
54 if (strcmp(name, mp->tim_name) == 0) {
55 topo_node_unlock(node);
56 return (mp);
57 }
58 }
59
60 return (NULL);
61 }
62
63 /*
64 * Simple API to determine if the specified node supports a given topo method
65 * (specified by the method name and version). Returns true if supported, false
66 * otherwise.
67 */
68 boolean_t
topo_method_supported(tnode_t * node,const char * name,topo_version_t vers)69 topo_method_supported(tnode_t *node, const char *name, topo_version_t vers)
70 {
71 topo_imethod_t *mp;
72
73 topo_node_lock(node);
74 for (mp = topo_list_next(&node->tn_methods); mp != NULL;
75 mp = topo_list_next(mp)) {
76 if ((strcmp(name, mp->tim_name) == 0) &&
77 (vers == mp->tim_version)) {
78 topo_node_unlock(node);
79 return (B_TRUE);
80 }
81 }
82 topo_node_unlock(node);
83 return (B_FALSE);
84 }
85
86 static void
topo_method_enter(topo_imethod_t * mp)87 topo_method_enter(topo_imethod_t *mp)
88 {
89 (void) pthread_mutex_lock(&mp->tim_lock);
90
91 while (mp->tim_busy != 0)
92 (void) pthread_cond_wait(&mp->tim_cv, &mp->tim_lock);
93
94 ++mp->tim_busy;
95
96 (void) pthread_mutex_unlock(&mp->tim_lock);
97 }
98
99 static void
topo_method_exit(topo_imethod_t * mp)100 topo_method_exit(topo_imethod_t *mp)
101 {
102 (void) pthread_mutex_lock(&mp->tim_lock);
103 --mp->tim_busy;
104
105 assert(mp->tim_busy == 0);
106
107 (void) pthread_cond_broadcast(&mp->tim_cv);
108 (void) pthread_mutex_unlock(&mp->tim_lock);
109 }
110
111 static int
set_methregister_error(topo_mod_t * mod,tnode_t * node,topo_imethod_t * mp,int err)112 set_methregister_error(topo_mod_t *mod, tnode_t *node, topo_imethod_t *mp,
113 int err)
114 {
115 if (mp != NULL) {
116 topo_list_delete(&node->tn_methods, mp);
117 if (mp->tim_name != NULL)
118 topo_mod_strfree(mod, mp->tim_name);
119 if (mp->tim_desc != NULL)
120 topo_mod_strfree(mod, mp->tim_desc);
121
122 topo_mod_free(mod, mp, sizeof (topo_imethod_t));
123 }
124
125 topo_node_unlock(node);
126
127 topo_dprintf(mod->tm_hdl, TOPO_DBG_ERR,
128 "method registration failed for %s: %s\n",
129 mod->tm_name, topo_strerror(err));
130
131 return (topo_mod_seterrno(mod, err));
132 }
133
134 int
topo_method_register(topo_mod_t * mod,tnode_t * node,const topo_method_t * mp)135 topo_method_register(topo_mod_t *mod, tnode_t *node, const topo_method_t *mp)
136 {
137 topo_imethod_t *imp;
138 const topo_method_t *meth;
139
140 /*
141 * Initialize module methods
142 */
143 for (meth = &mp[0]; meth->tm_name != NULL; meth++) {
144
145 topo_node_lock(node);
146 if (topo_method_lookup(node, meth->tm_name) != NULL) {
147 topo_node_unlock(node);
148 continue;
149 }
150
151 if (meth->tm_stability < TOPO_STABILITY_INTERNAL ||
152 meth->tm_stability > TOPO_STABILITY_MAX ||
153 meth->tm_func == NULL)
154 return (set_methregister_error(mod, node, NULL,
155 ETOPO_METHOD_INVAL));
156
157 imp = topo_mod_zalloc(mod, sizeof (topo_imethod_t));
158 if (imp == NULL)
159 return (set_methregister_error(mod, node, imp,
160 ETOPO_METHOD_NOMEM));
161
162 if ((imp->tim_name = topo_mod_strdup(mod, meth->tm_name))
163 == NULL)
164 return (set_methregister_error(mod, node, imp,
165 ETOPO_METHOD_NOMEM));
166
167 if ((imp->tim_desc = topo_mod_strdup(mod, meth->tm_desc))
168 == NULL)
169 return (set_methregister_error(mod, node, imp,
170 ETOPO_METHOD_NOMEM));
171
172
173 imp->tim_stability = meth->tm_stability;
174 imp->tim_version = meth->tm_version;
175 imp->tim_func = meth->tm_func;
176 imp->tim_mod = mod;
177
178 topo_list_append(&node->tn_methods, imp);
179 topo_node_unlock(node);
180
181 topo_dprintf(mod->tm_hdl, TOPO_DBG_MODSVC,
182 "registered module %s method "
183 "%s for %s=%d\n", mod->tm_name, imp->tim_name,
184 topo_node_name(node), topo_node_instance(node));
185
186 }
187
188 return (0);
189 }
190
191 void
topo_method_unregister(topo_mod_t * mod,tnode_t * node,const char * name)192 topo_method_unregister(topo_mod_t *mod, tnode_t *node, const char *name)
193 {
194 topo_imethod_t *mp;
195
196 topo_node_lock(node);
197 for (mp = topo_list_next(&node->tn_methods); mp != NULL;
198 mp = topo_list_next(mp)) {
199 if (strcmp(name, mp->tim_name) == 0)
200 break;
201 }
202
203 if (mp == NULL) {
204 topo_node_unlock(node);
205 return;
206 }
207
208 topo_list_delete(&node->tn_methods, mp);
209 topo_node_unlock(node);
210
211 if (mp->tim_name != NULL)
212 topo_mod_strfree(mod, mp->tim_name);
213 if (mp->tim_desc != NULL)
214 topo_mod_strfree(mod, mp->tim_desc);
215
216 topo_mod_free(mod, mp, sizeof (topo_imethod_t));
217 }
218
219 void
topo_method_unregister_all(topo_mod_t * mod,tnode_t * node)220 topo_method_unregister_all(topo_mod_t *mod, tnode_t *node)
221 {
222 topo_imethod_t *mp;
223
224 topo_node_lock(node);
225 while ((mp = topo_list_next(&node->tn_methods)) != NULL) {
226 topo_list_delete(&node->tn_methods, mp);
227 if (mp->tim_name != NULL)
228 topo_mod_strfree(mod, mp->tim_name);
229 if (mp->tim_desc != NULL)
230 topo_mod_strfree(mod, mp->tim_desc);
231 topo_mod_free(mod, mp, sizeof (topo_imethod_t));
232 }
233 topo_node_unlock(node);
234 }
235
236
237 int
topo_method_call(tnode_t * node,const char * method,topo_version_t version,nvlist_t * in,nvlist_t ** out,int * err)238 topo_method_call(tnode_t *node, const char *method,
239 topo_version_t version, nvlist_t *in, nvlist_t **out, int *err)
240 {
241 int rc, save;
242 topo_imethod_t *mp;
243
244 for (mp = topo_list_next(&node->tn_methods); mp != NULL;
245 mp = topo_list_next(mp)) {
246 if (strcmp(method, mp->tim_name) != 0)
247 continue;
248
249 if (version < mp->tim_version) {
250 *err = ETOPO_METHOD_VEROLD;
251 return (-1);
252 } else if (version > mp->tim_version) {
253 *err = ETOPO_METHOD_VERNEW;
254 return (-1);
255 }
256
257 topo_method_enter(mp);
258 save = mp->tim_mod->tm_errno;
259 mp->tim_mod->tm_errno = 0;
260 if ((rc = mp->tim_func(mp->tim_mod, node, version, in, out))
261 < 0) {
262 if (mp->tim_mod->tm_errno == 0)
263 *err = ETOPO_METHOD_FAIL;
264 else
265 *err = mp->tim_mod->tm_errno;
266 }
267 mp->tim_mod->tm_errno = save;
268 topo_method_exit(mp);
269
270 return (rc);
271
272 }
273
274 *err = ETOPO_METHOD_NOTSUP;
275 return (-1);
276 }
277
278 int
topo_method_invoke(tnode_t * node,const char * method,topo_version_t version,nvlist_t * in,nvlist_t ** out,int * err)279 topo_method_invoke(tnode_t *node, const char *method,
280 topo_version_t version, nvlist_t *in, nvlist_t **out, int *err)
281 {
282 int rc;
283
284 topo_node_hold(node);
285 rc = topo_method_call(node, method, version, in, out, err);
286 topo_node_rele(node);
287
288 return (rc);
289 }
290
291 struct sensor_errinfo
292 {
293 boolean_t se_predictive;
294 boolean_t se_nonrecov;
295 uint32_t se_src;
296 };
297
298 static boolean_t
topo_sensor_failed(int32_t type,uint32_t state,struct sensor_errinfo * seinfo)299 topo_sensor_failed(int32_t type, uint32_t state, struct sensor_errinfo *seinfo)
300 {
301 boolean_t failed;
302
303 failed = B_FALSE;
304 /*
305 * Unless the sensor explicitely says otherwise, all failures are
306 * non-recoverable, hard failures, coming from an unknown source.
307 */
308 seinfo->se_predictive = B_FALSE;
309 seinfo->se_nonrecov = B_TRUE;
310 seinfo->se_src = TOPO_SENSOR_ERRSRC_UNKNOWN;
311
312 switch (type) {
313 case TOPO_SENSOR_TYPE_THRESHOLD_STATE:
314 if (state & (TOPO_SENSOR_STATE_THRESH_LOWER_NONREC |
315 TOPO_SENSOR_STATE_THRESH_UPPER_NONREC)) {
316 failed = B_TRUE;
317 } else if (state & (TOPO_SENSOR_STATE_THRESH_LOWER_CRIT |
318 TOPO_SENSOR_STATE_THRESH_UPPER_CRIT)) {
319 failed = B_TRUE;
320 seinfo->se_nonrecov = B_FALSE;
321 }
322 break;
323
324 case TOPO_SENSOR_TYPE_POWER_SUPPLY:
325 if (state & TOPO_SENSOR_STATE_POWER_SUPPLY_PREDFAIL) {
326 failed = B_TRUE;
327 seinfo->se_predictive = B_TRUE;
328 seinfo->se_src = TOPO_SENSOR_ERRSRC_INTERNAL;
329 } else if (state & TOPO_SENSOR_STATE_POWER_SUPPLY_FAILURE) {
330 failed = B_TRUE;
331 seinfo->se_src = TOPO_SENSOR_ERRSRC_INTERNAL;
332 } else if (state &
333 (TOPO_SENSOR_STATE_POWER_SUPPLY_INPUT_LOST |
334 TOPO_SENSOR_STATE_POWER_SUPPLY_INPUT_RANGE |
335 TOPO_SENSOR_STATE_POWER_SUPPLY_INPUT_RANGE_PRES)) {
336 seinfo->se_src = TOPO_SENSOR_ERRSRC_EXTERNAL;
337 failed = B_TRUE;
338 }
339 break;
340
341 case TOPO_SENSOR_TYPE_GENERIC_FAILURE:
342 if (state & TOPO_SENSOR_STATE_GENERIC_FAIL_NONRECOV) {
343 failed = B_TRUE;
344 } else if (state & TOPO_SENSOR_STATE_GENERIC_FAIL_CRITICAL) {
345 failed = B_TRUE;
346 seinfo->se_nonrecov = B_FALSE;
347 }
348 break;
349
350 case TOPO_SENSOR_TYPE_GENERIC_OK:
351 if (state & TOPO_SENSOR_STATE_GENERIC_OK_DEASSERTED)
352 failed = B_TRUE;
353 break;
354 case TOPO_SENSOR_TYPE_GENERIC_PREDFAIL:
355 if (state & TOPO_SENSOR_STATE_GENERIC_PREDFAIL_ASSERTED) {
356 failed = B_TRUE;
357 seinfo->se_predictive = B_TRUE;
358 }
359 break;
360 }
361
362 return (failed);
363 }
364
365 static boolean_t
topo_spoof_apply(topo_hdl_t * thp,tnode_t * node,tnode_t * facnode,nvlist_t * in,uint32_t * state)366 topo_spoof_apply(topo_hdl_t *thp, tnode_t *node, tnode_t *facnode,
367 nvlist_t *in, uint32_t *state)
368 {
369 nvpair_t *elem = NULL;
370 nvlist_t *spoof, *rsrc = NULL;
371 char *fmrimatch, *fmri, *facmatch;
372 uint32_t spoof_state;
373 int err;
374
375 while ((elem = nvlist_next_nvpair(in, elem)) != NULL) {
376 if (nvpair_value_nvlist(elem, &spoof) != 0)
377 return (B_FALSE);
378
379 if (nvlist_lookup_string(spoof, ST_SPOOF_FMRI, &fmrimatch) !=
380 0 || nvlist_lookup_string(spoof, ST_SPOOF_SENSOR,
381 &facmatch) != 0 || nvlist_lookup_uint32(spoof,
382 ST_SPOOF_STATE, &spoof_state) != 0)
383 continue;
384
385 if (topo_node_resource(node, &rsrc, &err) != 0 ||
386 topo_fmri_nvl2str(thp, rsrc, &fmri, &err) != 0) {
387 nvlist_free(rsrc);
388 continue;
389 }
390 nvlist_free(rsrc);
391
392 if (fnmatch(fmrimatch, fmri, 0) == 0 &&
393 strcmp(facmatch, topo_node_name(facnode)) == 0) {
394 *state = spoof_state;
395 topo_hdl_strfree(thp, fmri);
396 return (B_TRUE);
397 }
398 topo_hdl_strfree(thp, fmri);
399 }
400 return (B_FALSE);
401 }
402
403 /*
404 * Determine whether there are any sensors indicating failure. This function
405 * is used internally to determine whether a given component is usable, as well
406 * by external monitoring software that wants additional information such as
407 * which sensors indicated failure. The return value is an nvlist of nvlists
408 * indexed by sensor name, each entry with the following contents:
409 *
410 * type, state, units, reading
411 *
412 * Identical to sensor node.
413 *
414 * nonrecov
415 *
416 * Boolean value that is set to indicate that the error is
417 * non-recoverable (the unit is out of service). The default is
418 * critical failure, which indicates a fault but the unit is still
419 * operating.
420 *
421 * injected
422 *
423 * Boolean value indicating that the sensor state was injected.
424 */
425 /*ARGSUSED*/
426 int
topo_method_sensor_failure(topo_mod_t * mod,tnode_t * node,topo_version_t version,nvlist_t * in,nvlist_t ** out)427 topo_method_sensor_failure(topo_mod_t *mod, tnode_t *node,
428 topo_version_t version, nvlist_t *in, nvlist_t **out)
429 {
430 topo_faclist_t faclist, *fp;
431 int err;
432 nvlist_t *nvl, *props, *propval, *tmp;
433 int ret = -1;
434 uint32_t type, state, units;
435 nvpair_t *elem;
436 double reading;
437 char *propname;
438 boolean_t has_reading, is_spoofed = B_FALSE;
439 struct sensor_errinfo seinfo;
440
441 if (topo_node_facility(mod->tm_hdl, node, TOPO_FAC_TYPE_SENSOR,
442 TOPO_FAC_TYPE_ANY, &faclist, &err) != 0)
443 return (topo_mod_seterrno(mod, ETOPO_METHOD_NOTSUP));
444
445 if (topo_mod_nvalloc(mod, &nvl, NV_UNIQUE_NAME) != 0)
446 goto error;
447
448 for (fp = topo_list_next(&faclist.tf_list); fp != NULL;
449 fp = topo_list_next(fp)) {
450 if (topo_prop_getpgrp(fp->tf_node, TOPO_PGROUP_FACILITY,
451 &props, &err) != 0) {
452 nvlist_free(nvl);
453 goto error;
454 }
455 type = state = units = 0;
456 reading = 0;
457 has_reading = B_FALSE;
458
459 elem = NULL;
460 while ((elem = nvlist_next_nvpair(props, elem)) != NULL) {
461 if (strcmp(nvpair_name(elem), TOPO_PROP_VAL) != 0 ||
462 nvpair_type(elem) != DATA_TYPE_NVLIST)
463 continue;
464
465 (void) nvpair_value_nvlist(elem, &propval);
466 if (nvlist_lookup_string(propval,
467 TOPO_PROP_VAL_NAME, &propname) != 0)
468 continue;
469
470 if (strcmp(propname, TOPO_FACILITY_TYPE) == 0) {
471 (void) nvlist_lookup_uint32(propval,
472 TOPO_PROP_VAL_VAL, &type);
473 } else if (strcmp(propname, TOPO_SENSOR_STATE) == 0) {
474 (void) nvlist_lookup_uint32(propval,
475 TOPO_PROP_VAL_VAL, &state);
476 } else if (strcmp(propname, TOPO_SENSOR_UNITS) == 0) {
477 (void) nvlist_lookup_uint32(propval,
478 TOPO_PROP_VAL_VAL, &units);
479 } else if (strcmp(propname, TOPO_SENSOR_READING) == 0) {
480 has_reading = B_TRUE;
481 (void) nvlist_lookup_double(propval,
482 TOPO_PROP_VAL_VAL, &reading);
483 }
484 }
485
486 if (in != NULL)
487 is_spoofed = topo_spoof_apply(mod->tm_hdl, node,
488 fp->tf_node, in, &state);
489
490 if (topo_sensor_failed(type, state, &seinfo)) {
491 tmp = NULL;
492 if (topo_mod_nvalloc(mod, &tmp, NV_UNIQUE_NAME) != 0 ||
493 nvlist_add_uint32(tmp, TOPO_FACILITY_TYPE,
494 type) != 0 ||
495 nvlist_add_uint32(tmp, TOPO_SENSOR_STATE,
496 state) != 0 ||
497 nvlist_add_uint32(tmp, TOPO_SENSOR_UNITS,
498 units) != 0 ||
499 nvlist_add_boolean_value(tmp,
500 "nonrecov", seinfo.se_nonrecov) != 0 ||
501 nvlist_add_boolean_value(tmp,
502 "predictive", seinfo.se_predictive) != 0 ||
503 nvlist_add_uint32(tmp, "source",
504 seinfo.se_src) != 0 ||
505 nvlist_add_boolean_value(nvl, "injected",
506 is_spoofed) != 0 ||
507 (has_reading && nvlist_add_double(tmp,
508 TOPO_SENSOR_READING, reading) != 0) ||
509 nvlist_add_nvlist(nvl, topo_node_name(fp->tf_node),
510 tmp) != 0) {
511 nvlist_free(props);
512 nvlist_free(tmp);
513 nvlist_free(nvl);
514 ret = topo_mod_seterrno(mod,
515 ETOPO_METHOD_NOMEM);
516 goto error;
517 }
518
519 nvlist_free(tmp);
520 }
521
522 nvlist_free(props);
523 is_spoofed = B_FALSE;
524 }
525
526 *out = nvl;
527 ret = 0;
528 error:
529 while ((fp = topo_list_next(&faclist.tf_list)) != NULL) {
530 topo_list_delete(&faclist.tf_list, fp);
531 topo_mod_free(mod, fp, sizeof (topo_faclist_t));
532 }
533 return (ret);
534 }
535