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 2010 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
ipath_init(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
ipath_cmp(struct ipath * ipp1,struct ipath * ipp2)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
ipath_epnamecmp(struct ipath * ipp,struct node * np)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
ipath_dummy_lut(struct arrow * arrowp)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 *
ipath_dummy(struct node * np,struct ipath * ipp)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 *
ipath_for_usednames(struct node * np)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 *
ipath(struct node * np)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 *
ipath2str(const char * ename,const struct ipath * ipp)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 void
ipathlastcomp(const struct ipath * ipp)342 ipathlastcomp(const struct ipath *ipp)
343 {
344 	int i;
345 
346 	for (i = 0; ipp[i].s != NULL; i++)
347 		;
348 
349 	out(O_ALTFP, "newfme: add %s to Usednames", ipp[i - 1].s);
350 	Usednames = lut_add(Usednames, (void *)ipp[i - 1].s,
351 	    (void *)ipp[i - 1].s, NULL);
352 }
353 
354 /*
355  * ipath2strlen -- calculate the len of what ipath2str() would return
356  */
357 size_t
ipath2strlen(const char * ename,const struct ipath * ipp)358 ipath2strlen(const char *ename, const struct ipath *ipp)
359 {
360 	int i;
361 	size_t len = 0;
362 
363 	/* count up length of class string */
364 	if (ename != NULL)
365 		len += strlen(ename);
366 
367 	/* count up length of path string, including slash separators */
368 	if (ipp != NULL) {
369 		for (i = 0; ipp[i].s != NULL; i++) {
370 			/* add slash separator, but no leading slash */
371 			if (i != 0)
372 				len++;
373 			len += snprintf(NULL, 0, "%s%d", ipp[i].s, ipp[i].i);
374 		}
375 	}
376 
377 	if (ename != NULL && ipp != NULL)
378 		len++;	/* room for '@' */
379 
380 	return (len);
381 }
382 
383 /*
384  * ipath_print -- print out an ename, ipath, or both with '@' between them
385  */
386 void
ipath_print(int flags,const char * ename,const struct ipath * ipp)387 ipath_print(int flags, const char *ename, const struct ipath *ipp)
388 {
389 	if (ename != NULL) {
390 		out(flags|O_NONL, ename);
391 		if (ipp != NULL)
392 			out(flags|O_NONL, "@");
393 	}
394 	if (ipp != NULL) {
395 		char *sep = "";
396 
397 		while (ipp->s != NULL) {
398 			out(flags|O_NONL, "%s%s%d", sep, ipp->s, ipp->i);
399 			ipp++;
400 			sep = "/";
401 		}
402 	}
403 }
404 
405 /*ARGSUSED*/
406 static void
ipath_destructor(void * left,void * right,void * arg)407 ipath_destructor(void *left, void *right, void *arg)
408 {
409 	struct ipath *ipp = (struct ipath *)right;
410 
411 	FREE(ipp);
412 }
413 
414 /*
415  * ipath_fini -- free the ipath cache
416  */
417 void
ipath_fini(void)418 ipath_fini(void)
419 {
420 	lut_free(Ipaths, ipath_destructor, NULL);
421 	Ipaths = NULL;
422 	lut_free(Usednames, NULL, NULL);
423 	Usednames = NULL;
424 
425 	if (Nipath) {
426 		stats_delete(Nipath);
427 		Nipath = NULL;
428 	}
429 
430 	if (Nbytes) {
431 		stats_delete(Nbytes);
432 		Nbytes = NULL;
433 	}
434 }
435