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