Home | History | Annotate | Download | only in eversholt
      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  * ipath.c -- instanced pathname module
     26  *
     27  * this module provides a cache of fully instantized component paths,
     28  * stored in a fairly compact format.
     29  */
     30 
     31 #include <stdio.h>
     32 #include <string.h>
     33 #include "alloc.h"
     34 #include "out.h"
     35 #include "lut.h"
     36 #include "tree.h"
     37 #include "ptree.h"
     38 #include "itree.h"
     39 #include "ipath.h"
     40 #include "ipath_impl.h"
     41 #include "stats.h"
     42 #include "eval.h"
     43 #include "config.h"
     44 
     45 static struct stats *Nipath;
     46 static struct stats *Nbytes;
     47 
     48 static struct lut *Ipaths;	/* the ipath cache itself */
     49 
     50 /*
     51  * ipath_init -- initialize the ipath module
     52  */
     53 void
     54 ipath_init(void)
     55 {
     56 	Nipath = stats_new_counter("ievent.nipath", "ipath cache entries", 1);
     57 	Nbytes = stats_new_counter("ievent.nbytes", "total cache size", 1);
     58 }
     59 
     60 /*
     61  * ipath_cmp -- compare two ipath entries
     62  *
     63  * since two ipaths containing the same components and instance
     64  * numbers always point to the same cache entry, they are equal
     65  * if their pointers are equal, so this function is not necessary
     66  * to test if two ipaths are same.  but when inserting a new ipath
     67  * into the cache, we must use the same lut comparison logic as when
     68  * we're searching for it, so this function must always match the
     69  * itree_epnamecmp() function's logic (see below) for searching the lut.
     70  */
     71 static int
     72 ipath_cmp(struct ipath *ipp1, struct ipath *ipp2)
     73 {
     74 	int i;
     75 
     76 	ASSERT(ipp1 != NULL);
     77 	ASSERT(ipp2 != NULL);
     78 
     79 	for (i = 0; ipp1[i].s != NULL && ipp2[i].s != NULL; i++)
     80 		if (ipp1[i].s != ipp2[i].s)
     81 			return (ipp2[i].s - ipp1[i].s);
     82 		else if (ipp1[i].i != ipp2[i].i)
     83 			return (ipp2[i].i - ipp1[i].i);
     84 
     85 	if (ipp1[i].s == NULL && ipp2[i].s == NULL)
     86 		return (0);
     87 	else if (ipp1[i].s == NULL)
     88 		return (1);
     89 	else
     90 		return (-1);
     91 }
     92 
     93 /*
     94  * ipath_epnamecmp -- compare an ipath with a struct node *epname list
     95  *
     96  * this function is used when searching the cache, allowing us to search
     97  * a lut full of ipaths by looking directly at a struct node *epname
     98  * (without having to convert it first).  the comparison logic here must
     99  * exactly match itree_cmp()'s logic (see above) so lut lookups use find
    100  * the same node as lut inserts.
    101  */
    102 static int
    103 ipath_epnamecmp(struct ipath *ipp, struct node *np)
    104 {
    105 	int i;
    106 
    107 	ASSERT(np != NULL);
    108 	ASSERT(ipp != NULL);
    109 
    110 	for (i = 0; ipp[i].s != NULL && np != NULL; i++, np = np->u.name.next) {
    111 		ASSERTinfo(np->t == T_NAME, ptree_nodetype2str(np->t));
    112 
    113 		if (ipp[i].s != np->u.name.s)
    114 			return (np->u.name.s - ipp[i].s);
    115 		else {
    116 			int inum;
    117 
    118 			if (np->u.name.child != NULL &&
    119 			    np->u.name.child->t == T_NUM)
    120 				inum = (int)np->u.name.child->u.ull;
    121 			else
    122 				config_getcompname(np->u.name.cp, NULL, &inum);
    123 
    124 			if (ipp[i].i != inum)
    125 				return (inum - ipp[i].i);
    126 		}
    127 	}
    128 
    129 	if (ipp[i].s == NULL && np == NULL)
    130 		return (0);
    131 	else if (ipp[i].s == NULL)
    132 		return (1);
    133 	else
    134 		return (-1);
    135 }
    136 
    137 /*
    138  * The following functions are only used in the "itree_create_dummy()" first
    139  * pass at itree creation. ipath_dummy() creates paths used in the itree (see
    140  * comment above add_event_dummy() for details). ipath_for_usednames() creates
    141  * a different set of paths using the full names from the propagations. These
    142  * are only used by ipath_dummy_lut() in order to set up the Usednames lut
    143  * correctly, which in turn allows conf propteries on any alement in those
    144  * names to be used in constraints.
    145  */
    146 struct lut *Usednames;
    147 
    148 void
    149 ipath_dummy_lut(struct arrow *arrowp)
    150 {
    151 	const struct ipath *ipp;
    152 
    153 	ipp = arrowp->head->myevent->ipp_un;
    154 	while (ipp->s != NULL) {
    155 		Usednames = lut_add(Usednames, (void *)ipp->s,
    156 		    (void *)ipp->s, NULL);
    157 		ipp++;
    158 	}
    159 	ipp = arrowp->tail->myevent->ipp_un;
    160 	while (ipp->s != NULL) {
    161 		Usednames = lut_add(Usednames, (void *)ipp->s,
    162 		    (void *)ipp->s, NULL);
    163 		ipp++;
    164 	}
    165 }
    166 
    167 struct ipath *
    168 ipath_dummy(struct node *np, struct ipath *ipp)
    169 {
    170 	struct ipath *ret;
    171 
    172 	ret = ipp;
    173 	while (ipp[1].s != NULL)
    174 		ipp++;
    175 	if (strcmp(ipp[0].s, np->u.name.last->u.name.s) == 0)
    176 		return (ret);
    177 
    178 	ret = MALLOC(sizeof (*ret) * 2);
    179 	ret[0].s = np->u.name.last->u.name.s;
    180 	ret[0].i = 0;
    181 	ret[1].s = NULL;
    182 	if ((ipp = lut_lookup(Ipaths, (void *)ret,
    183 	    (lut_cmp)ipath_cmp)) != NULL) {
    184 		FREE(ret);
    185 		return (ipp);
    186 	}
    187 	Ipaths = lut_add(Ipaths, (void *)ret, (void *)ret, (lut_cmp)ipath_cmp);
    188 	stats_counter_bump(Nipath);
    189 	stats_counter_add(Nbytes, 2 * sizeof (struct ipath));
    190 	return (ret);
    191 }
    192 
    193 struct ipath *
    194 ipath_for_usednames(struct node *np)
    195 {
    196 	struct ipath *ret, *ipp;
    197 	int i = 0;
    198 	struct node *np2;
    199 
    200 	for (np2 = np; np2 != NULL; np2 = np2->u.name.next)
    201 		i++;
    202 	ret = MALLOC(sizeof (*ret) * (i + 1));
    203 	for (i = 0, np2 = np; np2 != NULL; np2 = np2->u.name.next) {
    204 		ret[i].s = np2->u.name.s;
    205 		ret[i++].i = 0;
    206 	}
    207 	ret[i].s = NULL;
    208 	if ((ipp = lut_lookup(Ipaths, (void *)ret,
    209 	    (lut_cmp)ipath_cmp)) != NULL) {
    210 		FREE(ret);
    211 		return (ipp);
    212 	}
    213 	Ipaths = lut_add(Ipaths, (void *)ret, (void *)ret, (lut_cmp)ipath_cmp);
    214 	stats_counter_bump(Nipath);
    215 	stats_counter_add(Nbytes, (i + 1) * sizeof (struct ipath));
    216 	return (ret);
    217 }
    218 
    219 /*
    220  * ipath -- find instanced path in cache, or add it if necessary
    221  */
    222 const struct ipath *
    223 ipath(struct node *np)
    224 {
    225 	struct ipath *ret;
    226 	int count;
    227 	struct node *namep;
    228 	int i;
    229 
    230 	if ((ret = lut_lookup(Ipaths, (void *)np,
    231 	    (lut_cmp)ipath_epnamecmp)) != NULL)
    232 		return (ret);	/* already in cache */
    233 
    234 	/*
    235 	 * not in cache, make new cache entry.
    236 	 * start by counting the length of the name.
    237 	 */
    238 	count = 0;
    239 	namep = np;
    240 	while (namep != NULL) {
    241 		ASSERTinfo(namep->t == T_NAME, ptree_nodetype2str(namep->t));
    242 		count++;
    243 		namep = namep->u.name.next;
    244 	}
    245 
    246 	ASSERT(count > 0);
    247 
    248 	/* allocate array for name and last NULL entry */
    249 	ret = MALLOC(sizeof (*ret) * (count + 1));
    250 	ret[count].s = NULL;
    251 
    252 	/* fill in ipath entry */
    253 	namep = np;
    254 	i = 0;
    255 	while (namep != NULL) {
    256 		ASSERT(i < count);
    257 		ret[i].s = namep->u.name.s;
    258 		if (namep->u.name.child != NULL &&
    259 		    namep->u.name.child->t == T_NUM)
    260 			ret[i].i = (int)namep->u.name.child->u.ull;
    261 		else
    262 			config_getcompname(namep->u.name.cp, NULL, &ret[i].i);
    263 		i++;
    264 		namep = namep->u.name.next;
    265 	}
    266 
    267 	/* add it to the cache */
    268 	Ipaths = lut_add(Ipaths, (void *)ret, (void *)ret,
    269 	    (lut_cmp)ipath_cmp);
    270 
    271 	stats_counter_bump(Nipath);
    272 	stats_counter_add(Nbytes, (count + 1) * sizeof (struct ipath));
    273 
    274 	return (ret);
    275 }
    276 
    277 /*
    278  * ipath2str -- convert ename and ipath to class@path string
    279  *
    280  * if both ename and ipp are provided (non-NULL), the resulting string
    281  * will be "class@path".  otherwise, the string will just contain the
    282  * event class name (e.g. "ereport.io.pci.device") or just the path
    283  * name (e.g. "mothboard0/hostbridge0/pcibus1/pcidev0/pcifn1"), depending
    284  * on which argument is non-NULL.
    285  */
    286 char *
    287 ipath2str(const char *ename, const struct ipath *ipp)
    288 {
    289 	int i;
    290 	size_t len = 0;
    291 	char *ret;
    292 	char *cp;
    293 
    294 	/* count up length of class string */
    295 	if (ename != NULL)
    296 		len += strlen(ename);
    297 
    298 	/* count up length of path string, including slash separators */
    299 	if (ipp != NULL) {
    300 		for (i = 0; ipp[i].s != NULL; i++) {
    301 			/* add slash separator, but no leading slash */
    302 			if (i != 0)
    303 				len++;
    304 			len += snprintf(NULL, 0, "%s%d", ipp[i].s, ipp[i].i);
    305 		}
    306 	}
    307 
    308 	if (ename != NULL && ipp != NULL)
    309 		len++;	/* room for '@' */
    310 
    311 	len++;	/* room for final '\0' */
    312 
    313 	cp = ret = MALLOC(len);
    314 
    315 	if (ename != NULL) {
    316 		/* construct class string */
    317 		(void) strcpy(cp, ename);
    318 		cp += strlen(cp);
    319 	}
    320 
    321 	/* if doing both strings, put '@' between them */
    322 	if (ename != NULL && ipp != NULL)
    323 		*cp++ = '@';
    324 
    325 	if (ipp != NULL) {
    326 		/* construct path string */
    327 		for (i = 0; ipp[i].s != NULL; i++) {
    328 			if (i != 0)
    329 				*cp++ = '/';
    330 			(void) snprintf(cp, &ret[len] - cp, "%s%d",
    331 			    ipp[i].s, ipp[i].i);
    332 			cp += strlen(cp);
    333 		}
    334 	}
    335 
    336 	*cp++ = '\0';
    337 
    338 	return (ret);
    339 }
    340 
    341 /*
    342  * ipath2strlen -- calculate the len of what ipath2str() would return
    343  */
    344 size_t
    345 ipath2strlen(const char *ename, const struct ipath *ipp)
    346 {
    347 	int i;
    348 	size_t len = 0;
    349 
    350 	/* count up length of class string */
    351 	if (ename != NULL)
    352 		len += strlen(ename);
    353 
    354 	/* count up length of path string, including slash separators */
    355 	if (ipp != NULL) {
    356 		for (i = 0; ipp[i].s != NULL; i++) {
    357 			/* add slash separator, but no leading slash */
    358 			if (i != 0)
    359 				len++;
    360 			len += snprintf(NULL, 0, "%s%d", ipp[i].s, ipp[i].i);
    361 		}
    362 	}
    363 
    364 	if (ename != NULL && ipp != NULL)
    365 		len++;	/* room for '@' */
    366 
    367 	return (len);
    368 }
    369 
    370 /*
    371  * ipath_print -- print out an ename, ipath, or both with '@' between them
    372  */
    373 void
    374 ipath_print(int flags, const char *ename, const struct ipath *ipp)
    375 {
    376 	if (ename != NULL) {
    377 		out(flags|O_NONL, ename);
    378 		if (ipp != NULL)
    379 			out(flags|O_NONL, "@");
    380 	}
    381 	if (ipp != NULL) {
    382 		char *sep = "";
    383 
    384 		while (ipp->s != NULL) {
    385 			out(flags|O_NONL, "%s%s%d", sep, ipp->s, ipp->i);
    386 			ipp++;
    387 			sep = "/";
    388 		}
    389 	}
    390 }
    391 
    392 /*ARGSUSED*/
    393 static void
    394 ipath_destructor(void *left, void *right, void *arg)
    395 {
    396 	struct ipath *ipp = (struct ipath *)right;
    397 
    398 	FREE(ipp);
    399 }
    400 
    401 /*
    402  * ipath_fini -- free the ipath cache
    403  */
    404 void
    405 ipath_fini(void)
    406 {
    407 	lut_free(Ipaths, ipath_destructor, NULL);
    408 	Ipaths = NULL;
    409 	lut_free(Usednames, NULL, NULL);
    410 	Usednames = NULL;
    411 
    412 	if (Nipath) {
    413 		stats_delete(Nipath);
    414 		Nipath = NULL;
    415 	}
    416 
    417 	if (Nbytes) {
    418 		stats_delete(Nbytes);
    419 		Nbytes = NULL;
    420 	}
    421 }
    422