1 /* $NetBSD: coda_namecache.c,v 1.17 2005/12/11 12:19:50 christos Exp $ */ 2 3 /* 4 * 5 * Coda: an Experimental Distributed File System 6 * Release 3.1 7 * 8 * Copyright (c) 1987-1998 Carnegie Mellon University 9 * All Rights Reserved 10 * 11 * Permission to use, copy, modify and distribute this software and its 12 * documentation is hereby granted, provided that both the copyright 13 * notice and this permission notice appear in all copies of the 14 * software, derivative works or modified versions, and any portions 15 * thereof, and that both notices appear in supporting documentation, and 16 * that credit is given to Carnegie Mellon University in all documents 17 * and publicity pertaining to direct or indirect use of this code or its 18 * derivatives. 19 * 20 * CODA IS AN EXPERIMENTAL SOFTWARE SYSTEM AND IS KNOWN TO HAVE BUGS, 21 * SOME OF WHICH MAY HAVE SERIOUS CONSEQUENCES. CARNEGIE MELLON ALLOWS 22 * FREE USE OF THIS SOFTWARE IN ITS "AS IS" CONDITION. CARNEGIE MELLON 23 * DISCLAIMS ANY LIABILITY OF ANY KIND FOR ANY DAMAGES WHATSOEVER 24 * RESULTING DIRECTLY OR INDIRECTLY FROM THE USE OF THIS SOFTWARE OR OF 25 * ANY DERIVATIVE WORK. 26 * 27 * Carnegie Mellon encourages users of this software to return any 28 * improvements or extensions that they make, and to grant Carnegie 29 * Mellon the rights to redistribute these changes without encumbrance. 30 * 31 * @(#) coda/coda_namecache.c,v 1.1.1.1 1998/08/29 21:26:45 rvb Exp $ 32 */ 33 34 /* 35 * Mach Operating System 36 * Copyright (c) 1990 Carnegie-Mellon University 37 * Copyright (c) 1989 Carnegie-Mellon University 38 * All rights reserved. The CMU software License Agreement specifies 39 * the terms and conditions for use and redistribution. 40 */ 41 42 /* 43 * This code was written for the Coda file system at Carnegie Mellon University. 44 * Contributers include David Steere, James Kistler, and M. Satyanarayanan. 45 */ 46 47 /* 48 * This module contains the routines to implement the CODA name cache. The 49 * purpose of this cache is to reduce the cost of translating pathnames 50 * into Vice FIDs. Each entry in the cache contains the name of the file, 51 * the vnode (FID) of the parent directory, and the cred structure of the 52 * user accessing the file. 53 * 54 * The first time a file is accessed, it is looked up by the local Venus 55 * which first insures that the user has access to the file. In addition 56 * we are guaranteed that Venus will invalidate any name cache entries in 57 * case the user no longer should be able to access the file. For these 58 * reasons we do not need to keep access list information as well as a 59 * cred structure for each entry. 60 * 61 * The table can be accessed through the routines cnc_init(), cnc_enter(), 62 * cnc_lookup(), cnc_rmfidcred(), cnc_rmfid(), cnc_rmcred(), and cnc_purge(). 63 * There are several other routines which aid in the implementation of the 64 * hash table. 65 */ 66 67 /* 68 * NOTES: rvb@cs 69 * 1. The name cache holds a reference to every vnode in it. Hence files can not be 70 * closed or made inactive until they are released. 71 * 2. coda_nc_name(cp) was added to get a name for a cnode pointer for debugging. 72 * 3. coda_nc_find() has debug code to detect when entries are stored with different 73 * credentials. We don't understand yet, if/how entries are NOT EQ but still 74 * EQUAL 75 * 4. I wonder if this name cache could be replace by the vnode name cache. 76 * The latter has no zapping functions, so probably not. 77 */ 78 79 #include <sys/cdefs.h> 80 __KERNEL_RCSID(0, "$NetBSD: coda_namecache.c,v 1.17 2005/12/11 12:19:50 christos Exp $"); 81 82 #include <sys/param.h> 83 #include <sys/errno.h> 84 #include <sys/malloc.h> 85 #include <sys/select.h> 86 87 #include <coda/coda.h> 88 #include <coda/cnode.h> 89 #include <coda/coda_namecache.h> 90 91 #ifdef DEBUG 92 #include <coda/coda_vnops.h> 93 #endif 94 95 #ifndef insque 96 #include <sys/systm.h> 97 #endif /* insque */ 98 99 /* 100 * Declaration of the name cache data structure. 101 */ 102 103 int coda_nc_use = 1; /* Indicate use of CODA Name Cache */ 104 105 int coda_nc_size = CODA_NC_CACHESIZE; /* size of the cache */ 106 int coda_nc_hashsize = CODA_NC_HASHSIZE; /* size of the primary hash */ 107 108 struct coda_cache *coda_nc_heap; /* pointer to the cache entries */ 109 struct coda_hash *coda_nc_hash; /* hash table of cfscache pointers */ 110 struct coda_lru coda_nc_lru; /* head of lru chain */ 111 112 struct coda_nc_statistics coda_nc_stat; /* Keep various stats */ 113 114 /* 115 * for testing purposes 116 */ 117 int coda_nc_debug = 0; 118 119 /* 120 * Entry points for the CODA Name Cache 121 */ 122 static struct coda_cache * 123 coda_nc_find(struct cnode *dcp, const char *name, int namelen, 124 struct ucred *cred, int hash); 125 static void 126 coda_nc_remove(struct coda_cache *cncp, enum dc_status dcstat); 127 128 /* 129 * Initialize the cache, the LRU structure and the Hash structure(s) 130 */ 131 132 #define TOTAL_CACHE_SIZE (sizeof(struct coda_cache) * coda_nc_size) 133 #define TOTAL_HASH_SIZE (sizeof(struct coda_hash) * coda_nc_hashsize) 134 135 int coda_nc_initialized = 0; /* Initially the cache has not been initialized */ 136 137 void 138 coda_nc_init(void) 139 { 140 int i; 141 142 /* zero the statistics structure */ 143 144 memset(&coda_nc_stat, 0, (sizeof(struct coda_nc_statistics))); 145 146 #ifdef CODA_VERBOSE 147 printf("CODA NAME CACHE: CACHE %d, HASH TBL %d\n", CODA_NC_CACHESIZE, CODA_NC_HASHSIZE); 148 #endif 149 CODA_ALLOC(coda_nc_heap, struct coda_cache *, TOTAL_CACHE_SIZE); 150 CODA_ALLOC(coda_nc_hash, struct coda_hash *, TOTAL_HASH_SIZE); 151 152 coda_nc_lru.lru_next = 153 coda_nc_lru.lru_prev = (struct coda_cache *)LRU_PART(&coda_nc_lru); 154 155 156 for (i=0; i < coda_nc_size; i++) { /* initialize the heap */ 157 CODA_NC_LRUINS(&coda_nc_heap[i], &coda_nc_lru); 158 CODA_NC_HSHNUL(&coda_nc_heap[i]); 159 coda_nc_heap[i].cp = coda_nc_heap[i].dcp = (struct cnode *)0; 160 } 161 162 for (i=0; i < coda_nc_hashsize; i++) { /* initialize the hashtable */ 163 CODA_NC_HSHNUL((struct coda_cache *)&coda_nc_hash[i]); 164 } 165 166 coda_nc_initialized++; 167 } 168 169 /* 170 * Auxillary routines -- shouldn't be entry points 171 */ 172 173 static struct coda_cache * 174 coda_nc_find(struct cnode *dcp, const char *name, int namelen, 175 struct ucred *cred, int hash) 176 { 177 /* 178 * hash to find the appropriate bucket, look through the chain 179 * for the right entry (especially right cred, unless cred == 0) 180 */ 181 struct coda_cache *cncp; 182 int count = 1; 183 184 CODA_NC_DEBUG(CODA_NC_FIND, 185 myprintf(("coda_nc_find(dcp %p, name %s, len %d, cred %p, hash %d\n", 186 dcp, name, namelen, cred, hash));) 187 188 for (cncp = coda_nc_hash[hash].hash_next; 189 cncp != (struct coda_cache *)&coda_nc_hash[hash]; 190 cncp = cncp->hash_next, count++) 191 { 192 193 if ((CODA_NAMEMATCH(cncp, name, namelen, dcp)) && 194 ((cred == 0) || (cncp->cred == cred))) 195 { 196 /* compare cr_uid instead */ 197 coda_nc_stat.Search_len += count; 198 return(cncp); 199 } 200 #ifdef DEBUG 201 else if (CODA_NAMEMATCH(cncp, name, namelen, dcp)) { 202 printf("coda_nc_find: name %s, new cred = %p, cred = %p\n", 203 name, cred, cncp->cred); 204 printf("nref %d, nuid %d, ngid %d // oref %d, ocred %d, ogid %d\n", 205 cred->cr_ref, cred->cr_uid, cred->cr_gid, 206 cncp->cred->cr_ref, cncp->cred->cr_uid, cncp->cred->cr_gid); 207 print_cred(cred); 208 print_cred(cncp->cred); 209 } 210 #endif 211 } 212 213 return((struct coda_cache *)0); 214 } 215 216 /* 217 * Enter a new (dir cnode, name) pair into the cache, updating the 218 * LRU and Hash as needed. 219 */ 220 void 221 coda_nc_enter(struct cnode *dcp, const char *name, int namelen, 222 struct ucred *cred, struct cnode *cp) 223 { 224 struct coda_cache *cncp; 225 int hash; 226 227 if (coda_nc_use == 0) /* Cache is off */ 228 return; 229 230 CODA_NC_DEBUG(CODA_NC_ENTER, 231 myprintf(("Enter: dcp %p cp %p name %s cred %p \n", 232 dcp, cp, name, cred)); ) 233 234 if (namelen > CODA_NC_NAMELEN) { 235 CODA_NC_DEBUG(CODA_NC_ENTER, 236 myprintf(("long name enter %s\n",name));) 237 coda_nc_stat.long_name_enters++; /* record stats */ 238 return; 239 } 240 241 hash = CODA_NC_HASH(name, namelen, dcp); 242 cncp = coda_nc_find(dcp, name, namelen, cred, hash); 243 if (cncp != (struct coda_cache *) 0) { 244 coda_nc_stat.dbl_enters++; /* duplicate entry */ 245 return; 246 } 247 248 coda_nc_stat.enters++; /* record the enters statistic */ 249 250 /* Grab the next element in the lru chain */ 251 cncp = CODA_NC_LRUGET(coda_nc_lru); 252 253 CODA_NC_LRUREM(cncp); /* remove it from the lists */ 254 255 if (CODA_NC_VALID(cncp)) { 256 /* Seems really ugly, but we have to decrement the appropriate 257 hash bucket length here, so we have to find the hash bucket 258 */ 259 coda_nc_hash[CODA_NC_HASH(cncp->name, cncp->namelen, cncp->dcp)].length--; 260 261 coda_nc_stat.lru_rm++; /* zapped a valid entry */ 262 CODA_NC_HSHREM(cncp); 263 vrele(CTOV(cncp->dcp)); 264 vrele(CTOV(cncp->cp)); 265 crfree(cncp->cred); 266 } 267 268 /* 269 * Put a hold on the current vnodes and fill in the cache entry. 270 */ 271 vref(CTOV(cp)); 272 vref(CTOV(dcp)); 273 crhold(cred); 274 cncp->dcp = dcp; 275 cncp->cp = cp; 276 cncp->namelen = namelen; 277 cncp->cred = cred; 278 279 bcopy(name, cncp->name, (unsigned)namelen); 280 281 /* Insert into the lru and hash chains. */ 282 283 CODA_NC_LRUINS(cncp, &coda_nc_lru); 284 CODA_NC_HSHINS(cncp, &coda_nc_hash[hash]); 285 coda_nc_hash[hash].length++; /* Used for tuning */ 286 287 CODA_NC_DEBUG(CODA_NC_PRINTCODA_NC, print_coda_nc(); ) 288 } 289 290 /* 291 * Find the (dir cnode, name) pair in the cache, if it's cred 292 * matches the input, return it, otherwise return 0 293 */ 294 struct cnode * 295 coda_nc_lookup(struct cnode *dcp, const char *name, int namelen, 296 struct ucred *cred) 297 { 298 int hash; 299 struct coda_cache *cncp; 300 301 if (coda_nc_use == 0) /* Cache is off */ 302 return((struct cnode *) 0); 303 304 if (namelen > CODA_NC_NAMELEN) { 305 CODA_NC_DEBUG(CODA_NC_LOOKUP, 306 myprintf(("long name lookup %s\n",name));) 307 coda_nc_stat.long_name_lookups++; /* record stats */ 308 return((struct cnode *) 0); 309 } 310 311 /* Use the hash function to locate the starting point, 312 then the search routine to go down the list looking for 313 the correct cred. 314 */ 315 316 hash = CODA_NC_HASH(name, namelen, dcp); 317 cncp = coda_nc_find(dcp, name, namelen, cred, hash); 318 if (cncp == (struct coda_cache *) 0) { 319 coda_nc_stat.misses++; /* record miss */ 320 return((struct cnode *) 0); 321 } 322 323 coda_nc_stat.hits++; 324 325 /* put this entry at the end of the LRU */ 326 CODA_NC_LRUREM(cncp); 327 CODA_NC_LRUINS(cncp, &coda_nc_lru); 328 329 /* move it to the front of the hash chain */ 330 /* don't need to change the hash bucket length */ 331 CODA_NC_HSHREM(cncp); 332 CODA_NC_HSHINS(cncp, &coda_nc_hash[hash]); 333 334 CODA_NC_DEBUG(CODA_NC_LOOKUP, 335 printf("lookup: dcp %p, name %s, cred %p = cp %p\n", 336 dcp, name, cred, cncp->cp); ) 337 338 return(cncp->cp); 339 } 340 341 static void 342 coda_nc_remove(struct coda_cache *cncp, enum dc_status dcstat) 343 { 344 /* 345 * remove an entry -- vrele(cncp->dcp, cp), crfree(cred), 346 * remove it from it's hash chain, and 347 * place it at the head of the lru list. 348 */ 349 CODA_NC_DEBUG(CODA_NC_REMOVE, 350 myprintf(("coda_nc_remove %s from parent %s\n", 351 cncp->name, coda_f2s(&cncp->dcp->c_fid))); ) 352 353 354 CODA_NC_HSHREM(cncp); 355 356 CODA_NC_HSHNUL(cncp); /* have it be a null chain */ 357 if ((dcstat == IS_DOWNCALL) && (CTOV(cncp->dcp)->v_usecount == 1)) { 358 cncp->dcp->c_flags |= C_PURGING; 359 } 360 vrele(CTOV(cncp->dcp)); 361 362 if ((dcstat == IS_DOWNCALL) && (CTOV(cncp->cp)->v_usecount == 1)) { 363 cncp->cp->c_flags |= C_PURGING; 364 } 365 vrele(CTOV(cncp->cp)); 366 367 crfree(cncp->cred); 368 memset(DATA_PART(cncp), 0, DATA_SIZE); 369 370 /* Put the null entry just after the least-recently-used entry */ 371 /* LRU_TOP adjusts the pointer to point to the top of the structure. */ 372 CODA_NC_LRUREM(cncp); 373 CODA_NC_LRUINS(cncp, LRU_TOP(coda_nc_lru.lru_prev)); 374 } 375 376 /* 377 * Remove all entries with a parent which has the input fid. 378 */ 379 void 380 coda_nc_zapParentfid(CodaFid *fid, enum dc_status dcstat) 381 { 382 /* To get to a specific fid, we might either have another hashing 383 function or do a sequential search through the cache for the 384 appropriate entries. The later may be acceptable since I don't 385 think callbacks or whatever Case 1 covers are frequent occurrences. 386 */ 387 struct coda_cache *cncp, *ncncp; 388 int i; 389 390 if (coda_nc_use == 0) /* Cache is off */ 391 return; 392 393 CODA_NC_DEBUG(CODA_NC_ZAPPFID, 394 myprintf(("ZapParent: fid %s\n", coda_f2s(fid))); ) 395 396 coda_nc_stat.zapPfids++; 397 398 for (i = 0; i < coda_nc_hashsize; i++) { 399 400 /* 401 * Need to save the hash_next pointer in case we remove the 402 * entry. remove causes hash_next to point to itself. 403 */ 404 405 for (cncp = coda_nc_hash[i].hash_next; 406 cncp != (struct coda_cache *)&coda_nc_hash[i]; 407 cncp = ncncp) { 408 ncncp = cncp->hash_next; 409 if (coda_fid_eq(&(cncp->dcp->c_fid), fid)) { 410 coda_nc_hash[i].length--; /* Used for tuning */ 411 coda_nc_remove(cncp, dcstat); 412 } 413 } 414 } 415 } 416 417 /* 418 * Remove all entries which have the same fid as the input 419 */ 420 void 421 coda_nc_zapfid(CodaFid *fid, enum dc_status dcstat) 422 { 423 /* See comment for zapParentfid. This routine will be used 424 if attributes are being cached. 425 */ 426 struct coda_cache *cncp, *ncncp; 427 int i; 428 429 if (coda_nc_use == 0) /* Cache is off */ 430 return; 431 432 CODA_NC_DEBUG(CODA_NC_ZAPFID, 433 myprintf(("Zapfid: fid %s\n", coda_f2s(fid))); ) 434 435 coda_nc_stat.zapFids++; 436 437 for (i = 0; i < coda_nc_hashsize; i++) { 438 for (cncp = coda_nc_hash[i].hash_next; 439 cncp != (struct coda_cache *)&coda_nc_hash[i]; 440 cncp = ncncp) { 441 ncncp = cncp->hash_next; 442 if (coda_fid_eq(&cncp->cp->c_fid, fid)) { 443 coda_nc_hash[i].length--; /* Used for tuning */ 444 coda_nc_remove(cncp, dcstat); 445 } 446 } 447 } 448 } 449 450 /* 451 * Remove all entries which match the fid and the cred 452 */ 453 void 454 coda_nc_zapvnode(CodaFid *fid, struct ucred *cred, enum dc_status dcstat) 455 { 456 /* See comment for zapfid. I don't think that one would ever 457 want to zap a file with a specific cred from the kernel. 458 We'll leave this one unimplemented. 459 */ 460 if (coda_nc_use == 0) /* Cache is off */ 461 return; 462 463 CODA_NC_DEBUG(CODA_NC_ZAPVNODE, 464 myprintf(("Zapvnode: fid %s cred %p\n", 465 coda_f2s(fid), cred)); ) 466 } 467 468 /* 469 * Remove all entries which have the (dir vnode, name) pair 470 */ 471 void 472 coda_nc_zapfile(struct cnode *dcp, const char *name, int namelen) 473 { 474 /* use the hash function to locate the file, then zap all 475 entries of it regardless of the cred. 476 */ 477 struct coda_cache *cncp; 478 int hash; 479 480 if (coda_nc_use == 0) /* Cache is off */ 481 return; 482 483 CODA_NC_DEBUG(CODA_NC_ZAPFILE, 484 myprintf(("Zapfile: dcp %p name %s \n", 485 dcp, name)); ) 486 487 if (namelen > CODA_NC_NAMELEN) { 488 coda_nc_stat.long_remove++; /* record stats */ 489 return; 490 } 491 492 coda_nc_stat.zapFile++; 493 494 hash = CODA_NC_HASH(name, namelen, dcp); 495 cncp = coda_nc_find(dcp, name, namelen, 0, hash); 496 497 while (cncp) { 498 coda_nc_hash[hash].length--; /* Used for tuning */ 499 /* 1.3 */ 500 coda_nc_remove(cncp, NOT_DOWNCALL); 501 cncp = coda_nc_find(dcp, name, namelen, 0, hash); 502 } 503 } 504 505 /* 506 * Remove all the entries for a particular user. Used when tokens expire. 507 * A user is determined by his/her effective user id (id_uid). 508 */ 509 void 510 coda_nc_purge_user(uid_t uid, enum dc_status dcstat) 511 { 512 /* 513 * I think the best approach is to go through the entire cache 514 * via HASH or whatever and zap all entries which match the 515 * input cred. Or just flush the whole cache. It might be 516 * best to go through on basis of LRU since cache will almost 517 * always be full and LRU is more straightforward. 518 */ 519 520 struct coda_cache *cncp, *ncncp; 521 int hash; 522 523 if (coda_nc_use == 0) /* Cache is off */ 524 return; 525 526 CODA_NC_DEBUG(CODA_NC_PURGEUSER, 527 myprintf(("ZapDude: uid %x\n", uid)); ) 528 coda_nc_stat.zapUsers++; 529 530 for (cncp = CODA_NC_LRUGET(coda_nc_lru); 531 cncp != (struct coda_cache *)(&coda_nc_lru); 532 cncp = ncncp) { 533 ncncp = CODA_NC_LRUGET(*cncp); 534 535 if ((CODA_NC_VALID(cncp)) && 536 ((cncp->cred)->cr_uid == uid)) { 537 /* Seems really ugly, but we have to decrement the appropriate 538 hash bucket length here, so we have to find the hash bucket 539 */ 540 hash = CODA_NC_HASH(cncp->name, cncp->namelen, cncp->dcp); 541 coda_nc_hash[hash].length--; /* For performance tuning */ 542 543 coda_nc_remove(cncp, dcstat); 544 } 545 } 546 } 547 548 /* 549 * Flush the entire name cache. In response to a flush of the Venus cache. 550 */ 551 void 552 coda_nc_flush(enum dc_status dcstat) 553 { 554 /* One option is to deallocate the current name cache and 555 call init to start again. Or just deallocate, then rebuild. 556 Or again, we could just go through the array and zero the 557 appropriate fields. 558 */ 559 560 /* 561 * Go through the whole lru chain and kill everything as we go. 562 * I don't use remove since that would rebuild the lru chain 563 * as it went and that seemed unneccesary. 564 */ 565 struct coda_cache *cncp; 566 int i; 567 568 if (coda_nc_use == 0) /* Cache is off */ 569 return; 570 571 coda_nc_stat.Flushes++; 572 573 for (cncp = CODA_NC_LRUGET(coda_nc_lru); 574 cncp != (struct coda_cache *)&coda_nc_lru; 575 cncp = CODA_NC_LRUGET(*cncp)) { 576 if (CODA_NC_VALID(cncp)) { 577 578 CODA_NC_HSHREM(cncp); /* only zero valid nodes */ 579 CODA_NC_HSHNUL(cncp); 580 if ((dcstat == IS_DOWNCALL) 581 && (CTOV(cncp->dcp)->v_usecount == 1)) 582 { 583 cncp->dcp->c_flags |= C_PURGING; 584 } 585 vrele(CTOV(cncp->dcp)); 586 587 if (CTOV(cncp->cp)->v_flag & VTEXT) { 588 if (coda_vmflush(cncp->cp)) 589 CODADEBUG(CODA_FLUSH, 590 myprintf(("coda_nc_flush: %s busy\n", 591 coda_f2s(&cncp->cp->c_fid))); ) 592 } 593 594 if ((dcstat == IS_DOWNCALL) 595 && (CTOV(cncp->cp)->v_usecount == 1)) 596 { 597 cncp->cp->c_flags |= C_PURGING; 598 } 599 vrele(CTOV(cncp->cp)); 600 601 crfree(cncp->cred); 602 memset(DATA_PART(cncp), 0, DATA_SIZE); 603 } 604 } 605 606 for (i = 0; i < coda_nc_hashsize; i++) 607 coda_nc_hash[i].length = 0; 608 } 609 610 /* 611 * Debugging routines 612 */ 613 614 /* 615 * This routine should print out all the hash chains to the console. 616 */ 617 void 618 print_coda_nc(void) 619 { 620 int hash; 621 struct coda_cache *cncp; 622 623 for (hash = 0; hash < coda_nc_hashsize; hash++) { 624 myprintf(("\nhash %d\n",hash)); 625 626 for (cncp = coda_nc_hash[hash].hash_next; 627 cncp != (struct coda_cache *)&coda_nc_hash[hash]; 628 cncp = cncp->hash_next) { 629 myprintf(("cp %p dcp %p cred %p name %s\n", 630 cncp->cp, cncp->dcp, 631 cncp->cred, cncp->name)); 632 } 633 } 634 } 635 636 void 637 coda_nc_gather_stats(void) 638 { 639 int i, xmax = 0, sum = 0, temp, zeros = 0, ave, n; 640 641 for (i = 0; i < coda_nc_hashsize; i++) { 642 if (coda_nc_hash[i].length) { 643 sum += coda_nc_hash[i].length; 644 } else { 645 zeros++; 646 } 647 648 if (coda_nc_hash[i].length > xmax) 649 xmax = coda_nc_hash[i].length; 650 } 651 652 /* 653 * When computing the Arithmetic mean, only count slots which 654 * are not empty in the distribution. 655 */ 656 coda_nc_stat.Sum_bucket_len = sum; 657 coda_nc_stat.Num_zero_len = zeros; 658 coda_nc_stat.Max_bucket_len = xmax; 659 660 if ((n = coda_nc_hashsize - zeros) > 0) 661 ave = sum / n; 662 else 663 ave = 0; 664 665 sum = 0; 666 for (i = 0; i < coda_nc_hashsize; i++) { 667 if (coda_nc_hash[i].length) { 668 temp = coda_nc_hash[i].length - ave; 669 sum += temp * temp; 670 } 671 } 672 coda_nc_stat.Sum2_bucket_len = sum; 673 } 674 675 /* 676 * The purpose of this routine is to allow the hash and cache sizes to be 677 * changed dynamically. This should only be used in controlled environments, 678 * it makes no effort to lock other users from accessing the cache while it 679 * is in an improper state (except by turning the cache off). 680 */ 681 int 682 coda_nc_resize(int hashsize, int heapsize, enum dc_status dcstat) 683 { 684 if ((hashsize % 2) || (heapsize % 2)) { /* Illegal hash or cache sizes */ 685 return(EINVAL); 686 } 687 688 coda_nc_use = 0; /* Turn the cache off */ 689 690 coda_nc_flush(dcstat); /* free any cnodes in the cache */ 691 692 /* WARNING: free must happen *before* size is reset */ 693 CODA_FREE(coda_nc_heap,TOTAL_CACHE_SIZE); 694 CODA_FREE(coda_nc_hash,TOTAL_HASH_SIZE); 695 696 coda_nc_hashsize = hashsize; 697 coda_nc_size = heapsize; 698 699 coda_nc_init(); /* Set up a cache with the new size */ 700 701 coda_nc_use = 1; /* Turn the cache back on */ 702 return(0); 703 } 704 705 char coda_nc_name_buf[CODA_MAXNAMLEN+1]; 706 707 void 708 coda_nc_name(struct cnode *cp) 709 { 710 struct coda_cache *cncp, *ncncp; 711 int i; 712 713 if (coda_nc_use == 0) /* Cache is off */ 714 return; 715 716 for (i = 0; i < coda_nc_hashsize; i++) { 717 for (cncp = coda_nc_hash[i].hash_next; 718 cncp != (struct coda_cache *)&coda_nc_hash[i]; 719 cncp = ncncp) { 720 ncncp = cncp->hash_next; 721 if (cncp->cp == cp) { 722 bcopy(cncp->name, coda_nc_name_buf, cncp->namelen); 723 coda_nc_name_buf[cncp->namelen] = 0; 724 printf(" is %s (%p,%p)@%p", 725 coda_nc_name_buf, cncp->cp, cncp->dcp, cncp); 726 } 727 728 } 729 } 730 } 731