usr/src/lib/fm/topo/libtopo/common/topo_prop.c
author cindi
Sat, 11 Feb 2006 15:36:52 -0800
changeset 1414 b4126407ac5b
child 3062 46d280f5351d
permissions -rw-r--r--
PSARC 2006/020 FMA for Athlon 64 and Opteron Processors PSARC 2006/028 eversholt language enhancements 6181364 Eversholt needs method to revise value of a fault's property 6183842 eft can construct extra propagations in the instance tree 6187143 eversholt needs to use fmd_case_add_serd() to add counted ereports against open case 6232253 wildcarding may not pick up matches buried in config path 6284455 eversholt wildcarding and vertical expansion have trouble working together 6298484 properties are not auto-converting to integers in eversholt constraints 6298972 eversholt should be able to mark faults as no-message like the cpumem DE 6298974 nested SERD engines don't work 6298981 eft memory usage could improve by caching common constraint expressions 6323319 call() is not allowing string-valued returns 6323322 a global variable should be allowed as the RHS of an nvpair 6323393 eversholt caches a little too much info when caching constraints 6323554 eversholt type conversion can cause core dump 6328144 libexacct leaks like a really big sieve when faced with non-exacct input 6331093 payloadprop should be able to read and interpret hc scheme fmris 6332245 payloadprop() returns cached value from existing FME when not appropriate 6333617 eversholt should have way to check if a global is defined 6346926 eversholt needs a way to maintain diagnosis statistics 6359264 Provide FMA support for AMD64 processors 6363503 Can not register error handler callbacks for root node 6366821 cpu scheme serial number should be a string 6367031 eft.so leaks memory 6370284 cpumem-diagnosis checks the asru version against FM_EREPORT_VERSION instead of FM_CPU_SCHEME_VERSION 6377319 eft could close cases for resources already in the faulty state 6379498 fmd dies on assertion failure when repairing an fmd module 6381022 fmd_case_insert_event() should reject duplicates and save memory

/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#pragma ident	"%Z%%M%	%I%	%E% SMI"

#include <strings.h>
#include <assert.h>
#include <fm/libtopo.h>
#include <topo_prop.h>
#include <topo_string.h>
#include <topo_alloc.h>
#include <topo_error.h>

static topo_pgroup_t *
pgroup_get(tnode_t *node, const char *pgname)
{
	topo_pgroup_t *pg;
	/*
	 * Check for an existing pgroup
	 */
	for (pg = topo_list_next(&node->tn_pgroups); pg != NULL;
	    pg = topo_list_next(pg)) {
		if (strcmp(pg->tpg_name, pgname) == 0) {
			return (pg);
		}
	}

	return (NULL);
}

static topo_propval_t *
propval_get(topo_pgroup_t *pg, const char *pname)
{
	topo_proplist_t *pvl;

	for (pvl = topo_list_next(&pg->tpg_pvals); pvl != NULL;
	    pvl = topo_list_next(pvl)) {
		if (strcmp(pvl->tp_pval->tp_name, pname) == 0)
			return (pvl->tp_pval);
	}

	return (NULL);
}

static topo_propval_t *
topo_prop_get(tnode_t *node, const char *pgname, const char *pname, int *err)
{
	topo_pgroup_t *pg = NULL;
	topo_propval_t *pv = NULL;

	if ((pg = pgroup_get(node, pgname)) == NULL) {
		*err = ETOPO_PROP_NOENT;
		return (NULL);
	}

	if ((pv = propval_get(pg, pname)) == NULL) {
		*err = ETOPO_PROP_NOENT;
		return (NULL);
	}

	return (pv);
}

static int
prop_val_add(nvlist_t *nvl, topo_propval_t *pv)
{
	switch (pv->tp_type) {
		case TOPO_TYPE_INT32:
			return (nvlist_add_int32(nvl, TOPO_PROP_VAL_VAL,
			    pv->tp_u.tp_int32));
		case TOPO_TYPE_UINT32:
			return (nvlist_add_uint32(nvl, TOPO_PROP_VAL_VAL,
			    pv->tp_u.tp_uint32));
		case TOPO_TYPE_INT64:
			return (nvlist_add_int64(nvl, TOPO_PROP_VAL_VAL,
			    pv->tp_u.tp_int64));
		case TOPO_TYPE_UINT64:
			return (nvlist_add_uint64(nvl, TOPO_PROP_VAL_VAL,
			    pv->tp_u.tp_uint64));
		case TOPO_TYPE_STRING:
			return (nvlist_add_string(nvl, TOPO_PROP_VAL_VAL,
			    pv->tp_u.tp_string));
		case TOPO_TYPE_FMRI:
			return (nvlist_add_nvlist(nvl, TOPO_PROP_VAL_VAL,
			    pv->tp_u.tp_fmri));
		default:
			return (ETOPO_PROP_TYPE);
	}
}

