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