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