nvlist_t *
get_all_seterror(topo_hdl_t *thp, nvlist_t *nvl, int err)
{
	if (nvl != NULL)
		nvlist_free(nvl);

	(void) topo_hdl_seterrno(thp, err);

	return (NULL);
}

nvlist_t *
topo_prop_get_all(topo_hdl_t *thp, tnode_t *node)
{
	int err;
	nvlist_t *nvl, *pgnvl, *pvnvl;
	topo_pgroup_t *pg;
	topo_propval_t *pv;
	topo_proplist_t *pvl;

	if (topo_hdl_nvalloc(thp, &nvl, 0) != 0) {
		return (get_all_seterror(thp, NULL, ETOPO_NOMEM));
	}

	for (pg = topo_list_next(&node->tn_pgroups); pg != NULL;
	    pg = topo_list_next(pg)) {
		err = 0;
		if (topo_hdl_nvalloc(thp, &pgnvl, 0) != 0)
			return (get_all_seterror(thp, nvl, ETOPO_NOMEM));

		if ((err = nvlist_add_string(pgnvl, TOPO_PROP_GROUP_NAME,
		    pg->tpg_name)) != 0)
			return (get_all_seterror(thp, nvl, err));

		for (pvl = topo_list_next(&pg->tpg_pvals); pvl != NULL;
		    pvl = topo_list_next(pvl)) {

			pv = pvl->tp_pval;
			if (topo_hdl_nvalloc(thp, &pvnvl, 0)
			    != 0) {
				nvlist_free(pgnvl);
				return (get_all_seterror(thp, nvl,
				    ETOPO_NOMEM));
			}
			if ((err = nvlist_add_string(pvnvl, TOPO_PROP_VAL_NAME,
			    pv->tp_name)) != 0) {
				nvlist_free(pgnvl);
				nvlist_free(pvnvl);
				return (get_all_seterror(thp, nvl, err));
			}
			if ((err = prop_val_add(pvnvl, pv)) != 0) {
				nvlist_free(pgnvl);
				nvlist_free(pvnvl);
				return (get_all_seterror(thp, nvl, err));
			}
			if ((err = nvlist_add_nvlist(pgnvl, TOPO_PROP_VAL,
			    pvnvl)) != 0) {
				nvlist_free(pgnvl);
				nvlist_free(pvnvl);
				return (get_all_seterror(thp, nvl, err));
			}

			nvlist_free(pvnvl);
		}
		if ((err = nvlist_add_nvlist(nvl, TOPO_PROP_GROUP, pgnvl))
		    != 0) {
			nvlist_free(pgnvl);
			return (get_all_seterror(thp, nvl, err));
		}

		nvlist_free(pgnvl);
	}

	return (nvl);
}

static int
get_seterror(tnode_t *node, int *errp, int err)
{
	topo_node_unlock(node);
	*errp = err;
	return (-1);
}

int
topo_prop_get_int32(tnode_t *node, const char *pgname, const char *pname,
    int32_t *val, int *err)
{
	topo_propval_t *pv;

	topo_node_lock(node);
	if ((pv = topo_prop_get(node, pgname, pname, err))
	    == NULL)
		return (get_seterror(node, err, *err));

	if (pv->tp_type != TOPO_TYPE_INT32)
		return (get_seterror(node, err, ETOPO_PROP_TYPE));

	*val = pv->tp_u.tp_int32;

	topo_node_unlock(node);

	return (0);
}

int
topo_prop_get_uint32(tnode_t *node, const char *pgname, const char *pname,
    uint32_t *val, int *err)
{
	topo_propval_t *pv;

	topo_node_lock(node);
	if ((pv = topo_prop_get(node, pgname, pname, err))
	    == NULL)
		return (get_seterror(node, err, *err));

	if (pv->tp_type != TOPO_TYPE_UINT32)
		return (get_seterror(node, err, ETOPO_PROP_TYPE));

	*val = pv->tp_u.tp_uint32;

	topo_node_unlock(node);

	return (0);
}

