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, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  *
26  * ipath.c -- instanced pathname module
27  *
28  * this module provides a cache of fully instantized component paths,
29  * stored in a fairly compact format.
30  */
31 
32 #pragma ident	"%Z%%M%	%I%	%E% SMI"
33 
34 #include <stdio.h>
35 #include <string.h>
36 #include "alloc.h"
37 #include "out.h"
38 #include "lut.h"
39 #include "tree.h"
40 #include "ptree.h"
41 #include "itree.h"
42 #include "ipath.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 /* an ipath cache entry is an array of these, with s==NULL at the end */
51 struct ipath {
52 	const char *s;	/* component name (in stable) */
53 	int i;		/* instance number */
54 };
55 
56 static struct lut *Ipaths;	/* the ipath cache itself */
57 
58 /*
59  * ipath_init -- initialize the ipath module
60  */
61 void
62 ipath_init(void)
63 {
64 	Nipath = stats_new_counter("ievent.nipath", "ipath cache entries", 1);
65 	Nbytes = stats_new_counter("ievent.nbytes", "total cache size", 1);
66 }
67 
68 /*
69  * ipath_cmp -- compare two ipath entries
70  *
71  * since two ipaths containing the same components and instance
72  * numbers always point to the same cache entry, they are equal
73  * if their pointers are equal, so this function is not necessary
74  * to test if two ipaths are same.  but when inserting a new ipath
75  * into the cache, we must use the same lut comparison logic as when
76  * we're searching for it, so this function must always match the
77  * itree_epnamecmp() function's logic (see below) for searching the lut.
78  */
79 static int
80 ipath_cmp(struct ipath *ipp1, struct ipath *ipp2)
81 {
82 	int i;
83 
84 	ASSERT(ipp1 != NULL);
85 	ASSERT(ipp2 != NULL);
86 
87 	for (i = 0; ipp1[i].s != NULL && ipp2[i].s != NULL; i++)
88 		if (ipp1[i].s != ipp2[i].s)
89 			return (ipp2[i].s - ipp1[i].s);
90 		else if (ipp1[i].i != ipp2[i].i)
91 			return (ipp2[i].i - ipp1[i].i);
92 
93 	if (ipp1[i].s == NULL && ipp2[i].s == NULL)
94 		return (0);
95 	else if (ipp1[i].s == NULL)
96 		return (1);
97 	else
98 		return (-1);
99 }
100 
101 /*
102  * ipath_epnamecmp -- compare an ipath with a struct node *epname list
103  *
104  * this function is used when searching the cache, allowing us to search
105  * a lut full of ipaths by looking directly at a struct node *epname
106  * (without having to convert it first).  the comparison logic here must
107  * exactly match itree_cmp()'s logic (see above) so lut lookups use find
108  * the same node as lut inserts.
109  */
110 static int
111 ipath_epnamecmp(struct ipath *ipp, struct node *np)
112 {
113 	int i;
114 
115 	ASSERT(np != NULL);
116 	ASSERT(ipp != NULL);
117 
118 	for (i = 0; ipp[i].s != NULL && np != NULL; i++, np = np->u.name.next) {
119 		ASSERTinfo(np->t == T_NAME, ptree_nodetype2str(np->t));
120 
121 		if (ipp[i].s != np->u.name.s)
122 			return (np->u.name.s - ipp[i].s);
123 		else {
124 			int inum;
125 
126 			if (np->u.name.child != NULL &&
127 			    np->u.name.child->t == T_NUM)
128 				inum = (int)np->u.name.child->u.ull;
129 			else
130 				config_getcompname(np->u.name.cp, NULL, &inum);
131 
132 			if (ipp[i].i != inum)
133 				return (inum - ipp[i].i);
134 		}
135 	}
136 
137 	if (ipp[i].s == NULL && np == NULL)
138 		return (0);
139 	else if (ipp[i].s == NULL)
140 		return (1);
141 	else
142 		return (-1);
143 }
144 
145 /*
146  * ipath -- find instanced path in cache, or add it if necessary
147  */
148 const struct ipath *
149 ipath(struct node *np)
150 {
151 	struct ipath *ret;
152 	int count;
153 	struct node *namep;
154 	int i;
155 
156 	if ((ret = lut_lookup(Ipaths, (void *)np,
157 	    (lut_cmp)ipath_epnamecmp)) != NULL)
158 		return (ret);	/* already in cache */
159 
160 	/*
161 	 * not in cache, make new cache entry.
162 	 * start by counting the length of the name.
163 	 */
164 	count = 0;
165 	namep = np;
166 	while (namep != NULL) {
167 		ASSERTinfo(namep->t == T_NAME, ptree_nodetype2str(namep->t));
168 		count++;
169 		namep = namep->u.name.next;
170 	}
171 
172 	ASSERT(count > 0);
173 
174 	/* allocate array for name and last NULL entry */
175 	ret = MALLOC(sizeof (*ret) * (count + 1));
176 	ret[count].s = NULL;
177 
178 	/* fill in ipath entry */
179 	namep = np;
180 	i = 0;
181 	while (namep != NULL) {
182 		ASSERT(i < count);
183 		ret[i].s = namep->u.name.s;
184 		if (namep->u.name.child != NULL &&
185 		    namep->u.name.child->t == T_NUM)
186 			ret[i].i = (int)namep->u.name.child->u.ull;
187 		else
188 			config_getcompname(namep->u.name.cp, NULL, &ret[i].i);
189 		i++;
190 		namep = namep->u.name.next;
191 	}
192 
193 	/* add it to the cache */
194 	Ipaths = lut_add(Ipaths, (void *)ret, (void *)ret,
195 	    (lut_cmp)ipath_cmp);
196 
197 	stats_counter_bump(Nipath);
198 	stats_counter_add(Nbytes, (count + 1) * sizeof (struct ipath));
199 
200 	return (ret);
201 }
202 
203 /*
204  * ipath2str -- convert ename and ipath to class@path string
205  *
206  * if both ename and ipp are provided (non-NULL), the resulting string
207  * will be "class@path".  otherwise, the string will just contain the
208  * event class name (e.g. "ereport.io.pci.device") or just the path
209  * name (e.g. "mothboard0/hostbridge0/pcibus1/pcidev0/pcifn1"), depending
210  * on which argument is non-NULL.
211  */
212 char *
213 ipath2str(const char *ename, const struct ipath *ipp)
214 {
215 	int i;
216 	size_t len = 0;
217 	char *ret;
218 	char *cp;
219 
220 	/* count up length of class string */
221 	if (ename != NULL)
222 		len += strlen(ename);
223 
224 	/* count up length of path string, including slash separators */
225 	if (ipp != NULL) {
226 		for (i = 0; ipp[i].s != NULL; i++) {
227 			/* add slash separator, but no leading slash */
228 			if (i != 0)
229 				len++;
230 			len += snprintf(NULL, 0, "%s%d", ipp[i].s, ipp[i].i);
231 		}
232 	}
233 
234 	if (ename != NULL && ipp != NULL)
235 		len++;	/* room for '@' */
236 
237 	len++;	/* room for final '\0' */
238 
239 	cp = ret = MALLOC(len);
240 
241 	if (ename != NULL) {
242 		/* construct class string */
243 		(void) strcpy(cp, ename);
244 		cp += strlen(cp);
245 	}
246 
247 	/* if doing both strings, put '@' between them */
248 	if (ename != NULL && ipp != NULL)
249 		*cp++ = '@';
250 
251 	if (ipp != NULL) {
252 		/* construct path string */
253 		for (i = 0; ipp[i].s != NULL; i++) {
254 			if (i != 0)
255 				*cp++ = '/';
256 			(void) snprintf(cp, &ret[len] - cp, "%s%d",
257 			    ipp[i].s, ipp[i].i);
258 			cp += strlen(cp);
259 		}
260 	}
261 
262 	*cp++ = '\0';
263 
264 	return (ret);
265 }
266 
267 /*
268  * ipath2strlen -- calculate the len of what ipath2str() would return
269  */
270 size_t
271 ipath2strlen(const char *ename, const struct ipath *ipp)
272 {
273 	int i;
274 	size_t len = 0;
275 
276 	/* count up length of class string */
277 	if (ename != NULL)
278 		len += strlen(ename);
279 
280 	/* count up length of path string, including slash separators */
281 	if (ipp != NULL) {
282 		for (i = 0; ipp[i].s != NULL; i++) {
283 			/* add slash separator, but no leading slash */
284 			if (i != 0)
285 				len++;
286 			len += snprintf(NULL, 0, "%s%d", ipp[i].s, ipp[i].i);
287 		}
288 	}
289 
290 	if (ename != NULL && ipp != NULL)
291 		len++;	/* room for '@' */
292 
293 	return (len);
294 }
295 
296 /*
297  * ipath_print -- print out an ename, ipath, or both with '@' between them
298  */
299 void
300 ipath_print(int flags, const char *ename, const struct ipath *ipp)
301 {
302 	if (ename != NULL) {
303 		out(flags|O_NONL, ename);
304 		if (ipp != NULL)
305 			out(flags|O_NONL, "@");
306 	}
307 	if (ipp != NULL) {
308 		char *sep = "";
309 
310 		while (ipp->s != NULL) {
311 			out(flags|O_NONL, "%s%s%d", sep, ipp->s, ipp->i);
312 			ipp++;
313 			sep = "/";
314 		}
315 	}
316 }
317 
318 /*ARGSUSED*/
319 static void
320 ipath_destructor(void *left, void *right, void *arg)
321 {
322 	struct ipath *ipp = (struct ipath *)right;
323 
324 	FREE(ipp);
325 }
326 
327 /*
328  * ipath_fini -- free the ipath cache
329  */
330 void
331 ipath_fini(void)
332 {
333 	lut_free(Ipaths, ipath_destructor, NULL);
334 	Ipaths = NULL;
335 
336 	if (Nipath) {
337 		stats_delete(Nipath);
338 		Nipath = NULL;
339 	}
340 
341 	if (Nbytes) {
342 		stats_delete(Nbytes);
343 		Nbytes = NULL;
344 	}
345 }
346