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