int
topo_prop_get_int64(tnode_t *node, const char *pgname, const char *pname,
    int64_t *val, int *err)
{
	topo_propval_t *pv;

	topo_node_lock(node);
	if ((pv = topo_prop_get(node, pgname, pname, err))
	    == NULL)
		return (get_seterror(node, err, *err));

	if (pv->tp_type != TOPO_TYPE_INT64)
		return (get_seterror(node, err, ETOPO_PROP_TYPE));

	*val = pv->tp_u.tp_int64;

	topo_node_unlock(node);

	return (0);
}

int
topo_prop_get_uint64(tnode_t *node, const char *pgname, const char *pname,
    uint64_t *val, int *err)
{
	topo_propval_t *pv;

	topo_node_lock(node);
	if ((pv = topo_prop_get(node, pgname, pname, err))
	    == NULL)
		return (get_seterror(node, err, *err));

	if (pv->tp_type != TOPO_TYPE_UINT64)
		return (get_seterror(node, err, ETOPO_PROP_TYPE));

	*val = pv->tp_u.tp_int64;

	topo_node_unlock(node);

	return (0);
}

int
topo_prop_get_string(tnode_t *node, const char *pgname, const char *pname,
    char **val, int *err)
{
	topo_propval_t *pv;

	topo_node_lock(node);
	if ((pv = topo_prop_get(node, pgname, pname, err)) == NULL)
		return (get_seterror(node, err, *err));

	if (pv->tp_type != TOPO_TYPE_STRING)
		return (get_seterror(node, err, ETOPO_PROP_TYPE));

	if ((*val = topo_hdl_strdup(node->tn_hdl, pv->tp_u.tp_string))
	    == NULL)
		return (get_seterror(node, err, ETOPO_NOMEM));

	topo_node_unlock(node);

	return (0);
}

int
topo_prop_get_fmri(tnode_t *node, const char *pgname, const char *pname,
    nvlist_t **val, int *err)
{
	topo_propval_t *pv;

	topo_node_lock(node);
	if ((pv = topo_prop_get(node, pgname, pname, err)) == NULL)
		return (get_seterror(node, err, *err));

	if (pv->tp_type != TOPO_TYPE_FMRI)
		return (get_seterror(node, err, ETOPO_PROP_TYPE));

	if (topo_hdl_nvdup(node->tn_hdl, pv->tp_u.tp_fmri, val) < 0)
		return (get_seterror(node, err, ETOPO_NOMEM));

	topo_node_unlock(node);

	return (0);
}

static void
topo_propval_strfree(topo_propval_t *pv)
{
	topo_hdl_strfree(pv->tp_hdl, pv->tp_u.tp_string);
}

static void
topo_propval_nvlfree(topo_propval_t *pv)
{
	nvlist_free(pv->tp_u.tp_fmri);
}

static int
set_seterror(tnode_t *node, int *errp, int err)
{
	topo_node_unlock(node);

	*errp = err;

	return (-1);
}

static int
topo_prop_set(tnode_t *node, const char *pgname, const char *pname,
    topo_type_t type, int flag, void *val, int *err)
{
	topo_hdl_t *thp = node->tn_hdl;
	topo_pgroup_t *pg;
	topo_propval_t *pv;
	topo_proplist_t *pvl;

	topo_node_lock(node);
	if ((pg = pgroup_get(node, pgname)) == NULL)
		return (set_seterror(node, err, ETOPO_PROP_NOENT));

	if ((pv = propval_get(pg, pname)) != NULL) {
		if (pv->tp_type != type)
			return (set_seterror(node, err, ETOPO_PROP_TYPE));
		else if (pv->tp_flag == TOPO_PROP_SET_ONCE)
			return (set_seterror(node, err, ETOPO_PROP_DEFD));
	} else {
		/*
		 * Property values may be a shared resources among
		 * different nodes.  We will allocate resources
		 * on a per-handle basis.
		 */
		if ((pvl = topo_hdl_zalloc(thp, sizeof (topo_proplist_t)))
		    == NULL)
			return (set_seterror(node, err, ETOPO_NOMEM));

		if ((pv = topo_hdl_zalloc(thp, sizeof (topo_propval_t)))
		    == NULL) {
			topo_hdl_free(thp, pvl, sizeof (topo_proplist_t));
			return (set_seterror(node, err, ETOPO_NOMEM));
		}
		if ((pv->tp_name = topo_hdl_strdup(thp, pname))
		    == NULL) {
			topo_hdl_free(thp, pvl, sizeof (topo_proplist_t));
			topo_hdl_free(thp, pv, sizeof (topo_propval_t));
			return (set_seterror(node, err, ETOPO_NOMEM));
		}
		pv->tp_flag = flag;
		pv->tp_type = type;
		pv->tp_hdl = thp;
		topo_prop_hold(pv);
		pvl->tp_pval = pv;
		topo_list_append(&pg->tpg_pvals, pvl);


	}

	switch (type) {
		case TOPO_TYPE_INT32:
			pv->tp_u.tp_int32 = *(int32_t *)val;
			break;
		case TOPO_TYPE_UINT32:
			pv->tp_u.tp_uint32 = *(uint32_t *)val;
			break;
		case TOPO_TYPE_INT64:
			pv->tp_u.tp_int64 = *(int64_t *)val;
			break;
		case TOPO_TYPE_UINT64:
			pv->tp_u.tp_uint64 = *(uint64_t *)val;
			break;
		case TOPO_TYPE_STRING:
			pv->tp_u.tp_string = topo_hdl_strdup(thp, (char *)val);
			if (pv->tp_u.tp_string == NULL)
				return (set_seterror(node, err, ETOPO_NOMEM));
			pv->tp_free = topo_propval_strfree;
			break;
		case TOPO_TYPE_FMRI:
			if (topo_hdl_nvdup(thp,
			    (nvlist_t *)val, &pv->tp_u.tp_fmri) < 0)
				return (set_seterror(node, err, ETOPO_NOMEM));
			pv->tp_free = topo_propval_nvlfree;
			break;
		default:
			return (set_seterror(node, err, ETOPO_PROP_TYPE));
	}

	topo_node_unlock(node);

	return (0);
}

