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
100int 	coda_nc_use = 1;			 /* Indicate use of CODA Name Cache */
101
102int	coda_nc_size = CODA_NC_CACHESIZE;	 /* size of the cache */
103int	coda_nc_hashsize = CODA_NC_HASHSIZE; /* size of the primary hash */
104
105struct 	coda_cache *coda_nc_heap;	/* pointer to the cache entries */
106struct	coda_hash  *coda_nc_hash;	/* hash table of cfscache pointers */
107struct	coda_lru   coda_nc_lru;		/* head of lru chain */
108
109struct coda_nc_statistics coda_nc_stat;	/* Keep various stats */
110
111/*
112 * for testing purposes
113 */
114int coda_nc_debug = 0;
115
116/*
117 * Entry points for the CODA Name Cache
118 */
119static struct coda_cache *
120coda_nc_find(struct cnode *dcp, const char *name, int namelen,
121	struct ucred *cred, int hash);
122static void
123coda_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
132int coda_nc_initialized = 0;      /* Initially the cache has not been initialized */
133
134void
135coda_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
170static struct coda_cache *
171coda_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 */
221void
222coda_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 */
299struct cnode *
300coda_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
349static void
350coda_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 */
389void
390coda_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 */
435void
436coda_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 */
473void
474coda_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 */
495void
496coda_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 */
536void
537coda_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 */
580void
581coda_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 */
646void
647print_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
665void
666coda_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 */
710int
711coda_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
736char coda_nc_name_buf[CODA_MAXNAMLEN+1];
737
738void
739coda_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