Home | History | Annotate | Line # | Download | only in coda
coda_namecache.c revision 1.21
      1 /*	$NetBSD: coda_namecache.c,v 1.21 2007/10/10 20:42:21 ad 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.21 2007/10/10 20:42:21 ad 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,
    460     enum dc_status dcstat)
    461 {
    462 	/* See comment for zapfid. I don't think that one would ever
    463 	   want to zap a file with a specific cred from the kernel.
    464 	   We'll leave this one unimplemented.
    465 	 */
    466 	if (coda_nc_use == 0)			/* Cache is off */
    467 		return;
    468 
    469 	CODA_NC_DEBUG(CODA_NC_ZAPVNODE,
    470 		myprintf(("Zapvnode: fid %s cred %p\n",
    471 			  coda_f2s(fid), cred)); )
    472 }
    473 
    474 /*
    475  * Remove all entries which have the (dir vnode, name) pair
    476  */
    477 void
    478 coda_nc_zapfile(struct cnode *dcp, const char *name, int namelen)
    479 {
    480 	/* use the hash function to locate the file, then zap all
    481  	   entries of it regardless of the cred.
    482 	 */
    483 	struct coda_cache *cncp;
    484 	int hash;
    485 
    486 	if (coda_nc_use == 0)			/* Cache is off */
    487 		return;
    488 
    489 	CODA_NC_DEBUG(CODA_NC_ZAPFILE,
    490 		myprintf(("Zapfile: dcp %p name %s \n",
    491 			  dcp, name)); )
    492 
    493 	if (namelen > CODA_NC_NAMELEN) {
    494 		coda_nc_stat.long_remove++;		/* record stats */
    495 		return;
    496 	}
    497 
    498 	coda_nc_stat.zapFile++;
    499 
    500 	hash = CODA_NC_HASH(name, namelen, dcp);
    501 	cncp = coda_nc_find(dcp, name, namelen, 0, hash);
    502 
    503 	while (cncp) {
    504 	  coda_nc_hash[hash].length--;                 /* Used for tuning */
    505 /* 1.3 */
    506 	  coda_nc_remove(cncp, NOT_DOWNCALL);
    507 	  cncp = coda_nc_find(dcp, name, namelen, 0, hash);
    508 	}
    509 }
    510 
    511 /*
    512  * Remove all the entries for a particular user. Used when tokens expire.
    513  * A user is determined by his/her effective user id (id_uid).
    514  */
    515 void
    516 coda_nc_purge_user(uid_t uid, enum dc_status dcstat)
    517 {
    518 	/*
    519 	 * I think the best approach is to go through the entire cache
    520 	 * via HASH or whatever and zap all entries which match the
    521 	 * input cred. Or just flush the whole cache.  It might be
    522 	 * best to go through on basis of LRU since cache will almost
    523 	 * always be full and LRU is more straightforward.
    524 	 */
    525 
    526 	struct coda_cache *cncp, *ncncp;
    527 	int hash;
    528 
    529 	if (coda_nc_use == 0)			/* Cache is off */
    530 		return;
    531 
    532 	CODA_NC_DEBUG(CODA_NC_PURGEUSER,
    533 		myprintf(("ZapDude: uid %x\n", uid)); )
    534 	coda_nc_stat.zapUsers++;
    535 
    536 	for (cncp = CODA_NC_LRUGET(coda_nc_lru);
    537 	     cncp != (struct coda_cache *)(&coda_nc_lru);
    538 	     cncp = ncncp) {
    539 		ncncp = CODA_NC_LRUGET(*cncp);
    540 
    541 		if ((CODA_NC_VALID(cncp)) &&
    542 		   (kauth_cred_geteuid(cncp->cred) == uid)) {
    543 		        /* Seems really ugly, but we have to decrement the appropriate
    544 			   hash bucket length here, so we have to find the hash bucket
    545 			   */
    546 		        hash = CODA_NC_HASH(cncp->name, cncp->namelen, cncp->dcp);
    547 			coda_nc_hash[hash].length--;     /* For performance tuning */
    548 
    549 			coda_nc_remove(cncp, dcstat);
    550 		}
    551 	}
    552 }
    553 
    554 /*
    555  * Flush the entire name cache. In response to a flush of the Venus cache.
    556  */
    557 void
    558 coda_nc_flush(enum dc_status dcstat)
    559 {
    560 	/* One option is to deallocate the current name cache and
    561 	   call init to start again. Or just deallocate, then rebuild.
    562 	   Or again, we could just go through the array and zero the
    563 	   appropriate fields.
    564 	 */
    565 
    566 	/*
    567 	 * Go through the whole lru chain and kill everything as we go.
    568 	 * I don't use remove since that would rebuild the lru chain
    569 	 * as it went and that seemed unneccesary.
    570 	 */
    571 	struct coda_cache *cncp;
    572 	int i;
    573 
    574 	if (coda_nc_use == 0)			/* Cache is off */
    575 		return;
    576 
    577 	coda_nc_stat.Flushes++;
    578 
    579 	for (cncp = CODA_NC_LRUGET(coda_nc_lru);
    580 	     cncp != (struct coda_cache *)&coda_nc_lru;
    581 	     cncp = CODA_NC_LRUGET(*cncp)) {
    582 		if (CODA_NC_VALID(cncp)) {
    583 
    584 			CODA_NC_HSHREM(cncp);	/* only zero valid nodes */
    585 			CODA_NC_HSHNUL(cncp);
    586 			if ((dcstat == IS_DOWNCALL)
    587 			    && (CTOV(cncp->dcp)->v_usecount == 1))
    588 			{
    589 				cncp->dcp->c_flags |= C_PURGING;
    590 			}
    591 			vrele(CTOV(cncp->dcp));
    592 
    593 			if (CTOV(cncp->cp)->v_iflag & VI_TEXT) {
    594 			    if (coda_vmflush(cncp->cp))
    595 				CODADEBUG(CODA_FLUSH,
    596 					myprintf(("coda_nc_flush: %s busy\n",
    597 						coda_f2s(&cncp->cp->c_fid))); )
    598 			}
    599 
    600 			if ((dcstat == IS_DOWNCALL)
    601 			    && (CTOV(cncp->cp)->v_usecount == 1))
    602 			{
    603 				cncp->cp->c_flags |= C_PURGING;
    604 			}
    605 			vrele(CTOV(cncp->cp));
    606 
    607 			kauth_cred_free(cncp->cred);
    608 			memset(DATA_PART(cncp), 0, DATA_SIZE);
    609 		}
    610 	}
    611 
    612 	for (i = 0; i < coda_nc_hashsize; i++)
    613 	  coda_nc_hash[i].length = 0;
    614 }
    615 
    616 /*
    617  * Debugging routines
    618  */
    619 
    620 /*
    621  * This routine should print out all the hash chains to the console.
    622  */
    623 void
    624 print_coda_nc(void)
    625 {
    626 	int hash;
    627 	struct coda_cache *cncp;
    628 
    629 	for (hash = 0; hash < coda_nc_hashsize; hash++) {
    630 		myprintf(("\nhash %d\n",hash));
    631 
    632 		for (cncp = coda_nc_hash[hash].hash_next;
    633 		     cncp != (struct coda_cache *)&coda_nc_hash[hash];
    634 		     cncp = cncp->hash_next) {
    635 			myprintf(("cp %p dcp %p cred %p name %s\n",
    636 				  cncp->cp, cncp->dcp,
    637 				  cncp->cred, cncp->name));
    638 		     }
    639 	}
    640 }
    641 
    642 void
    643 coda_nc_gather_stats(void)
    644 {
    645     int i, xmax = 0, sum = 0, temp, zeros = 0, ave, n;
    646 
    647 	for (i = 0; i < coda_nc_hashsize; i++) {
    648 	  if (coda_nc_hash[i].length) {
    649 	    sum += coda_nc_hash[i].length;
    650 	  } else {
    651 	    zeros++;
    652 	  }
    653 
    654 	  if (coda_nc_hash[i].length > xmax)
    655 	    xmax = coda_nc_hash[i].length;
    656 	}
    657 
    658 	/*
    659 	 * When computing the Arithmetic mean, only count slots which
    660 	 * are not empty in the distribution.
    661 	 */
    662         coda_nc_stat.Sum_bucket_len = sum;
    663         coda_nc_stat.Num_zero_len = zeros;
    664         coda_nc_stat.Max_bucket_len = xmax;
    665 
    666 	if ((n = coda_nc_hashsize - zeros) > 0)
    667 	  ave = sum / n;
    668 	else
    669 	  ave = 0;
    670 
    671 	sum = 0;
    672 	for (i = 0; i < coda_nc_hashsize; i++) {
    673 	  if (coda_nc_hash[i].length) {
    674 	    temp = coda_nc_hash[i].length - ave;
    675 	    sum += temp * temp;
    676 	  }
    677 	}
    678         coda_nc_stat.Sum2_bucket_len = sum;
    679 }
    680 
    681 /*
    682  * The purpose of this routine is to allow the hash and cache sizes to be
    683  * changed dynamically. This should only be used in controlled environments,
    684  * it makes no effort to lock other users from accessing the cache while it
    685  * is in an improper state (except by turning the cache off).
    686  */
    687 int
    688 coda_nc_resize(int hashsize, int heapsize, enum dc_status dcstat)
    689 {
    690     if ((hashsize % 2) || (heapsize % 2)) { /* Illegal hash or cache sizes */
    691 	return(EINVAL);
    692     }
    693 
    694     coda_nc_use = 0;                       /* Turn the cache off */
    695 
    696     coda_nc_flush(dcstat);                 /* free any cnodes in the cache */
    697 
    698     /* WARNING: free must happen *before* size is reset */
    699     CODA_FREE(coda_nc_heap,TOTAL_CACHE_SIZE);
    700     CODA_FREE(coda_nc_hash,TOTAL_HASH_SIZE);
    701 
    702     coda_nc_hashsize = hashsize;
    703     coda_nc_size = heapsize;
    704 
    705     coda_nc_init();                        /* Set up a cache with the new size */
    706 
    707     coda_nc_use = 1;                       /* Turn the cache back on */
    708     return(0);
    709 }
    710 
    711 char coda_nc_name_buf[CODA_MAXNAMLEN+1];
    712 
    713 void
    714 coda_nc_name(struct cnode *cp)
    715 {
    716 	struct coda_cache *cncp, *ncncp;
    717 	int i;
    718 
    719 	if (coda_nc_use == 0)			/* Cache is off */
    720 		return;
    721 
    722 	for (i = 0; i < coda_nc_hashsize; i++) {
    723 		for (cncp = coda_nc_hash[i].hash_next;
    724 		     cncp != (struct coda_cache *)&coda_nc_hash[i];
    725 		     cncp = ncncp) {
    726 			ncncp = cncp->hash_next;
    727 			if (cncp->cp == cp) {
    728 				bcopy(cncp->name, coda_nc_name_buf, cncp->namelen);
    729 				coda_nc_name_buf[cncp->namelen] = 0;
    730 				printf(" is %s (%p,%p)@%p",
    731 					coda_nc_name_buf, cncp->cp, cncp->dcp, cncp);
    732 			}
    733 
    734 		}
    735 	}
    736 }
    737