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