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