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