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