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