xref: /netbsd-src/sys/coda/coda_namecache.c (revision 3b01aba77a7a698587faaae455bbfe740923c1f5)
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