coda_namecache.c revision 1.24
1/* $NetBSD: coda_namecache.c,v 1.24 2009/04/18 14:58:02 tsutsui 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.24 2009/04/18 14:58:02 tsutsui Exp $"); 81 82#include <sys/param.h> 83#include <sys/errno.h> 84#include <sys/malloc.h> 85#include <sys/select.h> 86#include <sys/kauth.h> 87 88#include <coda/coda.h> 89#include <coda/cnode.h> 90#include <coda/coda_namecache.h> 91 92#ifdef DEBUG 93#include <coda/coda_vnops.h> 94#endif 95 96/* 97 * Declaration of the name cache data structure. 98 */ 99 100int coda_nc_use = 1; /* Indicate use of CODA Name Cache */ 101 102int coda_nc_size = CODA_NC_CACHESIZE; /* size of the cache */ 103int coda_nc_hashsize = CODA_NC_HASHSIZE; /* size of the primary hash */ 104 105struct coda_cache *coda_nc_heap; /* pointer to the cache entries */ 106struct coda_hash *coda_nc_hash; /* hash table of cfscache pointers */ 107struct coda_lru coda_nc_lru; /* head of lru chain */ 108 109struct coda_nc_statistics coda_nc_stat; /* Keep various stats */ 110 111/* 112 * for testing purposes 113 */ 114int coda_nc_debug = 0; 115 116/* 117 * Entry points for the CODA Name Cache 118 */ 119static struct coda_cache * 120coda_nc_find(struct cnode *dcp, const char *name, int namelen, 121 kauth_cred_t cred, int hash); 122static void 123coda_nc_remove(struct coda_cache *cncp, enum dc_status dcstat); 124 125/* 126 * Initialize the cache, the LRU structure and the Hash structure(s) 127 */ 128 129#define TOTAL_CACHE_SIZE (sizeof(struct coda_cache) * coda_nc_size) 130#define TOTAL_HASH_SIZE (sizeof(struct coda_hash) * coda_nc_hashsize) 131 132int coda_nc_initialized = 0; /* Initially the cache has not been initialized */ 133 134void 135coda_nc_init(void) 136{ 137 int i; 138 139 /* zero the statistics structure */ 140 141 memset(&coda_nc_stat, 0, (sizeof(struct coda_nc_statistics))); 142 143#ifdef CODA_VERBOSE 144 printf("CODA NAME CACHE: CACHE %d, HASH TBL %d\n", CODA_NC_CACHESIZE, CODA_NC_HASHSIZE); 145#endif 146 CODA_ALLOC(coda_nc_heap, struct coda_cache *, TOTAL_CACHE_SIZE); 147 CODA_ALLOC(coda_nc_hash, struct coda_hash *, TOTAL_HASH_SIZE); 148 149 memset(coda_nc_heap, 0, TOTAL_CACHE_SIZE); 150 memset(coda_nc_hash, 0, TOTAL_HASH_SIZE); 151 152 TAILQ_INIT(&coda_nc_lru.head); 153 154 for (i=0; i < coda_nc_size; i++) { /* initialize the heap */ 155 TAILQ_INSERT_HEAD(&coda_nc_lru.head, &coda_nc_heap[i], lru); 156 } 157 158 for (i=0; i < coda_nc_hashsize; i++) { /* initialize the hashtable */ 159 LIST_INIT(&coda_nc_hash[i].head); 160 } 161 162 coda_nc_initialized++; 163} 164 165/* 166 * Auxillary routines -- shouldn't be entry points 167 */ 168 169static struct coda_cache * 170coda_nc_find(struct cnode *dcp, const char *name, int namelen, 171 kauth_cred_t cred, int hash) 172{ 173 /* 174 * hash to find the appropriate bucket, look through the chain 175 * for the right entry (especially right cred, unless cred == 0) 176 */ 177 struct coda_cache *cncp; 178 int count = 1; 179 180 CODA_NC_DEBUG(CODA_NC_FIND, 181 myprintf(("coda_nc_find(dcp %p, name %s, len %d, cred %p, hash %d\n", 182 dcp, name, namelen, cred, hash));) 183 184 LIST_FOREACH(cncp, &coda_nc_hash[hash].head, hash) 185 { 186 187 if ((CODA_NAMEMATCH(cncp, name, namelen, dcp)) && 188 ((cred == 0) || (cncp->cred == cred))) 189 { 190 /* compare cr_uid instead */ 191 coda_nc_stat.Search_len += count; 192 return(cncp); 193 } 194#ifdef DEBUG 195 else if (CODA_NAMEMATCH(cncp, name, namelen, dcp)) { 196 printf("coda_nc_find: name %s, new cred = %p, cred = %p\n", 197 name, cred, cncp->cred); 198 printf("nref %d, nuid %d, ngid %d // oref %d, ocred %d, ogid %d\n", 199 kauth_cred_getrefcnt(cred), 200 kauth_cred_geteuid(cred), 201 kauth_cred_getegid(cred), 202 kauth_cred_getrefcnt(cncp->cred), 203 kauth_cred_geteuid(cncp->cred), 204 kauth_cred_getegid(cncp->cred)); 205 print_cred(cred); 206 print_cred(cncp->cred); 207 } 208#endif 209 count++; 210 } 211 212 return((struct coda_cache *)0); 213} 214 215/* 216 * Enter a new (dir cnode, name) pair into the cache, updating the 217 * LRU and Hash as needed. 218 */ 219void 220coda_nc_enter(struct cnode *dcp, const char *name, int namelen, 221 kauth_cred_t cred, struct cnode *cp) 222{ 223 struct coda_cache *cncp; 224 int hash; 225 226 if (coda_nc_use == 0) /* Cache is off */ 227 return; 228 229 CODA_NC_DEBUG(CODA_NC_ENTER, 230 myprintf(("Enter: dcp %p cp %p name %s cred %p \n", 231 dcp, cp, name, cred)); ) 232 233 if (namelen > CODA_NC_NAMELEN) { 234 CODA_NC_DEBUG(CODA_NC_ENTER, 235 myprintf(("long name enter %s\n",name));) 236 coda_nc_stat.long_name_enters++; /* record stats */ 237 return; 238 } 239 240 hash = CODA_NC_HASH(name, namelen, dcp); 241 cncp = coda_nc_find(dcp, name, namelen, cred, hash); 242 if (cncp != (struct coda_cache *) 0) { 243 coda_nc_stat.dbl_enters++; /* duplicate entry */ 244 return; 245 } 246 247 coda_nc_stat.enters++; /* record the enters statistic */ 248 249 /* Grab the next element in the lru chain */ 250 cncp = TAILQ_FIRST(&coda_nc_lru.head); 251 TAILQ_REMOVE(&coda_nc_lru.head, cncp, lru); 252 253 if (CODA_NC_VALID(cncp)) { 254 /* Seems really ugly, but we have to decrement the appropriate 255 hash bucket length here, so we have to find the hash bucket 256 */ 257 coda_nc_hash[CODA_NC_HASH(cncp->name, cncp->namelen, cncp->dcp)].length--; 258 259 coda_nc_stat.lru_rm++; /* zapped a valid entry */ 260 LIST_REMOVE(cncp, hash); 261 vrele(CTOV(cncp->dcp)); 262 vrele(CTOV(cncp->cp)); 263 kauth_cred_free(cncp->cred); 264 } 265 266 /* 267 * Put a hold on the current vnodes and fill in the cache entry. 268 */ 269 vref(CTOV(cp)); 270 vref(CTOV(dcp)); 271 kauth_cred_hold(cred); 272 cncp->dcp = dcp; 273 cncp->cp = cp; 274 cncp->namelen = namelen; 275 cncp->cred = cred; 276 277 memcpy(cncp->name, name, (unsigned)namelen); 278 279 /* Insert into the lru and hash chains. */ 280 TAILQ_INSERT_TAIL(&coda_nc_lru.head, cncp, lru); 281 LIST_INSERT_HEAD(&coda_nc_hash[hash].head, cncp, hash); 282 coda_nc_hash[hash].length++; /* Used for tuning */ 283 284 CODA_NC_DEBUG(CODA_NC_PRINTCODA_NC, print_coda_nc(); ) 285} 286 287/* 288 * Find the (dir cnode, name) pair in the cache, if it's cred 289 * matches the input, return it, otherwise return 0 290 */ 291struct cnode * 292coda_nc_lookup(struct cnode *dcp, const char *name, int namelen, 293 kauth_cred_t cred) 294{ 295 int hash; 296 struct coda_cache *cncp; 297 298 if (coda_nc_use == 0) /* Cache is off */ 299 return((struct cnode *) 0); 300 301 if (namelen > CODA_NC_NAMELEN) { 302 CODA_NC_DEBUG(CODA_NC_LOOKUP, 303 myprintf(("long name lookup %s\n",name));) 304 coda_nc_stat.long_name_lookups++; /* record stats */ 305 return((struct cnode *) 0); 306 } 307 308 /* Use the hash function to locate the starting point, 309 then the search routine to go down the list looking for 310 the correct cred. 311 */ 312 313 hash = CODA_NC_HASH(name, namelen, dcp); 314 cncp = coda_nc_find(dcp, name, namelen, cred, hash); 315 if (cncp == (struct coda_cache *) 0) { 316 coda_nc_stat.misses++; /* record miss */ 317 return((struct cnode *) 0); 318 } 319 320 coda_nc_stat.hits++; 321 322 /* put this entry at the end of the LRU */ 323 TAILQ_REMOVE(&coda_nc_lru.head, cncp, lru); 324 TAILQ_INSERT_TAIL(&coda_nc_lru.head, cncp, lru); 325 326 /* move it to the front of the hash chain */ 327 /* don't need to change the hash bucket length */ 328 LIST_REMOVE(cncp, hash); 329 LIST_INSERT_HEAD(&coda_nc_hash[hash].head, cncp, hash); 330 331 CODA_NC_DEBUG(CODA_NC_LOOKUP, 332 printf("lookup: dcp %p, name %s, cred %p = cp %p\n", 333 dcp, name, cred, cncp->cp); ) 334 335 return(cncp->cp); 336} 337 338static void 339coda_nc_remove(struct coda_cache *cncp, enum dc_status dcstat) 340{ 341 /* 342 * remove an entry -- vrele(cncp->dcp, cp), crfree(cred), 343 * remove it from it's hash chain, and 344 * place it at the head of the lru list. 345 */ 346 CODA_NC_DEBUG(CODA_NC_REMOVE, 347 myprintf(("coda_nc_remove %s from parent %s\n", 348 cncp->name, coda_f2s(&cncp->dcp->c_fid))); ) 349 350 351 LIST_REMOVE(cncp, hash); 352 memset(&cncp->hash, 0, sizeof(cncp->hash)); 353 354 if ((dcstat == IS_DOWNCALL) && (CTOV(cncp->dcp)->v_usecount == 1)) { 355 cncp->dcp->c_flags |= C_PURGING; 356 } 357 vrele(CTOV(cncp->dcp)); 358 359 if ((dcstat == IS_DOWNCALL) && (CTOV(cncp->cp)->v_usecount == 1)) { 360 cncp->cp->c_flags |= C_PURGING; 361 } 362 vrele(CTOV(cncp->cp)); 363 364 kauth_cred_free(cncp->cred); 365 memset(DATA_PART(cncp), 0, DATA_SIZE); 366 367 /* move the null entry to the front for reuse */ 368 TAILQ_REMOVE(&coda_nc_lru.head, cncp, lru); 369 TAILQ_INSERT_HEAD(&coda_nc_lru.head, cncp, lru); 370} 371 372/* 373 * Remove all entries with a parent which has the input fid. 374 */ 375void 376coda_nc_zapParentfid(CodaFid *fid, enum dc_status dcstat) 377{ 378 /* To get to a specific fid, we might either have another hashing 379 function or do a sequential search through the cache for the 380 appropriate entries. The later may be acceptable since I don't 381 think callbacks or whatever Case 1 covers are frequent occurrences. 382 */ 383 struct coda_cache *cncp, *ncncp; 384 int i; 385 386 if (coda_nc_use == 0) /* Cache is off */ 387 return; 388 389 CODA_NC_DEBUG(CODA_NC_ZAPPFID, 390 myprintf(("ZapParent: fid %s\n", coda_f2s(fid))); ) 391 392 coda_nc_stat.zapPfids++; 393 394 for (i = 0; i < coda_nc_hashsize; i++) { 395 396 /* 397 * Need to save the hash_next pointer in case we remove the 398 * entry. remove causes hash_next to point to itself. 399 */ 400 401 ncncp = LIST_FIRST(&coda_nc_hash[i].head); 402 while ((cncp = ncncp) != NULL) { 403 ncncp = LIST_NEXT(cncp, hash); 404 405 if (coda_fid_eq(&(cncp->dcp->c_fid), fid)) { 406 coda_nc_hash[i].length--; /* Used for tuning */ 407 coda_nc_remove(cncp, dcstat); 408 } 409 } 410 } 411} 412 413/* 414 * Remove all entries which have the same fid as the input 415 */ 416void 417coda_nc_zapfid(CodaFid *fid, enum dc_status dcstat) 418{ 419 /* See comment for zapParentfid. This routine will be used 420 if attributes are being cached. 421 */ 422 struct coda_cache *cncp, *ncncp; 423 int i; 424 425 if (coda_nc_use == 0) /* Cache is off */ 426 return; 427 428 CODA_NC_DEBUG(CODA_NC_ZAPFID, 429 myprintf(("Zapfid: fid %s\n", coda_f2s(fid))); ) 430 431 coda_nc_stat.zapFids++; 432 433 for (i = 0; i < coda_nc_hashsize; i++) { 434 435 ncncp = LIST_FIRST(&coda_nc_hash[i].head); 436 while ((cncp = ncncp) != NULL) { 437 ncncp = LIST_NEXT(cncp, hash); 438 439 if (coda_fid_eq(&cncp->cp->c_fid, fid)) { 440 coda_nc_hash[i].length--; /* Used for tuning */ 441 coda_nc_remove(cncp, dcstat); 442 } 443 } 444 } 445} 446 447/* 448 * Remove all entries which match the fid and the cred 449 */ 450void 451coda_nc_zapvnode(CodaFid *fid, kauth_cred_t cred, 452 enum dc_status dcstat) 453{ 454 /* See comment for zapfid. I don't think that one would ever 455 want to zap a file with a specific cred from the kernel. 456 We'll leave this one unimplemented. 457 */ 458 if (coda_nc_use == 0) /* Cache is off */ 459 return; 460 461 CODA_NC_DEBUG(CODA_NC_ZAPVNODE, 462 myprintf(("Zapvnode: fid %s cred %p\n", 463 coda_f2s(fid), cred)); ) 464} 465 466/* 467 * Remove all entries which have the (dir vnode, name) pair 468 */ 469void 470coda_nc_zapfile(struct cnode *dcp, const char *name, int namelen) 471{ 472 /* use the hash function to locate the file, then zap all 473 entries of it regardless of the cred. 474 */ 475 struct coda_cache *cncp; 476 int hash; 477 478 if (coda_nc_use == 0) /* Cache is off */ 479 return; 480 481 CODA_NC_DEBUG(CODA_NC_ZAPFILE, 482 myprintf(("Zapfile: dcp %p name %s \n", 483 dcp, name)); ) 484 485 if (namelen > CODA_NC_NAMELEN) { 486 coda_nc_stat.long_remove++; /* record stats */ 487 return; 488 } 489 490 coda_nc_stat.zapFile++; 491 492 hash = CODA_NC_HASH(name, namelen, dcp); 493 cncp = coda_nc_find(dcp, name, namelen, 0, hash); 494 495 while (cncp) { 496 coda_nc_hash[hash].length--; /* Used for tuning */ 497/* 1.3 */ 498 coda_nc_remove(cncp, NOT_DOWNCALL); 499 cncp = coda_nc_find(dcp, name, namelen, 0, hash); 500 } 501} 502 503/* 504 * Remove all the entries for a particular user. Used when tokens expire. 505 * A user is determined by his/her effective user id (id_uid). 506 */ 507void 508coda_nc_purge_user(uid_t uid, enum dc_status dcstat) 509{ 510 /* 511 * I think the best approach is to go through the entire cache 512 * via HASH or whatever and zap all entries which match the 513 * input cred. Or just flush the whole cache. It might be 514 * best to go through on basis of LRU since cache will almost 515 * always be full and LRU is more straightforward. 516 */ 517 518 struct coda_cache *cncp, *ncncp; 519 int hash; 520 521 if (coda_nc_use == 0) /* Cache is off */ 522 return; 523 524 CODA_NC_DEBUG(CODA_NC_PURGEUSER, 525 myprintf(("ZapDude: uid %x\n", uid)); ) 526 coda_nc_stat.zapUsers++; 527 528 ncncp = TAILQ_FIRST(&coda_nc_lru.head); 529 while ((cncp = ncncp) != NULL) { 530 ncncp = TAILQ_NEXT(cncp, lru); 531 532 if ((CODA_NC_VALID(cncp)) && 533 (kauth_cred_geteuid(cncp->cred) == uid)) { 534 /* Seems really ugly, but we have to decrement the appropriate 535 hash bucket length here, so we have to find the hash bucket 536 */ 537 hash = CODA_NC_HASH(cncp->name, cncp->namelen, cncp->dcp); 538 coda_nc_hash[hash].length--; /* For performance tuning */ 539 540 coda_nc_remove(cncp, dcstat); 541 } 542 } 543} 544 545/* 546 * Flush the entire name cache. In response to a flush of the Venus cache. 547 */ 548void 549coda_nc_flush(enum dc_status dcstat) 550{ 551 /* One option is to deallocate the current name cache and 552 call init to start again. Or just deallocate, then rebuild. 553 Or again, we could just go through the array and zero the 554 appropriate fields. 555 */ 556 557 /* 558 * Go through the whole lru chain and kill everything as we go. 559 * I don't use remove since that would rebuild the lru chain 560 * as it went and that seemed unneccesary. 561 */ 562 struct coda_cache *cncp; 563 int i; 564 565 if (coda_nc_use == 0) /* Cache is off */ 566 return; 567 568 coda_nc_stat.Flushes++; 569 570 TAILQ_FOREACH(cncp, &coda_nc_lru.head, lru) { 571 if (CODA_NC_VALID(cncp)) { /* only zero valid nodes */ 572 LIST_REMOVE(cncp, hash); 573 memset(&cncp->hash, 0, sizeof(cncp->hash)); 574 575 if ((dcstat == IS_DOWNCALL) 576 && (CTOV(cncp->dcp)->v_usecount == 1)) 577 { 578 cncp->dcp->c_flags |= C_PURGING; 579 } 580 vrele(CTOV(cncp->dcp)); 581 582 if (CTOV(cncp->cp)->v_iflag & VI_TEXT) { 583 if (coda_vmflush(cncp->cp)) 584 CODADEBUG(CODA_FLUSH, 585 myprintf(("coda_nc_flush: %s busy\n", 586 coda_f2s(&cncp->cp->c_fid))); ) 587 } 588 589 if ((dcstat == IS_DOWNCALL) 590 && (CTOV(cncp->cp)->v_usecount == 1)) 591 { 592 cncp->cp->c_flags |= C_PURGING; 593 } 594 vrele(CTOV(cncp->cp)); 595 596 kauth_cred_free(cncp->cred); 597 memset(DATA_PART(cncp), 0, DATA_SIZE); 598 } 599 } 600 601 for (i = 0; i < coda_nc_hashsize; i++) 602 coda_nc_hash[i].length = 0; 603} 604 605/* 606 * Debugging routines 607 */ 608 609/* 610 * This routine should print out all the hash chains to the console. 611 */ 612void 613print_coda_nc(void) 614{ 615 int hash; 616 struct coda_cache *cncp; 617 618 for (hash = 0; hash < coda_nc_hashsize; hash++) { 619 myprintf(("\nhash %d\n",hash)); 620 621 LIST_FOREACH(cncp, &coda_nc_hash[hash].head, hash) { 622 myprintf(("cp %p dcp %p cred %p name %s\n", 623 cncp->cp, cncp->dcp, 624 cncp->cred, cncp->name)); 625 } 626 } 627} 628 629void 630coda_nc_gather_stats(void) 631{ 632 int i, xmax = 0, sum = 0, temp, zeros = 0, ave, n; 633 634 for (i = 0; i < coda_nc_hashsize; i++) { 635 if (coda_nc_hash[i].length) { 636 sum += coda_nc_hash[i].length; 637 } else { 638 zeros++; 639 } 640 641 if (coda_nc_hash[i].length > xmax) 642 xmax = coda_nc_hash[i].length; 643 } 644 645 /* 646 * When computing the Arithmetic mean, only count slots which 647 * are not empty in the distribution. 648 */ 649 coda_nc_stat.Sum_bucket_len = sum; 650 coda_nc_stat.Num_zero_len = zeros; 651 coda_nc_stat.Max_bucket_len = xmax; 652 653 if ((n = coda_nc_hashsize - zeros) > 0) 654 ave = sum / n; 655 else 656 ave = 0; 657 658 sum = 0; 659 for (i = 0; i < coda_nc_hashsize; i++) { 660 if (coda_nc_hash[i].length) { 661 temp = coda_nc_hash[i].length - ave; 662 sum += temp * temp; 663 } 664 } 665 coda_nc_stat.Sum2_bucket_len = sum; 666} 667 668/* 669 * The purpose of this routine is to allow the hash and cache sizes to be 670 * changed dynamically. This should only be used in controlled environments, 671 * it makes no effort to lock other users from accessing the cache while it 672 * is in an improper state (except by turning the cache off). 673 */ 674int 675coda_nc_resize(int hashsize, int heapsize, enum dc_status dcstat) 676{ 677 if ((hashsize % 2) || (heapsize % 2)) { /* Illegal hash or cache sizes */ 678 return(EINVAL); 679 } 680 681 coda_nc_use = 0; /* Turn the cache off */ 682 683 coda_nc_flush(dcstat); /* free any cnodes in the cache */ 684 685 /* WARNING: free must happen *before* size is reset */ 686 CODA_FREE(coda_nc_heap,TOTAL_CACHE_SIZE); 687 CODA_FREE(coda_nc_hash,TOTAL_HASH_SIZE); 688 689 coda_nc_hashsize = hashsize; 690 coda_nc_size = heapsize; 691 692 coda_nc_init(); /* Set up a cache with the new size */ 693 694 coda_nc_use = 1; /* Turn the cache back on */ 695 return(0); 696} 697 698char coda_nc_name_buf[CODA_MAXNAMLEN+1]; 699 700void 701coda_nc_name(struct cnode *cp) 702{ 703 struct coda_cache *cncp; 704 int i; 705 706 if (coda_nc_use == 0) /* Cache is off */ 707 return; 708 709 for (i = 0; i < coda_nc_hashsize; i++) { 710 711 LIST_FOREACH(cncp, &coda_nc_hash[i].head, hash) { 712 if (cncp->cp == cp) { 713 memcpy(coda_nc_name_buf, cncp->name, cncp->namelen); 714 coda_nc_name_buf[cncp->namelen] = 0; 715 printf(" is %s (%p,%p)@%p", 716 coda_nc_name_buf, cncp->cp, cncp->dcp, cncp); 717 } 718 719 } 720 } 721} 722