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