int
topo_prop_set_int32(tnode_t *node, const char *pgname, const char *pname,
    int flag, int32_t val, int *err)
{
	return (topo_prop_set(node, pgname, pname, TOPO_TYPE_INT32, flag,
	    &val, err));
}

int
topo_prop_set_uint32(tnode_t *node, const char *pgname, const char *pname,
    int flag, uint32_t val, int *err)
{
	return (topo_prop_set(node, pgname, pname, TOPO_TYPE_UINT32, flag,
	    &val, err));
}

int
topo_prop_set_int64(tnode_t *node, const char *pgname, const char *pname,
    int flag, int64_t val, int *err)
{
	return (topo_prop_set(node, pgname, pname, TOPO_TYPE_INT64, flag,
	    &val, err));
}

int
topo_prop_set_uint64(tnode_t *node, const char *pgname, const char *pname,
    int flag, uint64_t val, int *err)
{
	return (topo_prop_set(node, pgname, pname, TOPO_TYPE_UINT64, flag,
	    &val, err));
}

int
topo_prop_set_string(tnode_t *node, const char *pgname, const char *pname,
    int flag, const char *val, int *err)
{
	return (topo_prop_set(node, pgname, pname, TOPO_TYPE_STRING, flag,
	    (void *)val, err));
}

int
topo_prop_set_fmri(tnode_t *node, const char *pgname, const char *pname,
    int flag, const nvlist_t *fmri, int *err)
{
	return (topo_prop_set(node, pgname, pname, TOPO_TYPE_FMRI, flag,
	    (void *)fmri, err));
}

static int
inherit_seterror(tnode_t *node, int *errp, int err)
{
	topo_node_unlock(node);
	topo_node_unlock(node->tn_parent);

	*errp = err;

	return (-1);
}

int
topo_prop_inherit(tnode_t *node, const char *pgname, const char *name, int *err)
{
	topo_hdl_t *thp = node->tn_hdl;
	tnode_t *pnode = node->tn_parent;
	topo_pgroup_t *pg;
	topo_propval_t *pv;
	topo_proplist_t *pvl;

	topo_node_lock(pnode);
	topo_node_lock(node);
	/*
	 * Check for an existing property group and prop val
	 */
	if ((pg = pgroup_get(pnode, pgname)) == NULL)
		return (inherit_seterror(node, err, ETOPO_PROP_NOENT));

	if ((pv = propval_get(pg, name)) == NULL)
		return (inherit_seterror(node, err, ETOPO_PROP_NOENT));

	/*
	 * Can this propval be inherited?
	 */
	if (pv->tp_flag != TOPO_PROP_SET_ONCE)
		return (inherit_seterror(node, err, ETOPO_PROP_NOINHERIT));

	/*
	 * Property group should already exist: bump the ref count for this
	 * propval and add it to the node's property group
	 */
	if ((pg = pgroup_get(node, pgname)) == NULL)
		return (inherit_seterror(node, err, ETOPO_PROP_NOENT));

	if ((pvl = topo_hdl_zalloc(thp, sizeof (topo_proplist_t)))
	    == NULL)
		return (inherit_seterror(node, err, ETOPO_NOMEM));

	topo_prop_hold(pv);
	pvl->tp_pval = pv;
	topo_list_append(&pg->tpg_pvals, pvl);

	topo_node_unlock(node);
	topo_node_unlock(pnode);

	return (0);
}

