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 2008 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 #pragma ident "%Z%%M% %I% %E% SMI" 32 33 #include <stdio.h> 34 #include <string.h> 35 #include "alloc.h" 36 #include "out.h" 37 #include "lut.h" 38 #include "tree.h" 39 #include "ptree.h" 40 #include "itree.h" 41 #include "ipath.h" 42 #include "ipath_impl.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 static struct lut *Ipaths; /* the ipath cache itself */ 51 52 /* 53 * ipath_init -- initialize the ipath module 54 */ 55 void 56 ipath_init(void) 57 { 58 Nipath = stats_new_counter("ievent.nipath", "ipath cache entries", 1); 59 Nbytes = stats_new_counter("ievent.nbytes", "total cache size", 1); 60 } 61 62 /* 63 * ipath_cmp -- compare two ipath entries 64 * 65 * since two ipaths containing the same components and instance 66 * numbers always point to the same cache entry, they are equal 67 * if their pointers are equal, so this function is not necessary 68 * to test if two ipaths are same. but when inserting a new ipath 69 * into the cache, we must use the same lut comparison logic as when 70 * we're searching for it, so this function must always match the 71 * itree_epnamecmp() function's logic (see below) for searching the lut. 72 */ 73 static int 74 ipath_cmp(struct ipath *ipp1, struct ipath *ipp2) 75 { 76 int i; 77 78 ASSERT(ipp1 != NULL); 79 ASSERT(ipp2 != NULL); 80 81 for (i = 0; ipp1[i].s != NULL && ipp2[i].s != NULL; i++) 82 if (ipp1[i].s != ipp2[i].s) 83 return (ipp2[i].s - ipp1[i].s); 84 else if (ipp1[i].i != ipp2[i].i) 85 return (ipp2[i].i - ipp1[i].i); 86 87 if (ipp1[i].s == NULL && ipp2[i].s == NULL) 88 return (0); 89 else if (ipp1[i].s == NULL) 90 return (1); 91 else 92 return (-1); 93 } 94 95 /* 96 * ipath_epnamecmp -- compare an ipath with a struct node *epname list 97 * 98 * this function is used when searching the cache, allowing us to search 99 * a lut full of ipaths by looking directly at a struct node *epname 100 * (without having to convert it first). the comparison logic here must 101 * exactly match itree_cmp()'s logic (see above) so lut lookups use find 102 * the same node as lut inserts. 103 */ 104 static int 105 ipath_epnamecmp(struct ipath *ipp, struct node *np) 106 { 107 int i; 108 109 ASSERT(np != NULL); 110 ASSERT(ipp != NULL); 111 112 for (i = 0; ipp[i].s != NULL && np != NULL; i++, np = np->u.name.next) { 113 ASSERTinfo(np->t == T_NAME, ptree_nodetype2str(np->t)); 114 115 if (ipp[i].s != np->u.name.s) 116 return (np->u.name.s - ipp[i].s); 117 else { 118 int inum; 119 120 if (np->u.name.child != NULL && 121 np->u.name.child->t == T_NUM) 122 inum = (int)np->u.name.child->u.ull; 123 else 124 config_getcompname(np->u.name.cp, NULL, &inum); 125 126 if (ipp[i].i != inum) 127 return (inum - ipp[i].i); 128 } 129 } 130 131 if (ipp[i].s == NULL && np == NULL) 132 return (0); 133 else if (ipp[i].s == NULL) 134 return (1); 135 else 136 return (-1); 137 } 138 139 struct lut *Usednames; 140 141 void 142 ipath_dummy_lut(struct arrow *arrowp) 143 { 144 const struct ipath *ipp; 145 146 ipp = arrowp->head->myevent->ipp; 147 while (ipp->s != NULL) { 148 Usednames = lut_add(Usednames, (void *)ipp->s, 149 (void *)ipp->s, NULL); 150 ipp++; 151 } 152 ipp = arrowp->tail->myevent->ipp; 153 while (ipp->s != NULL) { 154 Usednames = lut_add(Usednames, (void *)ipp->s, 155 (void *)ipp->s, NULL); 156 ipp++; 157 } 158 } 159 160 struct ipath * 161 ipath_dummy(struct node *np, struct ipath *ipp) 162 { 163 struct ipath *ret; 164 165 ret = ipp; 166 while (ipp[1].s != NULL) 167 ipp++; 168 if (strcmp(ipp[0].s, np->u.name.last->u.name.s) == 0) 169 return (ret); 170 171 ret = MALLOC(sizeof (*ret) * 2); 172 ret[0].s = np->u.name.last->u.name.s; 173 ret[0].i = 0; 174 ret[1].s = NULL; 175 if ((ipp = lut_lookup(Ipaths, (void *)ret, 176 (lut_cmp)ipath_cmp)) != NULL) { 177 FREE(ret); 178 return (ipp); 179 } 180 Ipaths = lut_add(Ipaths, (void *)ret, (void *)ret, (lut_cmp)ipath_cmp); 181 stats_counter_bump(Nipath); 182 stats_counter_add(Nbytes, 2 * sizeof (struct ipath)); 183 return (ret); 184 } 185 186 /* 187 * ipath -- find instanced path in cache, or add it if necessary 188 */ 189 const struct ipath * 190 ipath(struct node *np) 191 { 192 struct ipath *ret; 193 int count; 194 struct node *namep; 195 int i; 196 197 if ((ret = lut_lookup(Ipaths, (void *)np, 198 (lut_cmp)ipath_epnamecmp)) != NULL) 199 return (ret); /* already in cache */ 200 201 /* 202 * not in cache, make new cache entry. 203 * start by counting the length of the name. 204 */ 205 count = 0; 206 namep = np; 207 while (namep != NULL) { 208 ASSERTinfo(namep->t == T_NAME, ptree_nodetype2str(namep->t)); 209 count++; 210 namep = namep->u.name.next; 211 } 212 213 ASSERT(count > 0); 214 215 /* allocate array for name and last NULL entry */ 216 ret = MALLOC(sizeof (*ret) * (count + 1)); 217 ret[count].s = NULL; 218 219 /* fill in ipath entry */ 220 namep = np; 221 i = 0; 222 while (namep != NULL) { 223 ASSERT(i < count); 224 ret[i].s = namep->u.name.s; 225 if (namep->u.name.child != NULL && 226 namep->u.name.child->t == T_NUM) 227 ret[i].i = (int)namep->u.name.child->u.ull; 228 else 229 config_getcompname(namep->u.name.cp, NULL, &ret[i].i); 230 i++; 231 namep = namep->u.name.next; 232 } 233 234 /* add it to the cache */ 235 Ipaths = lut_add(Ipaths, (void *)ret, (void *)ret, 236 (lut_cmp)ipath_cmp); 237 238 stats_counter_bump(Nipath); 239 stats_counter_add(Nbytes, (count + 1) * sizeof (struct ipath)); 240 241 return (ret); 242 } 243 244 /* 245 * ipath2str -- convert ename and ipath to class@path string 246 * 247 * if both ename and ipp are provided (non-NULL), the resulting string 248 * will be "class@path". otherwise, the string will just contain the 249 * event class name (e.g. "ereport.io.pci.device") or just the path 250 * name (e.g. "mothboard0/hostbridge0/pcibus1/pcidev0/pcifn1"), depending 251 * on which argument is non-NULL. 252 */ 253 char * 254 ipath2str(const char *ename, const struct ipath *ipp) 255 { 256 int i; 257 size_t len = 0; 258 char *ret; 259 char *cp; 260 261 /* count up length of class string */ 262 if (ename != NULL) 263 len += strlen(ename); 264 265 /* count up length of path string, including slash separators */ 266 if (ipp != NULL) { 267 for (i = 0; ipp[i].s != NULL; i++) { 268 /* add slash separator, but no leading slash */ 269 if (i != 0) 270 len++; 271 len += snprintf(NULL, 0, "%s%d", ipp[i].s, ipp[i].i); 272 } 273 } 274 275 if (ename != NULL && ipp != NULL) 276 len++; /* room for '@' */ 277 278 len++; /* room for final '\0' */ 279 280 cp = ret = MALLOC(len); 281 282 if (ename != NULL) { 283 /* construct class string */ 284 (void) strcpy(cp, ename); 285 cp += strlen(cp); 286 } 287 288 /* if doing both strings, put '@' between them */ 289 if (ename != NULL && ipp != NULL) 290 *cp++ = '@'; 291 292 if (ipp != NULL) { 293 /* construct path string */ 294 for (i = 0; ipp[i].s != NULL; i++) { 295 if (i != 0) 296 *cp++ = '/'; 297 (void) snprintf(cp, &ret[len] - cp, "%s%d", 298 ipp[i].s, ipp[i].i); 299 cp += strlen(cp); 300 } 301 } 302 303 *cp++ = '\0'; 304 305 return (ret); 306 } 307 308 /* 309 * ipath2strlen -- calculate the len of what ipath2str() would return 310 */ 311 size_t 312 ipath2strlen(const char *ename, const struct ipath *ipp) 313 { 314 int i; 315 size_t len = 0; 316 317 /* count up length of class string */ 318 if (ename != NULL) 319 len += strlen(ename); 320 321 /* count up length of path string, including slash separators */ 322 if (ipp != NULL) { 323 for (i = 0; ipp[i].s != NULL; i++) { 324 /* add slash separator, but no leading slash */ 325 if (i != 0) 326 len++; 327 len += snprintf(NULL, 0, "%s%d", ipp[i].s, ipp[i].i); 328 } 329 } 330 331 if (ename != NULL && ipp != NULL) 332 len++; /* room for '@' */ 333 334 return (len); 335 } 336 337 /* 338 * ipath_print -- print out an ename, ipath, or both with '@' between them 339 */ 340 void 341 ipath_print(int flags, const char *ename, const struct ipath *ipp) 342 { 343 if (ename != NULL) { 344 out(flags|O_NONL, ename); 345 if (ipp != NULL) 346 out(flags|O_NONL, "@"); 347 } 348 if (ipp != NULL) { 349 char *sep = ""; 350 351 while (ipp->s != NULL) { 352 out(flags|O_NONL, "%s%s%d", sep, ipp->s, ipp->i); 353 ipp++; 354 sep = "/"; 355 } 356 } 357 } 358 359 /*ARGSUSED*/ 360 static void 361 ipath_destructor(void *left, void *right, void *arg) 362 { 363 struct ipath *ipp = (struct ipath *)right; 364 365 FREE(ipp); 366 } 367 368 /* 369 * ipath_fini -- free the ipath cache 370 */ 371 void 372 ipath_fini(void) 373 { 374 lut_free(Ipaths, ipath_destructor, NULL); 375 Ipaths = NULL; 376 lut_free(Usednames, NULL, NULL); 377 Usednames = NULL; 378 379 if (Nipath) { 380 stats_delete(Nipath); 381 Nipath = NULL; 382 } 383 384 if (Nbytes) { 385 stats_delete(Nbytes); 386 Nbytes = NULL; 387 } 388 } 389