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