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