Home | History | Annotate | Download | only in common
      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