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