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