coda_namecache.c revision 1.4
1/* $NetBSD: coda_namecache.c,v 1.4 1998/09/15 02:02:58 rvb 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 * HISTORY 49 * $Log: coda_namecache.c,v $ 50 * Revision 1.4 1998/09/15 02:02:58 rvb 51 * Final piece of rename cfs->coda 52 * 53 * Revision 1.3 1998/09/12 15:05:48 rvb 54 * Change cfs/CFS in symbols, strings and constants to coda/CODA 55 * to avoid fs conflicts. 56 * 57 * Revision 1.2 1998/09/08 17:12:46 rvb 58 * Pass2 complete 59 * 60 * Revision 1.1.1.1 1998/08/29 21:26:45 rvb 61 * Very Preliminary Coda 62 * 63 * Revision 1.11 1998/08/28 18:12:16 rvb 64 * Now it also works on FreeBSD -current. This code will be 65 * committed to the FreeBSD -current and NetBSD -current 66 * trees. It will then be tailored to the particular platform 67 * by flushing conditional code. 68 * 69 * Revision 1.10 1998/08/18 17:05:14 rvb 70 * Don't use __RCSID now 71 * 72 * Revision 1.9 1998/08/18 16:31:39 rvb 73 * Sync the code for NetBSD -current; test on 1.3 later 74 * 75 * Revision 1.8 98/01/31 20:53:10 rvb 76 * First version that works on FreeBSD 2.2.5 77 * 78 * Revision 1.7 98/01/23 11:53:39 rvb 79 * Bring RVB_CODA1_1 to HEAD 80 * 81 * Revision 1.6.2.4 98/01/23 11:21:02 rvb 82 * Sync with 2.2.5 83 * 84 * Revision 1.6.2.3 97/12/16 12:40:03 rvb 85 * Sync with 1.3 86 * 87 * Revision 1.6.2.2 97/12/09 16:07:10 rvb 88 * Sync with vfs/include/coda.h 89 * 90 * Revision 1.6.2.1 97/12/06 17:41:18 rvb 91 * Sync with peters coda.h 92 * 93 * Revision 1.6 97/12/05 10:39:13 rvb 94 * Read CHANGES 95 * 96 * Revision 1.5.4.7 97/11/25 08:08:43 rvb 97 * cfs_venus ... done; until cred/vattr change 98 * 99 * Revision 1.5.4.6 97/11/24 15:44:43 rvb 100 * Final cfs_venus.c w/o macros, but one locking bug 101 * 102 * Revision 1.5.4.5 97/11/20 11:46:38 rvb 103 * Capture current cfs_venus 104 * 105 * Revision 1.5.4.4 97/11/18 10:27:13 rvb 106 * cfs_nbsd.c is DEAD!!!; integrated into cfs_vf/vnops.c 107 * cfs_nb_foo and cfs_foo are joined 108 * 109 * Revision 1.5.4.3 97/11/13 22:02:57 rvb 110 * pass2 cfs_NetBSD.h mt 111 * 112 * Revision 1.5.4.2 97/11/12 12:09:35 rvb 113 * reorg pass1 114 * 115 * Revision 1.5.4.1 97/10/28 23:10:12 rvb 116 * >64Meg; venus can be killed! 117 * 118 * Revision 1.5 97/08/05 11:08:01 lily 119 * Removed cfsnc_replace, replaced it with a coda_find, unhash, and 120 * rehash. This fixes a cnode leak and a bug in which the fid is 121 * not actually replaced. (cfs_namecache.c, cfsnc.h, cfs_subr.c) 122 * 123 * Revision 1.4 96/12/12 22:10:57 bnoble 124 * Fixed the "downcall invokes venus operation" deadlock in all known cases. 125 * There may be more 126 * 127 * Revision 1.3 1996/11/08 18:06:09 bnoble 128 * Minor changes in vnode operation signature, VOP_UPDATE signature, and 129 * some newly defined bits in the include files. 130 * 131 * Revision 1.2 1996/01/02 16:56:50 bnoble 132 * Added support for Coda MiniCache and raw inode calls (final commit) 133 * 134 * Revision 1.1.2.1 1995/12/20 01:57:15 bnoble 135 * Added CODA-specific files 136 * 137 * Revision 3.1.1.1 1995/03/04 19:07:57 bnoble 138 * Branch for NetBSD port revisions 139 * 140 * Revision 3.1 1995/03/04 19:07:56 bnoble 141 * Bump to major revision 3 to prepare for NetBSD port 142 * 143 * Revision 2.3 1994/10/14 09:57:54 dcs 144 * Made changes 'cause sun4s have braindead compilers 145 * 146 * Revision 2.2 94/08/28 19:37:35 luqi 147 * Add a new CODA_REPLACE call to allow venus to replace a ViceFid in the 148 * mini-cache. 149 * 150 * In "cfs.h": 151 * Add CODA_REPLACE decl. 152 * 153 * In "cfs_namecache.c": 154 * Add routine cfsnc_replace. 155 * 156 * In "cfs_subr.c": 157 * Add case-statement to process CODA_REPLACE. 158 * 159 * In "cfsnc.h": 160 * Add decl for CODA_NC_REPLACE. 161 * 162 * 163 * Revision 2.1 94/07/21 16:25:15 satya 164 * Conversion to C++ 3.0; start of Coda Release 2.0 165 * 166 * Revision 1.2 92/10/27 17:58:21 lily 167 * merge kernel/latest and alpha/src/cfs 168 * 169 * Revision 2.3 92/09/30 14:16:20 mja 170 * call coda_flush instead of calling inode_uncache_try directly 171 * (from dcs). Also... 172 * 173 * Substituted rvb's history blurb so that we agree with Mach 2.5 sources. 174 * [91/02/09 jjk] 175 * 176 * Added contributors blurb. 177 * [90/12/13 jjk] 178 * 179 * Revision 2.2 90/07/05 11:26:30 mrt 180 * Created for the Coda File System. 181 * [90/05/23 dcs] 182 * 183 * Revision 1.3 90/05/31 17:01:24 dcs 184 * Prepare for merge with facilities kernel. 185 * 186 * 187 */ 188 189/* 190 * This module contains the routines to implement the CODA name cache. The 191 * purpose of this cache is to reduce the cost of translating pathnames 192 * into Vice FIDs. Each entry in the cache contains the name of the file, 193 * the vnode (FID) of the parent directory, and the cred structure of the 194 * user accessing the file. 195 * 196 * The first time a file is accessed, it is looked up by the local Venus 197 * which first insures that the user has access to the file. In addition 198 * we are guaranteed that Venus will invalidate any name cache entries in 199 * case the user no longer should be able to access the file. For these 200 * reasons we do not need to keep access list information as well as a 201 * cred structure for each entry. 202 * 203 * The table can be accessed through the routines cnc_init(), cnc_enter(), 204 * cnc_lookup(), cnc_rmfidcred(), cnc_rmfid(), cnc_rmcred(), and cnc_purge(). 205 * There are several other routines which aid in the implementation of the 206 * hash table. 207 */ 208 209/* 210 * NOTES: rvb@cs 211 * 1. The name cache holds a reference to every vnode in it. Hence files can not be 212 * closed or made inactive until they are released. 213 * 2. coda_nc_name(cp) was added to get a name for a cnode pointer for debugging. 214 * 3. coda_nc_find() has debug code to detect when entries are stored with different 215 * credentials. We don't understand yet, if/how entries are NOT EQ but still 216 * EQUAL 217 * 4. I wonder if this name cache could be replace by the vnode name cache. 218 * The latter has no zapping functions, so probably not. 219 */ 220 221#include <sys/param.h> 222#include <sys/errno.h> 223#include <sys/malloc.h> 224#include <sys/select.h> 225 226#include <coda/coda.h> 227#include <coda/cnode.h> 228#include <coda/coda_namecache.h> 229 230#ifndef insque 231#include <sys/systm.h> 232#endif /* insque */ 233 234/* 235 * Declaration of the name cache data structure. 236 */ 237 238int coda_nc_use = 1; /* Indicate use of CODA Name Cache */ 239 240int coda_nc_size = CODA_NC_CACHESIZE; /* size of the cache */ 241int coda_nc_hashsize = CODA_NC_HASHSIZE; /* size of the primary hash */ 242 243struct coda_cache *coda_nc_heap; /* pointer to the cache entries */ 244struct coda_hash *coda_nc_hash; /* hash table of cfscache pointers */ 245struct coda_lru coda_nc_lru; /* head of lru chain */ 246 247struct coda_nc_statistics coda_nc_stat; /* Keep various stats */ 248 249/* 250 * for testing purposes 251 */ 252int coda_nc_debug = 0; 253 254/* 255 * Entry points for the CODA Name Cache 256 */ 257static struct coda_cache * 258coda_nc_find(struct cnode *dcp, const char *name, int namelen, 259 struct ucred *cred, int hash); 260static void 261coda_nc_remove(struct coda_cache *cncp, enum dc_status dcstat); 262 263/* 264 * Initialize the cache, the LRU structure and the Hash structure(s) 265 */ 266 267#define TOTAL_CACHE_SIZE (sizeof(struct coda_cache) * coda_nc_size) 268#define TOTAL_HASH_SIZE (sizeof(struct coda_hash) * coda_nc_hashsize) 269 270int coda_nc_initialized = 0; /* Initially the cache has not been initialized */ 271 272void 273coda_nc_init(void) 274{ 275 int i; 276 277 /* zero the statistics structure */ 278 279 bzero(&coda_nc_stat, (sizeof(struct coda_nc_statistics))); 280 281 printf("CODA NAME CACHE: CACHE %d, HASH TBL %d\n", CODA_NC_CACHESIZE, CODA_NC_HASHSIZE); 282 CODA_ALLOC(coda_nc_heap, struct coda_cache *, TOTAL_CACHE_SIZE); 283 CODA_ALLOC(coda_nc_hash, struct coda_hash *, TOTAL_HASH_SIZE); 284 285 coda_nc_lru.lru_next = 286 coda_nc_lru.lru_prev = (struct coda_cache *)LRU_PART(&coda_nc_lru); 287 288 289 for (i=0; i < coda_nc_size; i++) { /* initialize the heap */ 290 CODA_NC_LRUINS(&coda_nc_heap[i], &coda_nc_lru); 291 CODA_NC_HSHNUL(&coda_nc_heap[i]); 292 coda_nc_heap[i].cp = coda_nc_heap[i].dcp = (struct cnode *)0; 293 } 294 295 for (i=0; i < coda_nc_hashsize; i++) { /* initialize the hashtable */ 296 CODA_NC_HSHNUL((struct coda_cache *)&coda_nc_hash[i]); 297 } 298 299 coda_nc_initialized++; 300} 301 302/* 303 * Auxillary routines -- shouldn't be entry points 304 */ 305 306static struct coda_cache * 307coda_nc_find(dcp, name, namelen, cred, hash) 308 struct cnode *dcp; 309 const char *name; 310 int namelen; 311 struct ucred *cred; 312 int hash; 313{ 314 /* 315 * hash to find the appropriate bucket, look through the chain 316 * for the right entry (especially right cred, unless cred == 0) 317 */ 318 struct coda_cache *cncp; 319 int count = 1; 320 321 CODA_NC_DEBUG(CODA_NC_FIND, 322 myprintf(("coda_nc_find(dcp %p, name %s, len %d, cred %p, hash %d\n", 323 dcp, name, namelen, cred, hash));) 324 325 for (cncp = coda_nc_hash[hash].hash_next; 326 cncp != (struct coda_cache *)&coda_nc_hash[hash]; 327 cncp = cncp->hash_next, count++) 328 { 329 330 if ((CODA_NAMEMATCH(cncp, name, namelen, dcp)) && 331 ((cred == 0) || (cncp->cred == cred))) 332 { 333 /* compare cr_uid instead */ 334 coda_nc_stat.Search_len += count; 335 return(cncp); 336 } 337#ifdef DEBUG 338 else if (CODA_NAMEMATCH(cncp, name, namelen, dcp)) { 339 printf("coda_nc_find: name %s, new cred = %p, cred = %p\n", 340 name, cred, cncp->cred); 341 printf("nref %d, nuid %d, ngid %d // oref %d, ocred %d, ogid %d\n", 342 cred->cr_ref, cred->cr_uid, cred->cr_gid, 343 cncp->cred->cr_ref, cncp->cred->cr_uid, cncp->cred->cr_gid); 344 print_cred(cred); 345 print_cred(cncp->cred); 346 } 347#endif 348 } 349 350 return((struct coda_cache *)0); 351} 352 353/* 354 * Enter a new (dir cnode, name) pair into the cache, updating the 355 * LRU and Hash as needed. 356 */ 357void 358coda_nc_enter(dcp, name, namelen, cred, cp) 359 struct cnode *dcp; 360 const char *name; 361 int namelen; 362 struct ucred *cred; 363 struct cnode *cp; 364{ 365 struct coda_cache *cncp; 366 int hash; 367 368 if (coda_nc_use == 0) /* Cache is off */ 369 return; 370 371 CODA_NC_DEBUG(CODA_NC_ENTER, 372 myprintf(("Enter: dcp %p cp %p name %s cred %p \n", 373 dcp, cp, name, cred)); ) 374 375 if (namelen > CODA_NC_NAMELEN) { 376 CODA_NC_DEBUG(CODA_NC_ENTER, 377 myprintf(("long name enter %s\n",name));) 378 coda_nc_stat.long_name_enters++; /* record stats */ 379 return; 380 } 381 382 hash = CODA_NC_HASH(name, namelen, dcp); 383 cncp = coda_nc_find(dcp, name, namelen, cred, hash); 384 if (cncp != (struct coda_cache *) 0) { 385 coda_nc_stat.dbl_enters++; /* duplicate entry */ 386 return; 387 } 388 389 coda_nc_stat.enters++; /* record the enters statistic */ 390 391 /* Grab the next element in the lru chain */ 392 cncp = CODA_NC_LRUGET(coda_nc_lru); 393 394 CODA_NC_LRUREM(cncp); /* remove it from the lists */ 395 396 if (CODA_NC_VALID(cncp)) { 397 /* Seems really ugly, but we have to decrement the appropriate 398 hash bucket length here, so we have to find the hash bucket 399 */ 400 coda_nc_hash[CODA_NC_HASH(cncp->name, cncp->namelen, cncp->dcp)].length--; 401 402 coda_nc_stat.lru_rm++; /* zapped a valid entry */ 403 CODA_NC_HSHREM(cncp); 404 vrele(CTOV(cncp->dcp)); 405 vrele(CTOV(cncp->cp)); 406 crfree(cncp->cred); 407 } 408 409 /* 410 * Put a hold on the current vnodes and fill in the cache entry. 411 */ 412 vref(CTOV(cp)); 413 vref(CTOV(dcp)); 414 crhold(cred); 415 cncp->dcp = dcp; 416 cncp->cp = cp; 417 cncp->namelen = namelen; 418 cncp->cred = cred; 419 420 bcopy(name, cncp->name, (unsigned)namelen); 421 422 /* Insert into the lru and hash chains. */ 423 424 CODA_NC_LRUINS(cncp, &coda_nc_lru); 425 CODA_NC_HSHINS(cncp, &coda_nc_hash[hash]); 426 coda_nc_hash[hash].length++; /* Used for tuning */ 427 428 CODA_NC_DEBUG(CODA_NC_PRINTCODA_NC, print_coda_nc(); ) 429} 430 431/* 432 * Find the (dir cnode, name) pair in the cache, if it's cred 433 * matches the input, return it, otherwise return 0 434 */ 435struct cnode * 436coda_nc_lookup(dcp, name, namelen, cred) 437 struct cnode *dcp; 438 const char *name; 439 int namelen; 440 struct ucred *cred; 441{ 442 int hash; 443 struct coda_cache *cncp; 444 445 if (coda_nc_use == 0) /* Cache is off */ 446 return((struct cnode *) 0); 447 448 if (namelen > CODA_NC_NAMELEN) { 449 CODA_NC_DEBUG(CODA_NC_LOOKUP, 450 myprintf(("long name lookup %s\n",name));) 451 coda_nc_stat.long_name_lookups++; /* record stats */ 452 return((struct cnode *) 0); 453 } 454 455 /* Use the hash function to locate the starting point, 456 then the search routine to go down the list looking for 457 the correct cred. 458 */ 459 460 hash = CODA_NC_HASH(name, namelen, dcp); 461 cncp = coda_nc_find(dcp, name, namelen, cred, hash); 462 if (cncp == (struct coda_cache *) 0) { 463 coda_nc_stat.misses++; /* record miss */ 464 return((struct cnode *) 0); 465 } 466 467 coda_nc_stat.hits++; 468 469 /* put this entry at the end of the LRU */ 470 CODA_NC_LRUREM(cncp); 471 CODA_NC_LRUINS(cncp, &coda_nc_lru); 472 473 /* move it to the front of the hash chain */ 474 /* don't need to change the hash bucket length */ 475 CODA_NC_HSHREM(cncp); 476 CODA_NC_HSHINS(cncp, &coda_nc_hash[hash]); 477 478 CODA_NC_DEBUG(CODA_NC_LOOKUP, 479 printf("lookup: dcp %p, name %s, cred %p = cp %p\n", 480 dcp, name, cred, cncp->cp); ) 481 482 return(cncp->cp); 483} 484 485static void 486coda_nc_remove(cncp, dcstat) 487 struct coda_cache *cncp; 488 enum dc_status dcstat; 489{ 490 /* 491 * remove an entry -- vrele(cncp->dcp, cp), crfree(cred), 492 * remove it from it's hash chain, and 493 * place it at the head of the lru list. 494 */ 495 CODA_NC_DEBUG(CODA_NC_REMOVE, 496 myprintf(("coda_nc_remove %s from parent %lx.%lx.%lx\n", 497 cncp->name, (cncp->dcp)->c_fid.Volume, 498 (cncp->dcp)->c_fid.Vnode, (cncp->dcp)->c_fid.Unique));) 499 500 CODA_NC_HSHREM(cncp); 501 502 CODA_NC_HSHNUL(cncp); /* have it be a null chain */ 503 if ((dcstat == IS_DOWNCALL) && (CTOV(cncp->dcp)->v_usecount == 1)) { 504 cncp->dcp->c_flags |= C_PURGING; 505 } 506 vrele(CTOV(cncp->dcp)); 507 508 if ((dcstat == IS_DOWNCALL) && (CTOV(cncp->cp)->v_usecount == 1)) { 509 cncp->cp->c_flags |= C_PURGING; 510 } 511 vrele(CTOV(cncp->cp)); 512 513 crfree(cncp->cred); 514 bzero(DATA_PART(cncp),DATA_SIZE); 515 516 /* Put the null entry just after the least-recently-used entry */ 517 /* LRU_TOP adjusts the pointer to point to the top of the structure. */ 518 CODA_NC_LRUREM(cncp); 519 CODA_NC_LRUINS(cncp, LRU_TOP(coda_nc_lru.lru_prev)); 520} 521 522/* 523 * Remove all entries with a parent which has the input fid. 524 */ 525void 526coda_nc_zapParentfid(fid, dcstat) 527 ViceFid *fid; 528 enum dc_status dcstat; 529{ 530 /* To get to a specific fid, we might either have another hashing 531 function or do a sequential search through the cache for the 532 appropriate entries. The later may be acceptable since I don't 533 think callbacks or whatever Case 1 covers are frequent occurences. 534 */ 535 struct coda_cache *cncp, *ncncp; 536 int i; 537 538 if (coda_nc_use == 0) /* Cache is off */ 539 return; 540 541 CODA_NC_DEBUG(CODA_NC_ZAPPFID, 542 myprintf(("ZapParent: fid 0x%lx, 0x%lx, 0x%lx \n", 543 fid->Volume, fid->Vnode, fid->Unique)); ) 544 545 coda_nc_stat.zapPfids++; 546 547 for (i = 0; i < coda_nc_hashsize; i++) { 548 549 /* 550 * Need to save the hash_next pointer in case we remove the 551 * entry. remove causes hash_next to point to itself. 552 */ 553 554 for (cncp = coda_nc_hash[i].hash_next; 555 cncp != (struct coda_cache *)&coda_nc_hash[i]; 556 cncp = ncncp) { 557 ncncp = cncp->hash_next; 558 if ((cncp->dcp->c_fid.Volume == fid->Volume) && 559 (cncp->dcp->c_fid.Vnode == fid->Vnode) && 560 (cncp->dcp->c_fid.Unique == fid->Unique)) { 561 coda_nc_hash[i].length--; /* Used for tuning */ 562 coda_nc_remove(cncp, dcstat); 563 } 564 } 565 } 566} 567 568/* 569 * Remove all entries which have the same fid as the input 570 */ 571void 572coda_nc_zapfid(fid, dcstat) 573 ViceFid *fid; 574 enum dc_status dcstat; 575{ 576 /* See comment for zapParentfid. This routine will be used 577 if attributes are being cached. 578 */ 579 struct coda_cache *cncp, *ncncp; 580 int i; 581 582 if (coda_nc_use == 0) /* Cache is off */ 583 return; 584 585 CODA_NC_DEBUG(CODA_NC_ZAPFID, 586 myprintf(("Zapfid: fid 0x%lx, 0x%lx, 0x%lx \n", 587 fid->Volume, fid->Vnode, fid->Unique)); ) 588 589 coda_nc_stat.zapFids++; 590 591 for (i = 0; i < coda_nc_hashsize; i++) { 592 for (cncp = coda_nc_hash[i].hash_next; 593 cncp != (struct coda_cache *)&coda_nc_hash[i]; 594 cncp = ncncp) { 595 ncncp = cncp->hash_next; 596 if ((cncp->cp->c_fid.Volume == fid->Volume) && 597 (cncp->cp->c_fid.Vnode == fid->Vnode) && 598 (cncp->cp->c_fid.Unique == fid->Unique)) { 599 coda_nc_hash[i].length--; /* Used for tuning */ 600 coda_nc_remove(cncp, dcstat); 601 } 602 } 603 } 604} 605 606/* 607 * Remove all entries which match the fid and the cred 608 */ 609void 610coda_nc_zapvnode(fid, cred, dcstat) 611 ViceFid *fid; 612 struct ucred *cred; 613 enum dc_status dcstat; 614{ 615 /* See comment for zapfid. I don't think that one would ever 616 want to zap a file with a specific cred from the kernel. 617 We'll leave this one unimplemented. 618 */ 619 if (coda_nc_use == 0) /* Cache is off */ 620 return; 621 622 CODA_NC_DEBUG(CODA_NC_ZAPVNODE, 623 myprintf(("Zapvnode: fid 0x%lx, 0x%lx, 0x%lx cred %p\n", 624 fid->Volume, fid->Vnode, fid->Unique, cred)); ) 625 626} 627 628/* 629 * Remove all entries which have the (dir vnode, name) pair 630 */ 631void 632coda_nc_zapfile(dcp, name, namelen) 633 struct cnode *dcp; 634 const char *name; 635 int namelen; 636{ 637 /* use the hash function to locate the file, then zap all 638 entries of it regardless of the cred. 639 */ 640 struct coda_cache *cncp; 641 int hash; 642 643 if (coda_nc_use == 0) /* Cache is off */ 644 return; 645 646 CODA_NC_DEBUG(CODA_NC_ZAPFILE, 647 myprintf(("Zapfile: dcp %p name %s \n", 648 dcp, name)); ) 649 650 if (namelen > CODA_NC_NAMELEN) { 651 coda_nc_stat.long_remove++; /* record stats */ 652 return; 653 } 654 655 coda_nc_stat.zapFile++; 656 657 hash = CODA_NC_HASH(name, namelen, dcp); 658 cncp = coda_nc_find(dcp, name, namelen, 0, hash); 659 660 while (cncp) { 661 coda_nc_hash[hash].length--; /* Used for tuning */ 662/* 1.3 */ 663 coda_nc_remove(cncp, NOT_DOWNCALL); 664 cncp = coda_nc_find(dcp, name, namelen, 0, hash); 665 } 666} 667 668/* 669 * Remove all the entries for a particular user. Used when tokens expire. 670 * A user is determined by his/her effective user id (id_uid). 671 */ 672void 673coda_nc_purge_user(uid, dcstat) 674 vuid_t uid; 675 enum dc_status dcstat; 676{ 677 /* 678 * I think the best approach is to go through the entire cache 679 * via HASH or whatever and zap all entries which match the 680 * input cred. Or just flush the whole cache. It might be 681 * best to go through on basis of LRU since cache will almost 682 * always be full and LRU is more straightforward. 683 */ 684 685 struct coda_cache *cncp, *ncncp; 686 int hash; 687 688 if (coda_nc_use == 0) /* Cache is off */ 689 return; 690 691 CODA_NC_DEBUG(CODA_NC_PURGEUSER, 692 myprintf(("ZapDude: uid %lx\n", uid)); ) 693 coda_nc_stat.zapUsers++; 694 695 for (cncp = CODA_NC_LRUGET(coda_nc_lru); 696 cncp != (struct coda_cache *)(&coda_nc_lru); 697 cncp = ncncp) { 698 ncncp = CODA_NC_LRUGET(*cncp); 699 700 if ((CODA_NC_VALID(cncp)) && 701 ((cncp->cred)->cr_uid == uid)) { 702 /* Seems really ugly, but we have to decrement the appropriate 703 hash bucket length here, so we have to find the hash bucket 704 */ 705 hash = CODA_NC_HASH(cncp->name, cncp->namelen, cncp->dcp); 706 coda_nc_hash[hash].length--; /* For performance tuning */ 707 708 coda_nc_remove(cncp, dcstat); 709 } 710 } 711} 712 713/* 714 * Flush the entire name cache. In response to a flush of the Venus cache. 715 */ 716void 717coda_nc_flush(dcstat) 718 enum dc_status dcstat; 719{ 720 /* One option is to deallocate the current name cache and 721 call init to start again. Or just deallocate, then rebuild. 722 Or again, we could just go through the array and zero the 723 appropriate fields. 724 */ 725 726 /* 727 * Go through the whole lru chain and kill everything as we go. 728 * I don't use remove since that would rebuild the lru chain 729 * as it went and that seemed unneccesary. 730 */ 731 struct coda_cache *cncp; 732 int i; 733 734 if (coda_nc_use == 0) /* Cache is off */ 735 return; 736 737 coda_nc_stat.Flushes++; 738 739 for (cncp = CODA_NC_LRUGET(coda_nc_lru); 740 cncp != (struct coda_cache *)&coda_nc_lru; 741 cncp = CODA_NC_LRUGET(*cncp)) { 742 if (CODA_NC_VALID(cncp)) { 743 744 CODA_NC_HSHREM(cncp); /* only zero valid nodes */ 745 CODA_NC_HSHNUL(cncp); 746 if ((dcstat == IS_DOWNCALL) 747 && (CTOV(cncp->dcp)->v_usecount == 1)) 748 { 749 cncp->dcp->c_flags |= C_PURGING; 750 } 751 vrele(CTOV(cncp->dcp)); 752 753 if (CTOV(cncp->cp)->v_flag & VTEXT) { 754 if (coda_vmflush(cncp->cp)) 755 CODADEBUG(CODA_FLUSH, 756 myprintf(("coda_nc_flush: (%lx.%lx.%lx) busy\n", cncp->cp->c_fid.Volume, cncp->cp->c_fid.Vnode, cncp->cp->c_fid.Unique)); ) 757 } 758 759 if ((dcstat == IS_DOWNCALL) 760 && (CTOV(cncp->cp)->v_usecount == 1)) 761 { 762 cncp->cp->c_flags |= C_PURGING; 763 } 764 vrele(CTOV(cncp->cp)); 765 766 crfree(cncp->cred); 767 bzero(DATA_PART(cncp),DATA_SIZE); 768 } 769 } 770 771 for (i = 0; i < coda_nc_hashsize; i++) 772 coda_nc_hash[i].length = 0; 773} 774 775/* 776 * Debugging routines 777 */ 778 779/* 780 * This routine should print out all the hash chains to the console. 781 */ 782void 783print_coda_nc(void) 784{ 785 int hash; 786 struct coda_cache *cncp; 787 788 for (hash = 0; hash < coda_nc_hashsize; hash++) { 789 myprintf(("\nhash %d\n",hash)); 790 791 for (cncp = coda_nc_hash[hash].hash_next; 792 cncp != (struct coda_cache *)&coda_nc_hash[hash]; 793 cncp = cncp->hash_next) { 794 myprintf(("cp %p dcp %p cred %p name %s\n", 795 cncp->cp, cncp->dcp, 796 cncp->cred, cncp->name)); 797 } 798 } 799} 800 801void 802coda_nc_gather_stats(void) 803{ 804 int i, max = 0, sum = 0, temp, zeros = 0, ave, n; 805 806 for (i = 0; i < coda_nc_hashsize; i++) { 807 if (coda_nc_hash[i].length) { 808 sum += coda_nc_hash[i].length; 809 } else { 810 zeros++; 811 } 812 813 if (coda_nc_hash[i].length > max) 814 max = coda_nc_hash[i].length; 815 } 816 817 /* 818 * When computing the Arithmetic mean, only count slots which 819 * are not empty in the distribution. 820 */ 821 coda_nc_stat.Sum_bucket_len = sum; 822 coda_nc_stat.Num_zero_len = zeros; 823 coda_nc_stat.Max_bucket_len = max; 824 825 if ((n = coda_nc_hashsize - zeros) > 0) 826 ave = sum / n; 827 else 828 ave = 0; 829 830 sum = 0; 831 for (i = 0; i < coda_nc_hashsize; i++) { 832 if (coda_nc_hash[i].length) { 833 temp = coda_nc_hash[i].length - ave; 834 sum += temp * temp; 835 } 836 } 837 coda_nc_stat.Sum2_bucket_len = sum; 838} 839 840/* 841 * The purpose of this routine is to allow the hash and cache sizes to be 842 * changed dynamically. This should only be used in controlled environments, 843 * it makes no effort to lock other users from accessing the cache while it 844 * is in an improper state (except by turning the cache off). 845 */ 846int 847coda_nc_resize(hashsize, heapsize, dcstat) 848 int hashsize, heapsize; 849 enum dc_status dcstat; 850{ 851 if ((hashsize % 2) || (heapsize % 2)) { /* Illegal hash or cache sizes */ 852 return(EINVAL); 853 } 854 855 coda_nc_use = 0; /* Turn the cache off */ 856 857 coda_nc_flush(dcstat); /* free any cnodes in the cache */ 858 859 /* WARNING: free must happen *before* size is reset */ 860 CODA_FREE(coda_nc_heap,TOTAL_CACHE_SIZE); 861 CODA_FREE(coda_nc_hash,TOTAL_HASH_SIZE); 862 863 coda_nc_hashsize = hashsize; 864 coda_nc_size = heapsize; 865 866 coda_nc_init(); /* Set up a cache with the new size */ 867 868 coda_nc_use = 1; /* Turn the cache back on */ 869 return(0); 870} 871 872char coda_nc_name_buf[CODA_MAXNAMLEN+1]; 873 874void 875coda_nc_name(struct cnode *cp) 876{ 877 struct coda_cache *cncp, *ncncp; 878 int i; 879 880 if (coda_nc_use == 0) /* Cache is off */ 881 return; 882 883 for (i = 0; i < coda_nc_hashsize; i++) { 884 for (cncp = coda_nc_hash[i].hash_next; 885 cncp != (struct coda_cache *)&coda_nc_hash[i]; 886 cncp = ncncp) { 887 ncncp = cncp->hash_next; 888 if (cncp->cp == cp) { 889 bcopy(cncp->name, coda_nc_name_buf, cncp->namelen); 890 coda_nc_name_buf[cncp->namelen] = 0; 891 printf(" is %s (%p,%p)@%p", 892 coda_nc_name_buf, cncp->cp, cncp->dcp, cncp); 893 } 894 895 } 896 } 897} 898