Home | History | Annotate | Line # | Download | only in coda
coda_namecache.c revision 1.18
      1 /*	$NetBSD: coda_namecache.c,v 1.18 2006/05/14 21:24:49 elad 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  * This module contains the routines to implement the CODA name cache. The
     49  * purpose of this cache is to reduce the cost of translating pathnames
     50  * into Vice FIDs. Each entry in the cache contains the name of the file,
     51  * the vnode (FID) of the parent directory, and the cred structure of the
     52  * user accessing the file.
     53  *
     54  * The first time a file is accessed, it is looked up by the local Venus
     55  * which first insures that the user has access to the file. In addition
     56  * we are guaranteed that Venus will invalidate any name cache entries in
     57  * case the user no longer should be able to access the file. For these
     58  * reasons we do not need to keep access list information as well as a
     59  * cred structure for each entry.
     60  *
     61  * The table can be accessed through the routines cnc_init(), cnc_enter(),
     62  * cnc_lookup(), cnc_rmfidcred(), cnc_rmfid(), cnc_rmcred(), and cnc_purge().
     63  * There are several other routines which aid in the implementation of the
     64  * hash table.
     65  */
     66 
     67 /*
     68  * NOTES: rvb@cs
     69  * 1.	The name cache holds a reference to every vnode in it.  Hence files can not be
     70  *	 closed or made inactive until they are released.
     71  * 2.	coda_nc_name(cp) was added to get a name for a cnode pointer for debugging.
     72  * 3.	coda_nc_find() has debug code to detect when entries are stored with different
     73  *	 credentials.  We don't understand yet, if/how entries are NOT EQ but still
     74  *	 EQUAL
     75  * 4.	I wonder if this name cache could be replace by the vnode name cache.
     76  *	The latter has no zapping functions, so probably not.
     77  */
     78 
     79 #include <sys/cdefs.h>
     80 __KERNEL_RCSID(0, "$NetBSD: coda_namecache.c,v 1.18 2006/05/14 21:24:49 elad Exp $");
     81 
     82 #include <sys/param.h>
     83 #include <sys/errno.h>
     84 #include <sys/malloc.h>
     85 #include <sys/select.h>
     86 #include <sys/kauth.h>
     87 
     88 #include <coda/coda.h>
     89 #include <coda/cnode.h>
     90 #include <coda/coda_namecache.h>
     91 
     92 #ifdef	DEBUG
     93 #include <coda/coda_vnops.h>
     94 #endif
     95 
     96 #ifndef insque
     97 #include <sys/systm.h>
     98 #endif /* insque */
     99 
    100 /*
    101  * Declaration of the name cache data structure.
    102  */
    103 
    104 int 	coda_nc_use = 1;			 /* Indicate use of CODA Name Cache */
    105 
    106 int	coda_nc_size = CODA_NC_CACHESIZE;	 /* size of the cache */
    107 int	coda_nc_hashsize = CODA_NC_HASHSIZE; /* size of the primary hash */
    108 
    109 struct 	coda_cache *coda_nc_heap;	/* pointer to the cache entries */
    110 struct	coda_hash  *coda_nc_hash;	/* hash table of cfscache pointers */
    111 struct	coda_lru   coda_nc_lru;		/* head of lru chain */
    112 
    113 struct coda_nc_statistics coda_nc_stat;	/* Keep various stats */
    114 
    115 /*
    116  * for testing purposes
    117  */
    118 int coda_nc_debug = 0;
    119 
    120 /*
    121  * Entry points for the CODA Name Cache
    122  */
    123 static struct coda_cache *
    124 coda_nc_find(struct cnode *dcp, const char *name, int namelen,
    125 	kauth_cred_t cred, int hash);
    126 static void
    127 coda_nc_remove(struct coda_cache *cncp, enum dc_status dcstat);
    128 
    129 /*
    130  * Initialize the cache, the LRU structure and the Hash structure(s)
    131  */
    132 
    133 #define TOTAL_CACHE_SIZE 	(sizeof(struct coda_cache) * coda_nc_size)
    134 #define TOTAL_HASH_SIZE 	(sizeof(struct coda_hash)  * coda_nc_hashsize)
    135 
    136 int coda_nc_initialized = 0;      /* Initially the cache has not been initialized */
    137 
    138 void
    139 coda_nc_init(void)
    140 {
    141     int i;
    142 
    143     /* zero the statistics structure */
    144 
    145     memset(&coda_nc_stat, 0, (sizeof(struct coda_nc_statistics)));
    146 
    147 #ifdef	CODA_VERBOSE
    148     printf("CODA NAME CACHE: CACHE %d, HASH TBL %d\n", CODA_NC_CACHESIZE, CODA_NC_HASHSIZE);
    149 #endif
    150     CODA_ALLOC(coda_nc_heap, struct coda_cache *, TOTAL_CACHE_SIZE);
    151     CODA_ALLOC(coda_nc_hash, struct coda_hash *, TOTAL_HASH_SIZE);
    152 
    153     coda_nc_lru.lru_next =
    154 	coda_nc_lru.lru_prev = (struct coda_cache *)LRU_PART(&coda_nc_lru);
    155 
    156 
    157     for (i=0; i < coda_nc_size; i++) {	/* initialize the heap */
    158 	CODA_NC_LRUINS(&coda_nc_heap[i], &coda_nc_lru);
    159 	CODA_NC_HSHNUL(&coda_nc_heap[i]);
    160 	coda_nc_heap[i].cp = coda_nc_heap[i].dcp = (struct cnode *)0;
    161     }
    162 
    163     for (i=0; i < coda_nc_hashsize; i++) {	/* initialize the hashtable */
    164 	CODA_NC_HSHNUL((struct coda_cache *)&coda_nc_hash[i]);
    165     }
    166 
    167     coda_nc_initialized++;
    168 }
    169 
    170 /*
    171  * Auxillary routines -- shouldn't be entry points
    172  */
    173 
    174 static struct coda_cache *
    175 coda_nc_find(struct cnode *dcp, const char *name, int namelen,
    176 	kauth_cred_t cred, int hash)
    177 {
    178 	/*
    179 	 * hash to find the appropriate bucket, look through the chain
    180 	 * for the right entry (especially right cred, unless cred == 0)
    181 	 */
    182 	struct coda_cache *cncp;
    183 	int count = 1;
    184 
    185 	CODA_NC_DEBUG(CODA_NC_FIND,
    186 		myprintf(("coda_nc_find(dcp %p, name %s, len %d, cred %p, hash %d\n",
    187 			dcp, name, namelen, cred, hash));)
    188 
    189 	for (cncp = coda_nc_hash[hash].hash_next;
    190 	     cncp != (struct coda_cache *)&coda_nc_hash[hash];
    191 	     cncp = cncp->hash_next, count++)
    192 	{
    193 
    194 	    if ((CODA_NAMEMATCH(cncp, name, namelen, dcp)) &&
    195 		((cred == 0) || (cncp->cred == cred)))
    196 	    {
    197 		/* compare cr_uid instead */
    198 		coda_nc_stat.Search_len += count;
    199 		return(cncp);
    200 	    }
    201 #ifdef	DEBUG
    202 	    else if (CODA_NAMEMATCH(cncp, name, namelen, dcp)) {
    203 	    	printf("coda_nc_find: name %s, new cred = %p, cred = %p\n",
    204 			name, cred, cncp->cred);
    205 		printf("nref %d, nuid %d, ngid %d // oref %d, ocred %d, ogid %d\n",
    206 			kauth_cred_getrefcnt(cred),
    207 			kauth_cred_geteuid(cred),
    208 			kauth_cred_getegid(cred),
    209 			kauth_cred_getrefcnt(cncp->cred),
    210 			kauth_cred_geteuid(cncp->cred),
    211 			kauth_cred_getegid(cncp->cred));
    212 		print_cred(cred);
    213 		print_cred(cncp->cred);
    214 	    }
    215 #endif
    216 	}
    217 
    218 	return((struct coda_cache *)0);
    219 }
    220 
    221 /*
    222  * Enter a new (dir cnode, name) pair into the cache, updating the
    223  * LRU and Hash as needed.
    224  */
    225 void
    226 coda_nc_enter(struct cnode *dcp, const char *name, int namelen,
    227 	kauth_cred_t cred, struct cnode *cp)
    228 {
    229     struct coda_cache *cncp;
    230     int hash;
    231 
    232     if (coda_nc_use == 0)			/* Cache is off */
    233 	return;
    234 
    235     CODA_NC_DEBUG(CODA_NC_ENTER,
    236 		myprintf(("Enter: dcp %p cp %p name %s cred %p \n",
    237 		       dcp, cp, name, cred)); )
    238 
    239     if (namelen > CODA_NC_NAMELEN) {
    240 	CODA_NC_DEBUG(CODA_NC_ENTER,
    241 		    myprintf(("long name enter %s\n",name));)
    242 	    coda_nc_stat.long_name_enters++;	/* record stats */
    243 	return;
    244     }
    245 
    246     hash = CODA_NC_HASH(name, namelen, dcp);
    247     cncp = coda_nc_find(dcp, name, namelen, cred, hash);
    248     if (cncp != (struct coda_cache *) 0) {
    249 	coda_nc_stat.dbl_enters++;		/* duplicate entry */
    250 	return;
    251     }
    252 
    253     coda_nc_stat.enters++;		/* record the enters statistic */
    254 
    255     /* Grab the next element in the lru chain */
    256     cncp = CODA_NC_LRUGET(coda_nc_lru);
    257 
    258     CODA_NC_LRUREM(cncp);	/* remove it from the lists */
    259 
    260     if (CODA_NC_VALID(cncp)) {
    261 	/* Seems really ugly, but we have to decrement the appropriate
    262 	   hash bucket length here, so we have to find the hash bucket
    263 	   */
    264 	coda_nc_hash[CODA_NC_HASH(cncp->name, cncp->namelen, cncp->dcp)].length--;
    265 
    266 	coda_nc_stat.lru_rm++;	/* zapped a valid entry */
    267 	CODA_NC_HSHREM(cncp);
    268 	vrele(CTOV(cncp->dcp));
    269 	vrele(CTOV(cncp->cp));
    270 	kauth_cred_free(cncp->cred);
    271     }
    272 
    273     /*
    274      * Put a hold on the current vnodes and fill in the cache entry.
    275      */
    276     vref(CTOV(cp));
    277     vref(CTOV(dcp));
    278     kauth_cred_hold(cred);
    279     cncp->dcp = dcp;
    280     cncp->cp = cp;
    281     cncp->namelen = namelen;
    282     cncp->cred = cred;
    283 
    284     bcopy(name, cncp->name, (unsigned)namelen);
    285 
    286     /* Insert into the lru and hash chains. */
    287 
    288     CODA_NC_LRUINS(cncp, &coda_nc_lru);
    289     CODA_NC_HSHINS(cncp, &coda_nc_hash[hash]);
    290     coda_nc_hash[hash].length++;                      /* Used for tuning */
    291 
    292     CODA_NC_DEBUG(CODA_NC_PRINTCODA_NC, print_coda_nc(); )
    293 }
    294 
    295 /*
    296  * Find the (dir cnode, name) pair in the cache, if it's cred
    297  * matches the input, return it, otherwise return 0
    298  */
    299 struct cnode *
    300 coda_nc_lookup(struct cnode *dcp, const char *name, int namelen,
    301 	kauth_cred_t cred)
    302 {
    303 	int hash;
    304 	struct coda_cache *cncp;
    305 
    306 	if (coda_nc_use == 0)			/* Cache is off */
    307 		return((struct cnode *) 0);
    308 
    309 	if (namelen > CODA_NC_NAMELEN) {
    310 	        CODA_NC_DEBUG(CODA_NC_LOOKUP,
    311 			    myprintf(("long name lookup %s\n",name));)
    312 		coda_nc_stat.long_name_lookups++;		/* record stats */
    313 		return((struct cnode *) 0);
    314 	}
    315 
    316 	/* Use the hash function to locate the starting point,
    317 	   then the search routine to go down the list looking for
    318 	   the correct cred.
    319  	 */
    320 
    321 	hash = CODA_NC_HASH(name, namelen, dcp);
    322 	cncp = coda_nc_find(dcp, name, namelen, cred, hash);
    323 	if (cncp == (struct coda_cache *) 0) {
    324 		coda_nc_stat.misses++;			/* record miss */
    325 		return((struct cnode *) 0);
    326 	}
    327 
    328 	coda_nc_stat.hits++;
    329 
    330 	/* put this entry at the end of the LRU */
    331 	CODA_NC_LRUREM(cncp);
    332 	CODA_NC_LRUINS(cncp, &coda_nc_lru);
    333 
    334 	/* move it to the front of the hash chain */
    335 	/* don't need to change the hash bucket length */
    336 	CODA_NC_HSHREM(cncp);
    337 	CODA_NC_HSHINS(cncp, &coda_nc_hash[hash]);
    338 
    339 	CODA_NC_DEBUG(CODA_NC_LOOKUP,
    340 		printf("lookup: dcp %p, name %s, cred %p = cp %p\n",
    341 			dcp, name, cred, cncp->cp); )
    342 
    343 	return(cncp->cp);
    344 }
    345 
    346 static void
    347 coda_nc_remove(struct coda_cache *cncp, enum dc_status dcstat)
    348 {
    349 	/*
    350 	 * remove an entry -- vrele(cncp->dcp, cp), crfree(cred),
    351 	 * remove it from it's hash chain, and
    352 	 * place it at the head of the lru list.
    353 	 */
    354         CODA_NC_DEBUG(CODA_NC_REMOVE,
    355 		    myprintf(("coda_nc_remove %s from parent %s\n",
    356 			      cncp->name, coda_f2s(&cncp->dcp->c_fid))); )
    357 
    358 
    359   	CODA_NC_HSHREM(cncp);
    360 
    361 	CODA_NC_HSHNUL(cncp);		/* have it be a null chain */
    362 	if ((dcstat == IS_DOWNCALL) && (CTOV(cncp->dcp)->v_usecount == 1)) {
    363 		cncp->dcp->c_flags |= C_PURGING;
    364 	}
    365 	vrele(CTOV(cncp->dcp));
    366 
    367 	if ((dcstat == IS_DOWNCALL) && (CTOV(cncp->cp)->v_usecount == 1)) {
    368 		cncp->cp->c_flags |= C_PURGING;
    369 	}
    370 	vrele(CTOV(cncp->cp));
    371 
    372 	kauth_cred_free(cncp->cred);
    373 	memset(DATA_PART(cncp), 0, DATA_SIZE);
    374 
    375 	/* Put the null entry just after the least-recently-used entry */
    376 	/* LRU_TOP adjusts the pointer to point to the top of the structure. */
    377 	CODA_NC_LRUREM(cncp);
    378 	CODA_NC_LRUINS(cncp, LRU_TOP(coda_nc_lru.lru_prev));
    379 }
    380 
    381 /*
    382  * Remove all entries with a parent which has the input fid.
    383  */
    384 void
    385 coda_nc_zapParentfid(CodaFid *fid, enum dc_status dcstat)
    386 {
    387 	/* To get to a specific fid, we might either have another hashing
    388 	   function or do a sequential search through the cache for the
    389 	   appropriate entries. The later may be acceptable since I don't
    390 	   think callbacks or whatever Case 1 covers are frequent occurrences.
    391 	 */
    392 	struct coda_cache *cncp, *ncncp;
    393 	int i;
    394 
    395 	if (coda_nc_use == 0)			/* Cache is off */
    396 		return;
    397 
    398 	CODA_NC_DEBUG(CODA_NC_ZAPPFID,
    399 		myprintf(("ZapParent: fid %s\n", coda_f2s(fid))); )
    400 
    401 	coda_nc_stat.zapPfids++;
    402 
    403 	for (i = 0; i < coda_nc_hashsize; i++) {
    404 
    405 		/*
    406 		 * Need to save the hash_next pointer in case we remove the
    407 		 * entry. remove causes hash_next to point to itself.
    408 		 */
    409 
    410 		for (cncp = coda_nc_hash[i].hash_next;
    411 		     cncp != (struct coda_cache *)&coda_nc_hash[i];
    412 		     cncp = ncncp) {
    413 			ncncp = cncp->hash_next;
    414 			if (coda_fid_eq(&(cncp->dcp->c_fid), fid)) {
    415 			        coda_nc_hash[i].length--;      /* Used for tuning */
    416 				coda_nc_remove(cncp, dcstat);
    417 			}
    418 		}
    419 	}
    420 }
    421 
    422 /*
    423  * Remove all entries which have the same fid as the input
    424  */
    425 void
    426 coda_nc_zapfid(CodaFid *fid, enum dc_status dcstat)
    427 {
    428 	/* See comment for zapParentfid. This routine will be used
    429 	   if attributes are being cached.
    430 	 */
    431 	struct coda_cache *cncp, *ncncp;
    432 	int i;
    433 
    434 	if (coda_nc_use == 0)			/* Cache is off */
    435 		return;
    436 
    437 	CODA_NC_DEBUG(CODA_NC_ZAPFID,
    438 		myprintf(("Zapfid: fid %s\n", coda_f2s(fid))); )
    439 
    440 	coda_nc_stat.zapFids++;
    441 
    442 	for (i = 0; i < coda_nc_hashsize; i++) {
    443 		for (cncp = coda_nc_hash[i].hash_next;
    444 		     cncp != (struct coda_cache *)&coda_nc_hash[i];
    445 		     cncp = ncncp) {
    446 			ncncp = cncp->hash_next;
    447 			if (coda_fid_eq(&cncp->cp->c_fid, fid)) {
    448 			        coda_nc_hash[i].length--;     /* Used for tuning */
    449 				coda_nc_remove(cncp, dcstat);
    450 			}
    451 		}
    452 	}
    453 }
    454 
    455 /*
    456  * Remove all entries which match the fid and the cred
    457  */
    458 void
    459 coda_nc_zapvnode(CodaFid *fid, kauth_cred_t cred, enum dc_status dcstat)
    460 {
    461 	/* See comment for zapfid. I don't think that one would ever
    462 	   want to zap a file with a specific cred from the kernel.
    463 	   We'll leave this one unimplemented.
    464 	 */
    465 	if (coda_nc_use == 0)			/* Cache is off */
    466 		return;
    467 
    468 	CODA_NC_DEBUG(CODA_NC_ZAPVNODE,
    469 		myprintf(("Zapvnode: fid %s cred %p\n",
    470 			  coda_f2s(fid), cred)); )
    471 }
    472 
    473 /*
    474  * Remove all entries which have the (dir vnode, name) pair
    475  */
    476 void
    477 coda_nc_zapfile(struct cnode *dcp, const char *name, int namelen)
    478 {
    479 	/* use the hash function to locate the file, then zap all
    480  	   entries of it regardless of the cred.
    481 	 */
    482 	struct coda_cache *cncp;
    483 	int hash;
    484 
    485 	if (coda_nc_use == 0)			/* Cache is off */
    486 		return;
    487 
    488 	CODA_NC_DEBUG(CODA_NC_ZAPFILE,
    489 		myprintf(("Zapfile: dcp %p name %s \n",
    490 			  dcp, name)); )
    491 
    492 	if (namelen > CODA_NC_NAMELEN) {
    493 		coda_nc_stat.long_remove++;		/* record stats */
    494 		return;
    495 	}
    496 
    497 	coda_nc_stat.zapFile++;
    498 
    499 	hash = CODA_NC_HASH(name, namelen, dcp);
    500 	cncp = coda_nc_find(dcp, name, namelen, 0, hash);
    501 
    502 	while (cncp) {
    503 	  coda_nc_hash[hash].length--;                 /* Used for tuning */
    504 /* 1.3 */
    505 	  coda_nc_remove(cncp, NOT_DOWNCALL);
    506 	  cncp = coda_nc_find(dcp, name, namelen, 0, hash);
    507 	}
    508 }
    509 
    510 /*
    511  * Remove all the entries for a particular user. Used when tokens expire.
    512  * A user is determined by his/her effective user id (id_uid).
    513  */
    514 void
    515 coda_nc_purge_user(uid_t uid, enum dc_status dcstat)
    516 {
    517 	/*
    518 	 * I think the best approach is to go through the entire cache
    519 	 * via HASH or whatever and zap all entries which match the
    520 	 * input cred. Or just flush the whole cache.  It might be
    521 	 * best to go through on basis of LRU since cache will almost
    522 	 * always be full and LRU is more straightforward.
    523 	 */
    524 
    525 	struct coda_cache *cncp, *ncncp;
    526 	int hash;
    527 
    528 	if (coda_nc_use == 0)			/* Cache is off */
    529 		return;
    530 
    531 	CODA_NC_DEBUG(CODA_NC_PURGEUSER,
    532 		myprintf(("ZapDude: uid %x\n", uid)); )
    533 	coda_nc_stat.zapUsers++;
    534 
    535 	for (cncp = CODA_NC_LRUGET(coda_nc_lru);
    536 	     cncp != (struct coda_cache *)(&coda_nc_lru);
    537 	     cncp = ncncp) {
    538 		ncncp = CODA_NC_LRUGET(*cncp);
    539 
    540 		if ((CODA_NC_VALID(cncp)) &&
    541 		   (kauth_cred_geteuid(cncp->cred) == uid)) {
    542 		        /* Seems really ugly, but we have to decrement the appropriate
    543 			   hash bucket length here, so we have to find the hash bucket
    544 			   */
    545 		        hash = CODA_NC_HASH(cncp->name, cncp->namelen, cncp->dcp);
    546 			coda_nc_hash[hash].length--;     /* For performance tuning */
    547 
    548 			coda_nc_remove(cncp, dcstat);
    549 		}
    550 	}
    551 }
    552 
    553 /*
    554  * Flush the entire name cache. In response to a flush of the Venus cache.
    555  */
    556 void
    557 coda_nc_flush(enum dc_status dcstat)
    558 {
    559 	/* One option is to deallocate the current name cache and
    560 	   call init to start again. Or just deallocate, then rebuild.
    561 	   Or again, we could just go through the array and zero the
    562 	   appropriate fields.
    563 	 */
    564 
    565 	/*
    566 	 * Go through the whole lru chain and kill everything as we go.
    567 	 * I don't use remove since that would rebuild the lru chain
    568 	 * as it went and that seemed unneccesary.
    569 	 */
    570 	struct coda_cache *cncp;
    571 	int i;
    572 
    573 	if (coda_nc_use == 0)			/* Cache is off */
    574 		return;
    575 
    576 	coda_nc_stat.Flushes++;
    577 
    578 	for (cncp = CODA_NC_LRUGET(coda_nc_lru);
    579 	     cncp != (struct coda_cache *)&coda_nc_lru;
    580 	     cncp = CODA_NC_LRUGET(*cncp)) {
    581 		if (CODA_NC_VALID(cncp)) {
    582 
    583 			CODA_NC_HSHREM(cncp);	/* only zero valid nodes */
    584 			CODA_NC_HSHNUL(cncp);
    585 			if ((dcstat == IS_DOWNCALL)
    586 			    && (CTOV(cncp->dcp)->v_usecount == 1))
    587 			{
    588 				cncp->dcp->c_flags |= C_PURGING;
    589 			}
    590 			vrele(CTOV(cncp->dcp));
    591 
    592 			if (CTOV(cncp->cp)->v_flag & VTEXT) {
    593 			    if (coda_vmflush(cncp->cp))
    594 				CODADEBUG(CODA_FLUSH,
    595 					myprintf(("coda_nc_flush: %s busy\n",
    596 						coda_f2s(&cncp->cp->c_fid))); )
    597 			}
    598 
    599 			if ((dcstat == IS_DOWNCALL)
    600 			    && (CTOV(cncp->cp)->v_usecount == 1))
    601 			{
    602 				cncp->cp->c_flags |= C_PURGING;
    603 			}
    604 			vrele(CTOV(cncp->cp));
    605 
    606 			kauth_cred_free(cncp->cred);
    607 			memset(DATA_PART(cncp), 0, DATA_SIZE);
    608 		}
    609 	}
    610 
    611 	for (i = 0; i < coda_nc_hashsize; i++)
    612 	  coda_nc_hash[i].length = 0;
    613 }
    614 
    615 /*
    616  * Debugging routines
    617  */
    618 
    619 /*
    620  * This routine should print out all the hash chains to the console.
    621  */
    622 void
    623 print_coda_nc(void)
    624 {
    625 	int hash;
    626 	struct coda_cache *cncp;
    627 
    628 	for (hash = 0; hash < coda_nc_hashsize; hash++) {
    629 		myprintf(("\nhash %d\n",hash));
    630 
    631 		for (cncp = coda_nc_hash[hash].hash_next;
    632 		     cncp != (struct coda_cache *)&coda_nc_hash[hash];
    633 		     cncp = cncp->hash_next) {
    634 			myprintf(("cp %p dcp %p cred %p name %s\n",
    635 				  cncp->cp, cncp->dcp,
    636 				  cncp->cred, cncp->name));
    637 		     }
    638 	}
    639 }
    640 
    641 void
    642 coda_nc_gather_stats(void)
    643 {
    644     int i, xmax = 0, sum = 0, temp, zeros = 0, ave, n;
    645 
    646 	for (i = 0; i < coda_nc_hashsize; i++) {
    647 	  if (coda_nc_hash[i].length) {
    648 	    sum += coda_nc_hash[i].length;
    649 	  } else {
    650 	    zeros++;
    651 	  }
    652 
    653 	  if (coda_nc_hash[i].length > xmax)
    654 	    xmax = coda_nc_hash[i].length;
    655 	}
    656 
    657 	/*
    658 	 * When computing the Arithmetic mean, only count slots which
    659 	 * are not empty in the distribution.
    660 	 */
    661         coda_nc_stat.Sum_bucket_len = sum;
    662         coda_nc_stat.Num_zero_len = zeros;
    663         coda_nc_stat.Max_bucket_len = xmax;
    664 
    665 	if ((n = coda_nc_hashsize - zeros) > 0)
    666 	  ave = sum / n;
    667 	else
    668 	  ave = 0;
    669 
    670 	sum = 0;
    671 	for (i = 0; i < coda_nc_hashsize; i++) {
    672 	  if (coda_nc_hash[i].length) {
    673 	    temp = coda_nc_hash[i].length - ave;
    674 	    sum += temp * temp;
    675 	  }
    676 	}
    677         coda_nc_stat.Sum2_bucket_len = sum;
    678 }
    679 
    680 /*
    681  * The purpose of this routine is to allow the hash and cache sizes to be
    682  * changed dynamically. This should only be used in controlled environments,
    683  * it makes no effort to lock other users from accessing the cache while it
    684  * is in an improper state (except by turning the cache off).
    685  */
    686 int
    687 coda_nc_resize(int hashsize, int heapsize, enum dc_status dcstat)
    688 {
    689     if ((hashsize % 2) || (heapsize % 2)) { /* Illegal hash or cache sizes */
    690 	return(EINVAL);
    691     }
    692 
    693     coda_nc_use = 0;                       /* Turn the cache off */
    694 
    695     coda_nc_flush(dcstat);                 /* free any cnodes in the cache */
    696 
    697     /* WARNING: free must happen *before* size is reset */
    698     CODA_FREE(coda_nc_heap,TOTAL_CACHE_SIZE);
    699     CODA_FREE(coda_nc_hash,TOTAL_HASH_SIZE);
    700 
    701     coda_nc_hashsize = hashsize;
    702     coda_nc_size = heapsize;
    703 
    704     coda_nc_init();                        /* Set up a cache with the new size */
    705 
    706     coda_nc_use = 1;                       /* Turn the cache back on */
    707     return(0);
    708 }
    709 
    710 char coda_nc_name_buf[CODA_MAXNAMLEN+1];
    711 
    712 void
    713 coda_nc_name(struct cnode *cp)
    714 {
    715 	struct coda_cache *cncp, *ncncp;
    716 	int i;
    717 
    718 	if (coda_nc_use == 0)			/* Cache is off */
    719 		return;
    720 
    721 	for (i = 0; i < coda_nc_hashsize; i++) {
    722 		for (cncp = coda_nc_hash[i].hash_next;
    723 		     cncp != (struct coda_cache *)&coda_nc_hash[i];
    724 		     cncp = ncncp) {
    725 			ncncp = cncp->hash_next;
    726 			if (cncp->cp == cp) {
    727 				bcopy(cncp->name, coda_nc_name_buf, cncp->namelen);
    728 				coda_nc_name_buf[cncp->namelen] = 0;
    729 				printf(" is %s (%p,%p)@%p",
    730 					coda_nc_name_buf, cncp->cp, cncp->dcp, cncp);
    731 			}
    732 
    733 		}
    734 	}
    735 }
    736