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