]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - 6/sys/coda/coda_namecache.c
merge fix for boot-time hang on centos' xen
[FreeBSD/FreeBSD.git] / 6 / sys / coda / coda_namecache.c
1 /*-
2  *             Coda: an Experimental Distributed File System
3  *                              Release 3.1
4  * 
5  *           Copyright (c) 1987-1998 Carnegie Mellon University
6  *                          All Rights Reserved
7  * 
8  * Permission  to  use, copy, modify and distribute this software and its
9  * documentation is hereby granted,  provided  that  both  the  copyright
10  * notice  and  this  permission  notice  appear  in  all  copies  of the
11  * software, derivative works or  modified  versions,  and  any  portions
12  * thereof, and that both notices appear in supporting documentation, and
13  * that credit is given to Carnegie Mellon University  in  all  documents
14  * and publicity pertaining to direct or indirect use of this code or its
15  * derivatives.
16  * 
17  * CODA IS AN EXPERIMENTAL SOFTWARE SYSTEM AND IS  KNOWN  TO  HAVE  BUGS,
18  * SOME  OF  WHICH MAY HAVE SERIOUS CONSEQUENCES.  CARNEGIE MELLON ALLOWS
19  * FREE USE OF THIS SOFTWARE IN ITS "AS IS" CONDITION.   CARNEGIE  MELLON
20  * DISCLAIMS  ANY  LIABILITY  OF  ANY  KIND  FOR  ANY  DAMAGES WHATSOEVER
21  * RESULTING DIRECTLY OR INDIRECTLY FROM THE USE OF THIS SOFTWARE  OR  OF
22  * ANY DERIVATIVE WORK.
23  * 
24  * Carnegie  Mellon  encourages  users  of  this  software  to return any
25  * improvements or extensions that  they  make,  and  to  grant  Carnegie
26  * Mellon the rights to redistribute these changes without encumbrance.
27  * 
28  *      @(#) src/sys/coda/coda_namecache.c,v 1.1.1.1 1998/08/29 21:14:52 rvb Exp $
29  */
30 /*-
31  * Mach Operating System
32  * Copyright (c) 1990 Carnegie-Mellon University
33  * Copyright (c) 1989 Carnegie-Mellon University
34  * All rights reserved.  The CMU software License Agreement specifies
35  * the terms and conditions for use and redistribution.
36  */
37
38 /*
39  * This code was written for the Coda filesystem at Carnegie Mellon University.
40  * Contributers include David Steere, James Kistler, and M. Satyanarayanan.
41  */
42
43 /*
44  * This module contains the routines to implement the CODA name cache. The
45  * purpose of this cache is to reduce the cost of translating pathnames 
46  * into Vice FIDs. Each entry in the cache contains the name of the file,
47  * the vnode (FID) of the parent directory, and the cred structure of the
48  * user accessing the file.
49  *
50  * The first time a file is accessed, it is looked up by the local Venus
51  * which first insures that the user has access to the file. In addition
52  * we are guaranteed that Venus will invalidate any name cache entries in
53  * case the user no longer should be able to access the file. For these
54  * reasons we do not need to keep access list information as well as a
55  * cred structure for each entry.
56  *
57  * The table can be accessed through the routines cnc_init(), cnc_enter(),
58  * cnc_lookup(), cnc_rmfidcred(), cnc_rmfid(), cnc_rmcred(), and cnc_purge().
59  * There are several other routines which aid in the implementation of the
60  * hash table.
61  */
62
63 /*
64  * NOTES: rvb@cs
65  * 1.   The name cache holds a reference to every vnode in it.  Hence files can not be
66  *       closed or made inactive until they are released.
67  * 2.   coda_nc_name(cp) was added to get a name for a cnode pointer for debugging.
68  * 3.   coda_nc_find() has debug code to detect when entries are stored with different
69  *       credentials.  We don't understand yet, if/how entries are NOT EQ but still
70  *       EQUAL
71  * 4.   I wonder if this name cache could be replace by the vnode name cache.
72  *      The latter has no zapping functions, so probably not.
73  */
74
75 #include <sys/cdefs.h>
76 __FBSDID("$FreeBSD$");
77
78 #include <sys/param.h>
79 #include <sys/systm.h>
80 #include <sys/errno.h>
81 #include <sys/lock.h>
82 #include <sys/malloc.h>
83 #include <sys/mutex.h>
84 #include <sys/ucred.h>
85
86 #include <vm/vm.h>
87 #include <vm/vm_object.h>
88
89 #include <coda/coda.h>
90 #include <coda/cnode.h>
91 #include <coda/coda_namecache.h>
92
93 #ifdef  DEBUG
94 #include <coda/coda_vnops.h>
95 #endif
96
97 /* 
98  * Declaration of the name cache data structure.
99  */
100
101 int     coda_nc_use = 1;                         /* Indicate use of CODA Name Cache */
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 coda_cache 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 *coda_nc_find(struct cnode *dcp, const char *name, int namelen,
120         struct ucred *cred, int hash);
121 static void coda_nc_remove(struct coda_cache *cncp, enum dc_status dcstat);
122
123 /*  
124  * Initialize the cache, the LRU structure and the Hash structure(s)
125  */
126
127 #define TOTAL_CACHE_SIZE        (sizeof(struct coda_cache) * coda_nc_size)
128 #define TOTAL_HASH_SIZE         (sizeof(struct coda_hash)  * coda_nc_hashsize)
129
130 int coda_nc_initialized = 0;      /* Initially the cache has not been initialized */
131
132 void
133 coda_nc_init(void)
134 {
135     int i;
136
137     /* zero the statistics structure */
138     
139     bzero(&coda_nc_stat, (sizeof(struct coda_nc_statistics)));
140
141 #ifdef  CODA_VERBOSE
142     printf("CODA NAME CACHE: CACHE %d, HASH TBL %d\n", CODA_NC_CACHESIZE, CODA_NC_HASHSIZE);
143 #endif
144     CODA_ALLOC(coda_nc_heap, struct coda_cache *, TOTAL_CACHE_SIZE);
145     CODA_ALLOC(coda_nc_hash, struct coda_hash *, TOTAL_HASH_SIZE);
146     
147     coda_nc_lru.lru_next = 
148         coda_nc_lru.lru_prev = (struct coda_cache *)LRU_PART(&coda_nc_lru);
149     
150     
151     for (i=0; i < coda_nc_size; i++) {  /* initialize the heap */
152         CODA_NC_LRUINS(&coda_nc_heap[i], &coda_nc_lru);
153         CODA_NC_HSHNUL(&coda_nc_heap[i]);
154         coda_nc_heap[i].cp = coda_nc_heap[i].dcp = (struct cnode *)0;
155     }
156     
157     for (i=0; i < coda_nc_hashsize; i++) {      /* initialize the hashtable */
158         CODA_NC_HSHNUL((struct coda_cache *)&coda_nc_hash[i]);
159     }
160     
161     coda_nc_initialized++;
162 }
163
164 /*
165  * Auxillary routines -- shouldn't be entry points
166  */
167
168 static struct coda_cache *
169 coda_nc_find(dcp, name, namelen, cred, hash)
170         struct cnode *dcp;
171         const char *name;
172         int namelen;
173         struct ucred *cred;
174         int hash;
175 {
176         /* 
177          * hash to find the appropriate bucket, look through the chain
178          * for the right entry (especially right cred, unless cred == 0) 
179          */
180         struct coda_cache *cncp;
181         int count = 1;
182
183         CODA_NC_DEBUG(CODA_NC_FIND, 
184                     myprintf(("coda_nc_find(dcp %p, name %s, len %d, cred %p, hash %d\n",
185                            dcp, name, namelen, cred, hash));)
186
187         for (cncp = coda_nc_hash[hash].hash_next; 
188              cncp != (struct coda_cache *)&coda_nc_hash[hash];
189              cncp = cncp->hash_next, count++) 
190         {
191
192             if ((CODA_NAMEMATCH(cncp, name, namelen, dcp)) &&
193                 ((cred == 0) || (cncp->cred == cred))) 
194             { 
195                 /* compare cr_uid instead */
196                 coda_nc_stat.Search_len += count;
197                 return(cncp);
198             }
199 #ifdef  DEBUG
200             else if (CODA_NAMEMATCH(cncp, name, namelen, dcp)) {
201                 printf("coda_nc_find: name %s, new cred = %p, cred = %p\n",
202                         name, cred, cncp->cred);
203                 printf("nref %d, nuid %d, ngid %d // oref %d, ocred %d, ogid %d\n",
204                         cred->cr_ref, cred->cr_uid, cred->cr_gid,
205                         cncp->cred->cr_ref, cncp->cred->cr_uid, cncp->cred->cr_gid);
206                 print_cred(cred);
207                 print_cred(cncp->cred);
208             }
209 #endif
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  */
219 void
220 coda_nc_enter(dcp, name, namelen, cred, cp)
221     struct cnode *dcp;
222     const char *name;
223     int namelen;
224     struct ucred *cred;
225     struct cnode *cp;
226 {
227     struct coda_cache *cncp;
228     int hash;
229     
230     if (coda_nc_use == 0)                       /* Cache is off */
231         return;
232     
233     CODA_NC_DEBUG(CODA_NC_ENTER, 
234                 myprintf(("Enter: dcp %p cp %p name %s cred %p \n",
235                        dcp, cp, name, cred)); )
236         
237     if (namelen > CODA_NC_NAMELEN) {
238         CODA_NC_DEBUG(CODA_NC_ENTER, 
239                     myprintf(("long name enter %s\n",name));)
240             coda_nc_stat.long_name_enters++;    /* record stats */
241         return;
242     }
243     
244     hash = CODA_NC_HASH(name, namelen, dcp);
245     cncp = coda_nc_find(dcp, name, namelen, cred, hash);
246     if (cncp != (struct coda_cache *) 0) {      
247         coda_nc_stat.dbl_enters++;              /* duplicate entry */
248         return;
249     }
250     
251     coda_nc_stat.enters++;              /* record the enters statistic */
252     
253     /* Grab the next element in the lru chain */
254     cncp = CODA_NC_LRUGET(coda_nc_lru);
255     
256     CODA_NC_LRUREM(cncp);       /* remove it from the lists */
257     
258     if (CODA_NC_VALID(cncp)) {
259         /* Seems really ugly, but we have to decrement the appropriate
260            hash bucket length here, so we have to find the hash bucket
261            */
262         coda_nc_hash[CODA_NC_HASH(cncp->name, cncp->namelen, cncp->dcp)].length--;
263         
264         coda_nc_stat.lru_rm++;  /* zapped a valid entry */
265         CODA_NC_HSHREM(cncp);
266         vrele(CTOV(cncp->dcp)); 
267         vrele(CTOV(cncp->cp));
268         crfree(cncp->cred);
269     }
270     
271     /*
272      * Put a hold on the current vnodes and fill in the cache entry.
273      */
274     vref(CTOV(cp));
275     vref(CTOV(dcp));
276     cncp->dcp = dcp;
277     cncp->cp = cp;
278     cncp->namelen = namelen;
279     cncp->cred = crhold(cred);
280     
281     bcopy(name, cncp->name, (unsigned)namelen);
282     
283     /* Insert into the lru and hash chains. */
284     
285     CODA_NC_LRUINS(cncp, &coda_nc_lru);
286     CODA_NC_HSHINS(cncp, &coda_nc_hash[hash]);
287     coda_nc_hash[hash].length++;                      /* Used for tuning */
288     
289     CODA_NC_DEBUG(CODA_NC_PRINTCODA_NC, print_coda_nc(); )
290 }
291
292 /*
293  * Find the (dir cnode, name) pair in the cache, if it's cred
294  * matches the input, return it, otherwise return 0
295  */
296 struct cnode *
297 coda_nc_lookup(dcp, name, namelen, cred)
298         struct cnode *dcp;
299         const char *name;
300         int namelen;
301         struct ucred *cred;
302 {
303         int hash;
304         struct coda_cache *cncp;
305
306         if (coda_nc_use == 0)                   /* Cache is off */
307                 return((struct cnode *) 0);
308
309         if (namelen > CODA_NC_NAMELEN) {
310                 CODA_NC_DEBUG(CODA_NC_LOOKUP, 
311                             myprintf(("long name lookup %s\n",name));)
312                 coda_nc_stat.long_name_lookups++;               /* record stats */
313                 return((struct cnode *) 0);
314         }
315
316         /* Use the hash function to locate the starting point,
317            then the search routine to go down the list looking for
318            the correct cred.
319          */
320
321         hash = CODA_NC_HASH(name, namelen, dcp);
322         cncp = coda_nc_find(dcp, name, namelen, cred, hash);
323         if (cncp == (struct coda_cache *) 0) {
324                 coda_nc_stat.misses++;                  /* record miss */
325                 return((struct cnode *) 0);
326         }
327
328         coda_nc_stat.hits++;
329
330         /* put this entry at the end of the LRU */
331         CODA_NC_LRUREM(cncp);
332         CODA_NC_LRUINS(cncp, &coda_nc_lru);
333
334         /* move it to the front of the hash chain */
335         /* don't need to change the hash bucket length */
336         CODA_NC_HSHREM(cncp);
337         CODA_NC_HSHINS(cncp, &coda_nc_hash[hash]);
338
339         CODA_NC_DEBUG(CODA_NC_LOOKUP, 
340                 printf("lookup: dcp %p, name %s, cred %p = cp %p\n",
341                         dcp, name, cred, cncp->cp); )
342
343         return(cncp->cp);
344 }
345
346 static void
347 coda_nc_remove(cncp, dcstat)
348         struct coda_cache *cncp;
349         enum dc_status dcstat;
350 {
351         /* 
352          * remove an entry -- vrele(cncp->dcp, cp), crfree(cred),
353          * remove it from it's hash chain, and
354          * place it at the head of the lru list.
355          */
356         CODA_NC_DEBUG(CODA_NC_REMOVE,
357                     myprintf(("coda_nc_remove %s from parent %s\n",
358                               cncp->name, coda_f2s(&cncp->dcp->c_fid))); )      
359         CODA_NC_HSHREM(cncp);
360
361         CODA_NC_HSHNUL(cncp);           /* have it be a null chain */
362         if ((dcstat == IS_DOWNCALL) && (vrefcnt(CTOV(cncp->dcp)) == 1)) {
363                 cncp->dcp->c_flags |= C_PURGING;
364         }
365         vrele(CTOV(cncp->dcp)); 
366
367         if ((dcstat == IS_DOWNCALL) && (vrefcnt(CTOV(cncp->cp)) == 1)) {
368                 cncp->cp->c_flags |= C_PURGING;
369         }
370         vrele(CTOV(cncp->cp)); 
371
372         crfree(cncp->cred); 
373         bzero(DATA_PART(cncp),DATA_SIZE);
374
375         /* Put the null entry just after the least-recently-used entry */
376         /* LRU_TOP adjusts the pointer to point to the top of the structure. */
377         CODA_NC_LRUREM(cncp);
378         CODA_NC_LRUINS(cncp, LRU_TOP(coda_nc_lru.lru_prev));
379 }
380
381 /*
382  * Remove all entries with a parent which has the input fid.
383  */
384 void
385 coda_nc_zapParentfid(fid, dcstat)
386         CodaFid *fid;
387         enum dc_status dcstat;
388 {
389         /* To get to a specific fid, we might either have another hashing
390            function or do a sequential search through the cache for the
391            appropriate entries. The later may be acceptable since I don't
392            think callbacks or whatever Case 1 covers are frequent occurences.
393          */
394         struct coda_cache *cncp, *ncncp;
395         int i;
396
397         if (coda_nc_use == 0)                   /* Cache is off */
398                 return; 
399
400         CODA_NC_DEBUG(CODA_NC_ZAPPFID, 
401                       myprintf(("ZapParent: fid %s\n", coda_f2s(fid))); )
402
403         coda_nc_stat.zapPfids++;
404
405         for (i = 0; i < coda_nc_hashsize; i++) {
406
407                 /*
408                  * Need to save the hash_next pointer in case we remove the
409                  * entry. remove causes hash_next to point to itself.
410                  */
411
412                 for (cncp = coda_nc_hash[i].hash_next; 
413                      cncp != (struct coda_cache *)&coda_nc_hash[i];
414                      cncp = ncncp) {
415                         ncncp = cncp->hash_next;
416                         if (coda_fid_eq(&(cncp->dcp->c_fid), fid)) {
417                                 coda_nc_hash[i].length--;      /* Used for tuning */
418                                 coda_nc_remove(cncp, dcstat); 
419                         }
420                 }
421         }
422 }
423
424
425 /*
426  * Remove all entries which have the same fid as the input
427  */
428 void
429 coda_nc_zapfid(fid, dcstat)
430         CodaFid *fid;
431         enum dc_status dcstat;
432 {
433         /* See comment for zapParentfid. This routine will be used
434            if attributes are being cached. 
435          */
436         struct coda_cache *cncp, *ncncp;
437         int i;
438
439         if (coda_nc_use == 0)                   /* Cache is off */
440                 return;
441
442         CODA_NC_DEBUG(CODA_NC_ZAPFID, 
443                       myprintf(("Zapfid: fid %s\n", coda_f2s(fid))); )
444
445         coda_nc_stat.zapFids++;
446
447         for (i = 0; i < coda_nc_hashsize; i++) {
448                 for (cncp = coda_nc_hash[i].hash_next; 
449                      cncp != (struct coda_cache *)&coda_nc_hash[i];
450                      cncp = ncncp) {
451                         ncncp = cncp->hash_next;
452                         if (coda_fid_eq(&cncp->cp->c_fid, fid)) {
453                             coda_nc_hash[i].length--;     /* Used for tuning */
454                             coda_nc_remove(cncp, dcstat); 
455                         }
456                 }
457         }
458 }
459
460 /* 
461  * Remove all entries which match the fid and the cred
462  */
463 void
464 coda_nc_zapvnode(fid, cred, dcstat)     
465         CodaFid *fid;
466         struct ucred *cred;
467         enum dc_status dcstat;
468 {
469         /* See comment for zapfid. I don't think that one would ever
470            want to zap a file with a specific cred from the kernel.
471            We'll leave this one unimplemented.
472          */
473
474         if (coda_nc_use == 0)                   /* Cache is off */
475                 return;
476
477         CODA_NC_DEBUG(CODA_NC_ZAPVNODE,
478                       myprintf(("Zapvnode: fid %s cred %p\n",
479                                 coda_f2s(fid), cred)); )
480
481  
482
483 }
484
485 /*
486  * Remove all entries which have the (dir vnode, name) pair
487  */
488 void
489 coda_nc_zapfile(dcp, name, namelen)
490         struct cnode *dcp;
491         const char *name;
492         int namelen;
493 {
494         /* use the hash function to locate the file, then zap all
495            entries of it regardless of the cred.
496          */
497         struct coda_cache *cncp;
498         int hash;
499
500         if (coda_nc_use == 0)                   /* Cache is off */
501                 return;
502
503         CODA_NC_DEBUG(CODA_NC_ZAPFILE, 
504                 myprintf(("Zapfile: dcp %p name %s \n",
505                           dcp, name)); )
506
507         if (namelen > CODA_NC_NAMELEN) {
508                 coda_nc_stat.long_remove++;             /* record stats */
509                 return;
510         }
511
512         coda_nc_stat.zapFile++;
513
514         hash = CODA_NC_HASH(name, namelen, dcp);
515         cncp = coda_nc_find(dcp, name, namelen, 0, hash);
516
517         while (cncp) {
518           coda_nc_hash[hash].length--;                 /* Used for tuning */
519
520           coda_nc_remove(cncp, NOT_DOWNCALL);
521           cncp = coda_nc_find(dcp, name, namelen, 0, hash);
522         }
523 }
524
525 /* 
526  * Remove all the entries for a particular user. Used when tokens expire.
527  * A user is determined by his/her effective user id (id_uid).
528  */
529 void
530 coda_nc_purge_user(uid, dcstat)
531         uid_t   uid;
532         enum dc_status  dcstat;
533 {
534         /* 
535          * I think the best approach is to go through the entire cache
536          * via HASH or whatever and zap all entries which match the
537          * input cred. Or just flush the whole cache.  It might be
538          * best to go through on basis of LRU since cache will almost
539          * always be full and LRU is more straightforward.  
540          */
541
542         struct coda_cache *cncp, *ncncp;
543         int hash;
544
545         if (coda_nc_use == 0)                   /* Cache is off */
546                 return;
547
548         CODA_NC_DEBUG(CODA_NC_PURGEUSER, 
549                 myprintf(("ZapDude: uid %x\n", uid)); )
550         coda_nc_stat.zapUsers++;
551
552         for (cncp = CODA_NC_LRUGET(coda_nc_lru);
553              cncp != (struct coda_cache *)(&coda_nc_lru);
554              cncp = ncncp) {
555                 ncncp = CODA_NC_LRUGET(*cncp);
556
557                 if ((CODA_NC_VALID(cncp)) &&
558                    ((cncp->cred)->cr_uid == uid)) {
559                         /* Seems really ugly, but we have to decrement the appropriate
560                            hash bucket length here, so we have to find the hash bucket
561                            */
562                         hash = CODA_NC_HASH(cncp->name, cncp->namelen, cncp->dcp);
563                         coda_nc_hash[hash].length--;     /* For performance tuning */
564
565                         coda_nc_remove(cncp, dcstat); 
566                 }
567         }
568 }
569
570 /*
571  * Flush the entire name cache. In response to a flush of the Venus cache.
572  */
573 void
574 coda_nc_flush(dcstat)
575         enum dc_status dcstat;
576 {
577         /* One option is to deallocate the current name cache and
578            call init to start again. Or just deallocate, then rebuild.
579            Or again, we could just go through the array and zero the 
580            appropriate fields. 
581          */
582         
583         /* 
584          * Go through the whole lru chain and kill everything as we go.
585          * I don't use remove since that would rebuild the lru chain
586          * as it went and that seemed unneccesary.
587          */
588         struct coda_cache *cncp;
589         int i;
590
591         if (coda_nc_use == 0)                   /* Cache is off */
592                 return;
593
594         coda_nc_stat.Flushes++;
595
596         for (cncp = CODA_NC_LRUGET(coda_nc_lru);
597              cncp != (struct coda_cache *)&coda_nc_lru;
598              cncp = CODA_NC_LRUGET(*cncp)) {
599                 if (CODA_NC_VALID(cncp)) {
600
601                         CODA_NC_HSHREM(cncp);   /* only zero valid nodes */
602                         CODA_NC_HSHNUL(cncp);
603                         if ((dcstat == IS_DOWNCALL) 
604                             && (vrefcnt(CTOV(cncp->dcp)) == 1))
605                         {
606                                 cncp->dcp->c_flags |= C_PURGING;
607                         }
608                         vrele(CTOV(cncp->dcp)); 
609
610                         ASSERT_VOP_LOCKED(CTOV(cncp->cp), "coda_nc_flush");
611                         if (CTOV(cncp->cp)->v_vflag & VV_TEXT) {
612                             if (coda_vmflush(cncp->cp))
613                                 CODADEBUG(CODA_FLUSH, 
614                         myprintf(("coda_nc_flush: %s busy\n",
615                                  coda_f2s(&cncp->cp->c_fid))); )
616                         }
617
618                         if ((dcstat == IS_DOWNCALL) 
619                             && (vrefcnt(CTOV(cncp->cp)) == 1))
620                         {
621                                 cncp->cp->c_flags |= C_PURGING;
622                         }
623                         vrele(CTOV(cncp->cp));  
624
625                         crfree(cncp->cred); 
626                         bzero(DATA_PART(cncp),DATA_SIZE);
627                 }
628         }
629
630         for (i = 0; i < coda_nc_hashsize; i++)
631           coda_nc_hash[i].length = 0;
632 }
633
634 /*
635  * Debugging routines
636  */
637
638 /* 
639  * This routine should print out all the hash chains to the console.
640  */
641 void
642 print_coda_nc(void)
643 {
644         int hash;
645         struct coda_cache *cncp;
646
647         for (hash = 0; hash < coda_nc_hashsize; hash++) {
648                 myprintf(("\nhash %d\n",hash));
649
650                 for (cncp = coda_nc_hash[hash].hash_next; 
651                      cncp != (struct coda_cache *)&coda_nc_hash[hash];
652                      cncp = cncp->hash_next) {
653                         myprintf(("cp %p dcp %p cred %p name %s\n",
654                                   cncp->cp, cncp->dcp,
655                                   cncp->cred, cncp->name));
656                      }
657         }
658 }
659
660 void
661 coda_nc_gather_stats(void)
662 {
663     int i, max = 0, sum = 0, temp, zeros = 0, ave, n;
664
665         for (i = 0; i < coda_nc_hashsize; i++) {
666           if (coda_nc_hash[i].length) {
667             sum += coda_nc_hash[i].length;
668           } else {
669             zeros++;
670           }
671
672           if (coda_nc_hash[i].length > max)
673             max = coda_nc_hash[i].length;
674         }
675
676         /*
677          * When computing the Arithmetic mean, only count slots which 
678          * are not empty in the distribution.
679          */
680         coda_nc_stat.Sum_bucket_len = sum;
681         coda_nc_stat.Num_zero_len = zeros;
682         coda_nc_stat.Max_bucket_len = max;
683
684         if ((n = coda_nc_hashsize - zeros) > 0) 
685           ave = sum / n;
686         else
687           ave = 0;
688
689         sum = 0;
690         for (i = 0; i < coda_nc_hashsize; i++) {
691           if (coda_nc_hash[i].length) {
692             temp = coda_nc_hash[i].length - ave;
693             sum += temp * temp;
694           }
695         }
696         coda_nc_stat.Sum2_bucket_len = sum;
697 }
698
699 /*
700  * The purpose of this routine is to allow the hash and cache sizes to be
701  * changed dynamically. This should only be used in controlled environments,
702  * it makes no effort to lock other users from accessing the cache while it
703  * is in an improper state (except by turning the cache off).
704  */
705 int
706 coda_nc_resize(hashsize, heapsize, dcstat)
707      int hashsize, heapsize;
708      enum dc_status dcstat;
709 {
710     if ((hashsize % 2) || (heapsize % 2)) { /* Illegal hash or cache sizes */
711         return(EINVAL);
712     }                 
713     
714     coda_nc_use = 0;                       /* Turn the cache off */
715     
716     coda_nc_flush(dcstat);                 /* free any cnodes in the cache */
717     
718     /* WARNING: free must happen *before* size is reset */
719     CODA_FREE(coda_nc_heap,TOTAL_CACHE_SIZE);
720     CODA_FREE(coda_nc_hash,TOTAL_HASH_SIZE);
721     
722     coda_nc_hashsize = hashsize;
723     coda_nc_size = heapsize;
724     
725     coda_nc_init();                        /* Set up a cache with the new size */
726     
727     coda_nc_use = 1;                       /* Turn the cache back on */
728     return(0);
729 }
730
731 #ifdef  DEBUG
732 char coda_nc_name_buf[CODA_MAXNAMLEN+1];
733
734 void
735 coda_nc_name(struct cnode *cp)
736 {
737         struct coda_cache *cncp, *ncncp;
738         int i;
739
740         if (coda_nc_use == 0)                   /* Cache is off */
741                 return;
742
743         for (i = 0; i < coda_nc_hashsize; i++) {
744                 for (cncp = coda_nc_hash[i].hash_next; 
745                      cncp != (struct coda_cache *)&coda_nc_hash[i];
746                      cncp = ncncp) {
747                         ncncp = cncp->hash_next;
748                         if (cncp->cp == cp) {
749                                 bcopy(cncp->name, coda_nc_name_buf, cncp->namelen);
750                                 coda_nc_name_buf[cncp->namelen] = 0;
751                                 printf(" is %s (%p,%p)@%p",
752                                         coda_nc_name_buf, cncp->cp, cncp->dcp, cncp);
753                         }
754
755                 }
756         }
757 }
758 #endif