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 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 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 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 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 * 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 * 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 * 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 * 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 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 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 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 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 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