int
topo_prop_stability(tnode_t *node, const char *pgname, topo_stability_t *stab)
{
	topo_pgroup_t *pg;

	for (pg = topo_list_next(&node->tn_pgroups); pg != NULL;
	    pg = topo_list_next(pg)) {
		if (strcmp(pgname, pg->tpg_name) == 0) {
			*stab = pg->tpg_stability;
			return (0);
		}
	}

	return (-1);
}

int
topo_pgroup_create(tnode_t *node, const char *pname, topo_stability_t stab,
    int *err)
{
	topo_pgroup_t *pg;

	*err = 0;

	/*
	 * Check for an existing pgroup
	 */
	for (pg = topo_list_next(&node->tn_pgroups); pg != NULL;
	    pg = topo_list_next(pg)) {
		if (strcmp(pg->tpg_name, pname) == 0) {
			*err = ETOPO_PROP_DEFD;
			return (-1);
		}
	}

	if ((pg = topo_hdl_zalloc(node->tn_hdl,
	    sizeof (topo_pgroup_t))) == NULL) {
		*err = ETOPO_NOMEM;
		return (-1);
	}

	if ((pg->tpg_name = topo_hdl_strdup(node->tn_hdl, pname)) == NULL) {
		topo_hdl_free(node->tn_hdl, pg, sizeof (topo_pgroup_t));
		*err = ETOPO_NOMEM;
		return (-1);
	}

	pg->tpg_stability = stab;

	topo_list_append(&node->tn_pgroups, pg);

	return (0);
}

void
topo_pgroup_destroy(tnode_t *node, const char *pname)
{
	topo_hdl_t *thp = node->tn_hdl;
	topo_pgroup_t *pg;
	topo_proplist_t *pvl;

	topo_node_lock(node);
	for (pg = topo_list_next(&node->tn_pgroups); pg != NULL;
	    pg = topo_list_next(pg)) {
		if (strcmp(pg->tpg_name, pname) == 0) {
			break;
		}
	}

	if (pg == NULL) {
		topo_node_unlock(node);
		return;
	}

	while ((pvl = topo_list_next(&pg->tpg_list)) != NULL) {
		topo_list_delete(&pg->tpg_pvals, pvl);
		topo_prop_rele(pvl->tp_pval);
		topo_hdl_free(thp, pvl, sizeof (topo_proplist_t));
	}

	topo_list_delete(&node->tn_pgroups, pg);

	if (pg->tpg_name != NULL)
		topo_hdl_strfree(thp, pg->tpg_name);
	topo_hdl_free(thp, pg, sizeof (topo_pgroup_t));

	topo_node_unlock(node);
}

void
topo_pgroup_destroy_all(tnode_t *node)
{
	topo_hdl_t *thp = node->tn_hdl;
	topo_pgroup_t *pg;
	topo_proplist_t *pvl;

	topo_node_lock(node);
	while ((pg = topo_list_next(&node->tn_pgroups)) != NULL) {
		while ((pvl = topo_list_next(&pg->tpg_pvals)) != NULL) {
			topo_list_delete(&pg->tpg_pvals, pvl);
			topo_prop_rele(pvl->tp_pval);
			topo_hdl_free(thp, pvl, sizeof (topo_proplist_t));
		}

		topo_list_delete(&node->tn_pgroups, pg);

		if (pg->tpg_name != NULL)
			topo_hdl_strfree(thp, pg->tpg_name);
		topo_hdl_free(thp, pg, sizeof (topo_pgroup_t));
	}
	topo_node_unlock(node);
}
static void
topo_propval_destroy(topo_propval_t *pv)
{
	topo_hdl_t *thp = pv->tp_hdl;

	if (pv->tp_name != NULL)
		topo_hdl_strfree(thp, pv->tp_name);

	if (pv->tp_free != NULL)
		pv->tp_free(pv);

	topo_hdl_free(thp, pv, sizeof (topo_propval_t));
}

void
topo_prop_hold(topo_propval_t *pv)
{
	pv->tp_refs++;
}

void
topo_prop_rele(topo_propval_t *pv)
{
	pv->tp_refs--;

	assert(pv->tp_refs >= 0);

	if (pv->tp_refs == 0)
		topo_propval_destroy(pv);
}