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