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