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