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