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