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
45static struct stats *Nipath;
46static struct stats *Nbytes;
47
48static struct lut *Ipaths;	/* the ipath cache itself */
49
50/*
51 * ipath_init -- initialize the ipath module
52 */
53void
54ipath_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 */
71static int
72ipath_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 */
102static int
103ipath_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 */
146struct lut *Usednames;
147
148void
149ipath_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
167struct ipath *
168ipath_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
193struct ipath *
194ipath_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 */
222const struct ipath *
223ipath(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 */
286char *
287ipath2str(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
341void
342ipathlastcomp(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 */
357size_t
358ipath2strlen(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 */
386void
387ipath_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*/
406static void
407ipath_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 */
417void
418ipath_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