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