coda_namecache.c revision 1.2
1/*	$NetBSD: coda_namecache.c,v 1.2 1998/09/08 17:12:46 rvb 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 * 	@(#) cfs/cfs_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 * HISTORY
49 * $Log: coda_namecache.c,v $
50 * Revision 1.2  1998/09/08 17:12:46  rvb
51 * Pass2 complete
52 *
53 * Revision 1.1.1.1  1998/08/29 21:26:45  rvb
54 * Very Preliminary Coda
55 *
56 * Revision 1.11  1998/08/28 18:12:16  rvb
57 * Now it also works on FreeBSD -current.  This code will be
58 * committed to the FreeBSD -current and NetBSD -current
59 * trees.  It will then be tailored to the particular platform
60 * by flushing conditional code.
61 *
62 * Revision 1.10  1998/08/18 17:05:14  rvb
63 * Don't use __RCSID now
64 *
65 * Revision 1.9  1998/08/18 16:31:39  rvb
66 * Sync the code for NetBSD -current; test on 1.3 later
67 *
68 * Revision 1.8  98/01/31  20:53:10  rvb
69 * First version that works on FreeBSD 2.2.5
70 *
71 * Revision 1.7  98/01/23  11:53:39  rvb
72 * Bring RVB_CFS1_1 to HEAD
73 *
74 * Revision 1.6.2.4  98/01/23  11:21:02  rvb
75 * Sync with 2.2.5
76 *
77 * Revision 1.6.2.3  97/12/16  12:40:03  rvb
78 * Sync with 1.3
79 *
80 * Revision 1.6.2.2  97/12/09  16:07:10  rvb
81 * Sync with vfs/include/coda.h
82 *
83 * Revision 1.6.2.1  97/12/06  17:41:18  rvb
84 * Sync with peters coda.h
85 *
86 * Revision 1.6  97/12/05  10:39:13  rvb
87 * Read CHANGES
88 *
89 * Revision 1.5.4.7  97/11/25  08:08:43  rvb
90 * cfs_venus ... done; until cred/vattr change
91 *
92 * Revision 1.5.4.6  97/11/24  15:44:43  rvb
93 * Final cfs_venus.c w/o macros, but one locking bug
94 *
95 * Revision 1.5.4.5  97/11/20  11:46:38  rvb
96 * Capture current cfs_venus
97 *
98 * Revision 1.5.4.4  97/11/18  10:27:13  rvb
99 * cfs_nbsd.c is DEAD!!!; integrated into cfs_vf/vnops.c
100 * cfs_nb_foo and cfs_foo are joined
101 *
102 * Revision 1.5.4.3  97/11/13  22:02:57  rvb
103 * pass2 cfs_NetBSD.h mt
104 *
105 * Revision 1.5.4.2  97/11/12  12:09:35  rvb
106 * reorg pass1
107 *
108 * Revision 1.5.4.1  97/10/28  23:10:12  rvb
109 * >64Meg; venus can be killed!
110 *
111 * Revision 1.5  97/08/05  11:08:01  lily
112 * Removed cfsnc_replace, replaced it with a cfs_find, unhash, and
113 * rehash.  This fixes a cnode leak and a bug in which the fid is
114 * not actually replaced.  (cfs_namecache.c, cfsnc.h, cfs_subr.c)
115 *
116 * Revision 1.4  96/12/12  22:10:57  bnoble
117 * Fixed the "downcall invokes venus operation" deadlock in all known cases.
118 * There may be more
119 *
120 * Revision 1.3  1996/11/08 18:06:09  bnoble
121 * Minor changes in vnode operation signature, VOP_UPDATE signature, and
122 * some newly defined bits in the include files.
123 *
124 * Revision 1.2  1996/01/02 16:56:50  bnoble
125 * Added support for Coda MiniCache and raw inode calls (final commit)
126 *
127 * Revision 1.1.2.1  1995/12/20 01:57:15  bnoble
128 * Added CFS-specific files
129 *
130 * Revision 3.1.1.1  1995/03/04  19:07:57  bnoble
131 * Branch for NetBSD port revisions
132 *
133 * Revision 3.1  1995/03/04  19:07:56  bnoble
134 * Bump to major revision 3 to prepare for NetBSD port
135 *
136 * Revision 2.3  1994/10/14  09:57:54  dcs
137 * Made changes 'cause sun4s have braindead compilers
138 *
139 * Revision 2.2  94/08/28  19:37:35  luqi
140 * Add a new CFS_REPLACE call to allow venus to replace a ViceFid in the
141 * mini-cache.
142 *
143 * In "cfs.h":
144 * Add CFS_REPLACE decl.
145 *
146 * In "cfs_namecache.c":
147 * Add routine cfsnc_replace.
148 *
149 * In "cfs_subr.c":
150 * Add case-statement to process CFS_REPLACE.
151 *
152 * In "cfsnc.h":
153 * Add decl for CFSNC_REPLACE.
154 *
155 *
156 * Revision 2.1  94/07/21  16:25:15  satya
157 * Conversion to C++ 3.0; start of Coda Release 2.0
158 *
159 * Revision 1.2  92/10/27  17:58:21  lily
160 * merge kernel/latest and alpha/src/cfs
161 *
162 * Revision 2.3  92/09/30  14:16:20  mja
163 * 	call cfs_flush instead of calling inode_uncache_try directly
164 * 	(from dcs). Also...
165 *
166 * 	Substituted rvb's history blurb so that we agree with Mach 2.5 sources.
167 * 	[91/02/09            jjk]
168 *
169 * 	Added contributors blurb.
170 * 	[90/12/13            jjk]
171 *
172 * Revision 2.2  90/07/05  11:26:30  mrt
173 * 	Created for the Coda File System.
174 * 	[90/05/23            dcs]
175 *
176 * Revision 1.3  90/05/31  17:01:24  dcs
177 * Prepare for merge with facilities kernel.
178 *
179 *
180 */
181
182/*
183 * This module contains the routines to implement the CFS name cache. The
184 * purpose of this cache is to reduce the cost of translating pathnames
185 * into Vice FIDs. Each entry in the cache contains the name of the file,
186 * the vnode (FID) of the parent directory, and the cred structure of the
187 * user accessing the file.
188 *
189 * The first time a file is accessed, it is looked up by the local Venus
190 * which first insures that the user has access to the file. In addition
191 * we are guaranteed that Venus will invalidate any name cache entries in
192 * case the user no longer should be able to access the file. For these
193 * reasons we do not need to keep access list information as well as a
194 * cred structure for each entry.
195 *
196 * The table can be accessed through the routines cnc_init(), cnc_enter(),
197 * cnc_lookup(), cnc_rmfidcred(), cnc_rmfid(), cnc_rmcred(), and cnc_purge().
198 * There are several other routines which aid in the implementation of the
199 * hash table.
200 */
201
202/*
203 * NOTES: rvb@cs
204 * 1.	The name cache holds a reference to every vnode in it.  Hence files can not be
205 *	 closed or made inactive until they are released.
206 * 2.	cfsnc_name(cp) was added to get a name for a cnode pointer for debugging.
207 * 3.	cfsnc_find() has debug code to detect when entries are stored with different
208 *	 credentials.  We don't understand yet, if/how entries are NOT EQ but still
209 *	 EQUAL
210 * 4.	I wonder if this name cache could be replace by the vnode name cache.
211 *	The latter has no zapping functions, so probably not.
212 */
213
214#include <sys/param.h>
215#include <sys/errno.h>
216#include <sys/malloc.h>
217#include <sys/select.h>
218
219#include <cfs/coda.h>
220#include <cfs/cnode.h>
221#include <cfs/cfsnc.h>
222
223#ifndef insque
224#include <sys/systm.h>
225#endif /* insque */
226
227/*
228 * Declaration of the name cache data structure.
229 */
230
231int 	cfsnc_use = 1;			 /* Indicate use of CFS Name Cache */
232
233int	cfsnc_size = CFSNC_CACHESIZE;	 /* size of the cache */
234int	cfsnc_hashsize = CFSNC_HASHSIZE; /* size of the primary hash */
235
236struct 	cfscache *cfsncheap;	/* pointer to the cache entries */
237struct	cfshash  *cfsnchash;	/* hash table of cfscache pointers */
238struct	cfslru   cfsnc_lru;	/* head of lru chain */
239
240struct cfsnc_statistics cfsnc_stat;	/* Keep various stats */
241
242/*
243 * for testing purposes
244 */
245int cfsnc_debug = 0;
246
247/*
248 * Entry points for the CFS Name Cache
249 */
250static struct cfscache *
251cfsnc_find(struct cnode *dcp, const char *name, int namelen,
252	struct ucred *cred, int hash);
253static void
254cfsnc_remove(struct cfscache *cncp, enum dc_status dcstat);
255
256/*
257 * Initialize the cache, the LRU structure and the Hash structure(s)
258 */
259
260#define TOTAL_CACHE_SIZE 	(sizeof(struct cfscache) * cfsnc_size)
261#define TOTAL_HASH_SIZE 	(sizeof(struct cfshash)  * cfsnc_hashsize)
262
263int cfsnc_initialized = 0;      /* Initially the cache has not been initialized */
264
265void
266cfsnc_init(void)
267{
268    int i;
269
270    /* zero the statistics structure */
271
272    bzero(&cfsnc_stat, (sizeof(struct cfsnc_statistics)));
273
274    printf("CFS NAME CACHE: CACHE %d, HASH TBL %d\n", CFSNC_CACHESIZE, CFSNC_HASHSIZE);
275    CFS_ALLOC(cfsncheap, struct cfscache *, TOTAL_CACHE_SIZE);
276    CFS_ALLOC(cfsnchash, struct cfshash *, TOTAL_HASH_SIZE);
277
278    cfsnc_lru.lru_next =
279	cfsnc_lru.lru_prev = (struct cfscache *)LRU_PART(&cfsnc_lru);
280
281
282    for (i=0; i < cfsnc_size; i++) {	/* initialize the heap */
283	CFSNC_LRUINS(&cfsncheap[i], &cfsnc_lru);
284	CFSNC_HSHNUL(&cfsncheap[i]);
285	cfsncheap[i].cp = cfsncheap[i].dcp = (struct cnode *)0;
286    }
287
288    for (i=0; i < cfsnc_hashsize; i++) {	/* initialize the hashtable */
289	CFSNC_HSHNUL((struct cfscache *)&cfsnchash[i]);
290    }
291
292    cfsnc_initialized++;
293}
294
295/*
296 * Auxillary routines -- shouldn't be entry points
297 */
298
299static struct cfscache *
300cfsnc_find(dcp, name, namelen, cred, hash)
301	struct cnode *dcp;
302	const char *name;
303	int namelen;
304	struct ucred *cred;
305	int hash;
306{
307	/*
308	 * hash to find the appropriate bucket, look through the chain
309	 * for the right entry (especially right cred, unless cred == 0)
310	 */
311	struct cfscache *cncp;
312	int count = 1;
313
314	CFSNC_DEBUG(CFSNC_FIND,
315		    myprintf(("cfsnc_find(dcp %p, name %s, len %d, cred %p, hash %d\n",
316			   dcp, name, namelen, cred, hash));)
317
318	for (cncp = cfsnchash[hash].hash_next;
319	     cncp != (struct cfscache *)&cfsnchash[hash];
320	     cncp = cncp->hash_next, count++)
321	{
322
323	    if ((CFS_NAMEMATCH(cncp, name, namelen, dcp)) &&
324		((cred == 0) || (cncp->cred == cred)))
325	    {
326		/* compare cr_uid instead */
327		cfsnc_stat.Search_len += count;
328		return(cncp);
329	    }
330#ifdef	DEBUG
331	    else if (CFS_NAMEMATCH(cncp, name, namelen, dcp)) {
332	    	printf("cfsnc_find: name %s, new cred = %p, cred = %p\n",
333			name, cred, cncp->cred);
334		printf("nref %d, nuid %d, ngid %d // oref %d, ocred %d, ogid %d\n",
335			cred->cr_ref, cred->cr_uid, cred->cr_gid,
336			cncp->cred->cr_ref, cncp->cred->cr_uid, cncp->cred->cr_gid);
337		print_cred(cred);
338		print_cred(cncp->cred);
339	    }
340#endif
341	}
342
343	return((struct cfscache *)0);
344}
345
346/*
347 * Enter a new (dir cnode, name) pair into the cache, updating the
348 * LRU and Hash as needed.
349 */
350void
351cfsnc_enter(dcp, name, namelen, cred, cp)
352    struct cnode *dcp;
353    const char *name;
354    int namelen;
355    struct ucred *cred;
356    struct cnode *cp;
357{
358    struct cfscache *cncp;
359    int hash;
360
361    if (cfsnc_use == 0)			/* Cache is off */
362	return;
363
364    CFSNC_DEBUG(CFSNC_ENTER,
365		myprintf(("Enter: dcp %p cp %p name %s cred %p \n",
366		       dcp, cp, name, cred)); )
367
368    if (namelen > CFSNC_NAMELEN) {
369	CFSNC_DEBUG(CFSNC_ENTER,
370		    myprintf(("long name enter %s\n",name));)
371	    cfsnc_stat.long_name_enters++;	/* record stats */
372	return;
373    }
374
375    hash = CFSNC_HASH(name, namelen, dcp);
376    cncp = cfsnc_find(dcp, name, namelen, cred, hash);
377    if (cncp != (struct cfscache *) 0) {
378	cfsnc_stat.dbl_enters++;		/* duplicate entry */
379	return;
380    }
381
382    cfsnc_stat.enters++;		/* record the enters statistic */
383
384    /* Grab the next element in the lru chain */
385    cncp = CFSNC_LRUGET(cfsnc_lru);
386
387    CFSNC_LRUREM(cncp);	/* remove it from the lists */
388
389    if (CFSNC_VALID(cncp)) {
390	/* Seems really ugly, but we have to decrement the appropriate
391	   hash bucket length here, so we have to find the hash bucket
392	   */
393	cfsnchash[CFSNC_HASH(cncp->name, cncp->namelen, cncp->dcp)].length--;
394
395	cfsnc_stat.lru_rm++;	/* zapped a valid entry */
396	CFSNC_HSHREM(cncp);
397	vrele(CTOV(cncp->dcp));
398	vrele(CTOV(cncp->cp));
399	crfree(cncp->cred);
400    }
401
402    /*
403     * Put a hold on the current vnodes and fill in the cache entry.
404     */
405    vref(CTOV(cp));
406    vref(CTOV(dcp));
407    crhold(cred);
408    cncp->dcp = dcp;
409    cncp->cp = cp;
410    cncp->namelen = namelen;
411    cncp->cred = cred;
412
413    bcopy(name, cncp->name, (unsigned)namelen);
414
415    /* Insert into the lru and hash chains. */
416
417    CFSNC_LRUINS(cncp, &cfsnc_lru);
418    CFSNC_HSHINS(cncp, &cfsnchash[hash]);
419    cfsnchash[hash].length++;                      /* Used for tuning */
420
421    CFSNC_DEBUG(CFSNC_PRINTCFSNC, print_cfsnc(); )
422}
423
424/*
425 * Find the (dir cnode, name) pair in the cache, if it's cred
426 * matches the input, return it, otherwise return 0
427 */
428struct cnode *
429cfsnc_lookup(dcp, name, namelen, cred)
430	struct cnode *dcp;
431	const char *name;
432	int namelen;
433	struct ucred *cred;
434{
435	int hash;
436	struct cfscache *cncp;
437
438	if (cfsnc_use == 0)			/* Cache is off */
439		return((struct cnode *) 0);
440
441	if (namelen > CFSNC_NAMELEN) {
442	        CFSNC_DEBUG(CFSNC_LOOKUP,
443			    myprintf(("long name lookup %s\n",name));)
444		cfsnc_stat.long_name_lookups++;		/* record stats */
445		return((struct cnode *) 0);
446	}
447
448	/* Use the hash function to locate the starting point,
449	   then the search routine to go down the list looking for
450	   the correct cred.
451 	 */
452
453	hash = CFSNC_HASH(name, namelen, dcp);
454	cncp = cfsnc_find(dcp, name, namelen, cred, hash);
455	if (cncp == (struct cfscache *) 0) {
456		cfsnc_stat.misses++;			/* record miss */
457		return((struct cnode *) 0);
458	}
459
460	cfsnc_stat.hits++;
461
462	/* put this entry at the end of the LRU */
463	CFSNC_LRUREM(cncp);
464	CFSNC_LRUINS(cncp, &cfsnc_lru);
465
466	/* move it to the front of the hash chain */
467	/* don't need to change the hash bucket length */
468	CFSNC_HSHREM(cncp);
469	CFSNC_HSHINS(cncp, &cfsnchash[hash]);
470
471	CFSNC_DEBUG(CFSNC_LOOKUP,
472		printf("lookup: dcp %p, name %s, cred %p = cp %p\n",
473			dcp, name, cred, cncp->cp); )
474
475	return(cncp->cp);
476}
477
478static void
479cfsnc_remove(cncp, dcstat)
480	struct cfscache *cncp;
481	enum dc_status dcstat;
482{
483	/*
484	 * remove an entry -- vrele(cncp->dcp, cp), crfree(cred),
485	 * remove it from it's hash chain, and
486	 * place it at the head of the lru list.
487	 */
488        CFSNC_DEBUG(CFSNC_REMOVE,
489		    myprintf(("cfsnc_remove %s from parent %lx.%lx.%lx\n",
490			   cncp->name, (cncp->dcp)->c_fid.Volume,
491			   (cncp->dcp)->c_fid.Vnode, (cncp->dcp)->c_fid.Unique));)
492
493  	CFSNC_HSHREM(cncp);
494
495	CFSNC_HSHNUL(cncp);		/* have it be a null chain */
496	if ((dcstat == IS_DOWNCALL) && (CTOV(cncp->dcp)->v_usecount == 1)) {
497		cncp->dcp->c_flags |= C_PURGING;
498	}
499	vrele(CTOV(cncp->dcp));
500
501	if ((dcstat == IS_DOWNCALL) && (CTOV(cncp->cp)->v_usecount == 1)) {
502		cncp->cp->c_flags |= C_PURGING;
503	}
504	vrele(CTOV(cncp->cp));
505
506	crfree(cncp->cred);
507	bzero(DATA_PART(cncp),DATA_SIZE);
508
509	/* Put the null entry just after the least-recently-used entry */
510	/* LRU_TOP adjusts the pointer to point to the top of the structure. */
511	CFSNC_LRUREM(cncp);
512	CFSNC_LRUINS(cncp, LRU_TOP(cfsnc_lru.lru_prev));
513}
514
515/*
516 * Remove all entries with a parent which has the input fid.
517 */
518void
519cfsnc_zapParentfid(fid, dcstat)
520	ViceFid *fid;
521	enum dc_status dcstat;
522{
523	/* To get to a specific fid, we might either have another hashing
524	   function or do a sequential search through the cache for the
525	   appropriate entries. The later may be acceptable since I don't
526	   think callbacks or whatever Case 1 covers are frequent occurences.
527	 */
528	struct cfscache *cncp, *ncncp;
529	int i;
530
531	if (cfsnc_use == 0)			/* Cache is off */
532		return;
533
534	CFSNC_DEBUG(CFSNC_ZAPPFID,
535		myprintf(("ZapParent: fid 0x%lx, 0x%lx, 0x%lx \n",
536			fid->Volume, fid->Vnode, fid->Unique)); )
537
538	cfsnc_stat.zapPfids++;
539
540	for (i = 0; i < cfsnc_hashsize; i++) {
541
542		/*
543		 * Need to save the hash_next pointer in case we remove the
544		 * entry. remove causes hash_next to point to itself.
545		 */
546
547		for (cncp = cfsnchash[i].hash_next;
548		     cncp != (struct cfscache *)&cfsnchash[i];
549		     cncp = ncncp) {
550			ncncp = cncp->hash_next;
551			if ((cncp->dcp->c_fid.Volume == fid->Volume) &&
552			    (cncp->dcp->c_fid.Vnode == fid->Vnode)   &&
553			    (cncp->dcp->c_fid.Unique == fid->Unique)) {
554			        cfsnchash[i].length--;      /* Used for tuning */
555				cfsnc_remove(cncp, dcstat);
556			}
557		}
558	}
559}
560
561/*
562 * Remove all entries which have the same fid as the input
563 */
564void
565cfsnc_zapfid(fid, dcstat)
566	ViceFid *fid;
567	enum dc_status dcstat;
568{
569	/* See comment for zapParentfid. This routine will be used
570	   if attributes are being cached.
571	 */
572	struct cfscache *cncp, *ncncp;
573	int i;
574
575	if (cfsnc_use == 0)			/* Cache is off */
576		return;
577
578	CFSNC_DEBUG(CFSNC_ZAPFID,
579		myprintf(("Zapfid: fid 0x%lx, 0x%lx, 0x%lx \n",
580			fid->Volume, fid->Vnode, fid->Unique)); )
581
582	cfsnc_stat.zapFids++;
583
584	for (i = 0; i < cfsnc_hashsize; i++) {
585		for (cncp = cfsnchash[i].hash_next;
586		     cncp != (struct cfscache *)&cfsnchash[i];
587		     cncp = ncncp) {
588			ncncp = cncp->hash_next;
589			if ((cncp->cp->c_fid.Volume == fid->Volume) &&
590			    (cncp->cp->c_fid.Vnode == fid->Vnode)   &&
591			    (cncp->cp->c_fid.Unique == fid->Unique)) {
592			        cfsnchash[i].length--;     /* Used for tuning */
593				cfsnc_remove(cncp, dcstat);
594			}
595		}
596	}
597}
598
599/*
600 * Remove all entries which match the fid and the cred
601 */
602void
603cfsnc_zapvnode(fid, cred, dcstat)
604	ViceFid *fid;
605	struct ucred *cred;
606	enum dc_status dcstat;
607{
608	/* See comment for zapfid. I don't think that one would ever
609	   want to zap a file with a specific cred from the kernel.
610	   We'll leave this one unimplemented.
611	 */
612	if (cfsnc_use == 0)			/* Cache is off */
613		return;
614
615	CFSNC_DEBUG(CFSNC_ZAPVNODE,
616		myprintf(("Zapvnode: fid 0x%lx, 0x%lx, 0x%lx cred %p\n",
617			  fid->Volume, fid->Vnode, fid->Unique, cred)); )
618
619}
620
621/*
622 * Remove all entries which have the (dir vnode, name) pair
623 */
624void
625cfsnc_zapfile(dcp, name, namelen)
626	struct cnode *dcp;
627	const char *name;
628	int namelen;
629{
630	/* use the hash function to locate the file, then zap all
631 	   entries of it regardless of the cred.
632	 */
633	struct cfscache *cncp;
634	int hash;
635
636	if (cfsnc_use == 0)			/* Cache is off */
637		return;
638
639	CFSNC_DEBUG(CFSNC_ZAPFILE,
640		myprintf(("Zapfile: dcp %p name %s \n",
641			  dcp, name)); )
642
643	if (namelen > CFSNC_NAMELEN) {
644		cfsnc_stat.long_remove++;		/* record stats */
645		return;
646	}
647
648	cfsnc_stat.zapFile++;
649
650	hash = CFSNC_HASH(name, namelen, dcp);
651	cncp = cfsnc_find(dcp, name, namelen, 0, hash);
652
653	while (cncp) {
654	  cfsnchash[hash].length--;                 /* Used for tuning */
655/* 1.3 */
656	  cfsnc_remove(cncp, NOT_DOWNCALL);
657	  cncp = cfsnc_find(dcp, name, namelen, 0, hash);
658	}
659}
660
661/*
662 * Remove all the entries for a particular user. Used when tokens expire.
663 * A user is determined by his/her effective user id (id_uid).
664 */
665void
666cfsnc_purge_user(uid, dcstat)
667	vuid_t	uid;
668	enum dc_status  dcstat;
669{
670	/*
671	 * I think the best approach is to go through the entire cache
672	 * via HASH or whatever and zap all entries which match the
673	 * input cred. Or just flush the whole cache.  It might be
674	 * best to go through on basis of LRU since cache will almost
675	 * always be full and LRU is more straightforward.
676	 */
677
678	struct cfscache *cncp, *ncncp;
679	int hash;
680
681	if (cfsnc_use == 0)			/* Cache is off */
682		return;
683
684	CFSNC_DEBUG(CFSNC_PURGEUSER,
685		myprintf(("ZapDude: uid %lx\n", uid)); )
686	cfsnc_stat.zapUsers++;
687
688	for (cncp = CFSNC_LRUGET(cfsnc_lru);
689	     cncp != (struct cfscache *)(&cfsnc_lru);
690	     cncp = ncncp) {
691		ncncp = CFSNC_LRUGET(*cncp);
692
693		if ((CFSNC_VALID(cncp)) &&
694		   ((cncp->cred)->cr_uid == uid)) {
695		        /* Seems really ugly, but we have to decrement the appropriate
696			   hash bucket length here, so we have to find the hash bucket
697			   */
698		        hash = CFSNC_HASH(cncp->name, cncp->namelen, cncp->dcp);
699			cfsnchash[hash].length--;     /* For performance tuning */
700
701			cfsnc_remove(cncp, dcstat);
702		}
703	}
704}
705
706/*
707 * Flush the entire name cache. In response to a flush of the Venus cache.
708 */
709void
710cfsnc_flush(dcstat)
711	enum dc_status dcstat;
712{
713	/* One option is to deallocate the current name cache and
714	   call init to start again. Or just deallocate, then rebuild.
715	   Or again, we could just go through the array and zero the
716	   appropriate fields.
717	 */
718
719	/*
720	 * Go through the whole lru chain and kill everything as we go.
721	 * I don't use remove since that would rebuild the lru chain
722	 * as it went and that seemed unneccesary.
723	 */
724	struct cfscache *cncp;
725	int i;
726
727	if (cfsnc_use == 0)			/* Cache is off */
728		return;
729
730	cfsnc_stat.Flushes++;
731
732	for (cncp = CFSNC_LRUGET(cfsnc_lru);
733	     cncp != (struct cfscache *)&cfsnc_lru;
734	     cncp = CFSNC_LRUGET(*cncp)) {
735		if (CFSNC_VALID(cncp)) {
736
737			CFSNC_HSHREM(cncp);	/* only zero valid nodes */
738			CFSNC_HSHNUL(cncp);
739			if ((dcstat == IS_DOWNCALL)
740			    && (CTOV(cncp->dcp)->v_usecount == 1))
741			{
742				cncp->dcp->c_flags |= C_PURGING;
743			}
744			vrele(CTOV(cncp->dcp));
745
746			if (CTOV(cncp->cp)->v_flag & VTEXT) {
747			    if (cfs_vmflush(cncp->cp))
748				CFSDEBUG(CFS_FLUSH,
749					 myprintf(("cfsnc_flush: (%lx.%lx.%lx) busy\n", cncp->cp->c_fid.Volume, cncp->cp->c_fid.Vnode, cncp->cp->c_fid.Unique)); )
750			}
751
752			if ((dcstat == IS_DOWNCALL)
753			    && (CTOV(cncp->cp)->v_usecount == 1))
754			{
755				cncp->cp->c_flags |= C_PURGING;
756			}
757			vrele(CTOV(cncp->cp));
758
759			crfree(cncp->cred);
760			bzero(DATA_PART(cncp),DATA_SIZE);
761		}
762	}
763
764	for (i = 0; i < cfsnc_hashsize; i++)
765	  cfsnchash[i].length = 0;
766}
767
768/*
769 * Debugging routines
770 */
771
772/*
773 * This routine should print out all the hash chains to the console.
774 */
775void
776print_cfsnc(void)
777{
778	int hash;
779	struct cfscache *cncp;
780
781	for (hash = 0; hash < cfsnc_hashsize; hash++) {
782		myprintf(("\nhash %d\n",hash));
783
784		for (cncp = cfsnchash[hash].hash_next;
785		     cncp != (struct cfscache *)&cfsnchash[hash];
786		     cncp = cncp->hash_next) {
787			myprintf(("cp %p dcp %p cred %p name %s\n",
788				  cncp->cp, cncp->dcp,
789				  cncp->cred, cncp->name));
790		     }
791	}
792}
793
794void
795cfsnc_gather_stats(void)
796{
797    int i, max = 0, sum = 0, temp, zeros = 0, ave, n;
798
799	for (i = 0; i < cfsnc_hashsize; i++) {
800	  if (cfsnchash[i].length) {
801	    sum += cfsnchash[i].length;
802	  } else {
803	    zeros++;
804	  }
805
806	  if (cfsnchash[i].length > max)
807	    max = cfsnchash[i].length;
808	}
809
810	/*
811	 * When computing the Arithmetic mean, only count slots which
812	 * are not empty in the distribution.
813	 */
814        cfsnc_stat.Sum_bucket_len = sum;
815        cfsnc_stat.Num_zero_len = zeros;
816        cfsnc_stat.Max_bucket_len = max;
817
818	if ((n = cfsnc_hashsize - zeros) > 0)
819	  ave = sum / n;
820	else
821	  ave = 0;
822
823	sum = 0;
824	for (i = 0; i < cfsnc_hashsize; i++) {
825	  if (cfsnchash[i].length) {
826	    temp = cfsnchash[i].length - ave;
827	    sum += temp * temp;
828	  }
829	}
830        cfsnc_stat.Sum2_bucket_len = sum;
831}
832
833/*
834 * The purpose of this routine is to allow the hash and cache sizes to be
835 * changed dynamically. This should only be used in controlled environments,
836 * it makes no effort to lock other users from accessing the cache while it
837 * is in an improper state (except by turning the cache off).
838 */
839int
840cfsnc_resize(hashsize, heapsize, dcstat)
841     int hashsize, heapsize;
842     enum dc_status dcstat;
843{
844    if ((hashsize % 2) || (heapsize % 2)) { /* Illegal hash or cache sizes */
845	return(EINVAL);
846    }
847
848    cfsnc_use = 0;                       /* Turn the cache off */
849
850    cfsnc_flush(dcstat);                 /* free any cnodes in the cache */
851
852    /* WARNING: free must happen *before* size is reset */
853    CFS_FREE(cfsncheap,TOTAL_CACHE_SIZE);
854    CFS_FREE(cfsnchash,TOTAL_HASH_SIZE);
855
856    cfsnc_hashsize = hashsize;
857    cfsnc_size = heapsize;
858
859    cfsnc_init();                        /* Set up a cache with the new size */
860
861    cfsnc_use = 1;                       /* Turn the cache back on */
862    return(0);
863}
864
865char cfsnc_name_buf[CFS_MAXNAMLEN+1];
866
867void
868cfsnc_name(struct cnode *cp)
869{
870	struct cfscache *cncp, *ncncp;
871	int i;
872
873	if (cfsnc_use == 0)			/* Cache is off */
874		return;
875
876	for (i = 0; i < cfsnc_hashsize; i++) {
877		for (cncp = cfsnchash[i].hash_next;
878		     cncp != (struct cfscache *)&cfsnchash[i];
879		     cncp = ncncp) {
880			ncncp = cncp->hash_next;
881			if (cncp->cp == cp) {
882				bcopy(cncp->name, cfsnc_name_buf, cncp->namelen);
883				cfsnc_name_buf[cncp->namelen] = 0;
884				printf(" is %s (%p,%p)@%p",
885					cfsnc_name_buf, cncp->cp, cncp->dcp, cncp);
886			}
887
888		}
889	}
890}
891