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