coda_namecache.c revision 1.3 1 /* $NetBSD: coda_namecache.c,v 1.3 1998/09/12 15:05:48 rvb 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 * @(#) cfs/cfs_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 * HISTORY
49 * $Log: coda_namecache.c,v $
50 * Revision 1.3 1998/09/12 15:05:48 rvb
51 * Change cfs/CFS in symbols, strings and constants to coda/CODA
52 * to avoid fs conflicts.
53 *
54 * Revision 1.2 1998/09/08 17:12:46 rvb
55 * Pass2 complete
56 *
57 * Revision 1.1.1.1 1998/08/29 21:26:45 rvb
58 * Very Preliminary Coda
59 *
60 * Revision 1.11 1998/08/28 18:12:16 rvb
61 * Now it also works on FreeBSD -current. This code will be
62 * committed to the FreeBSD -current and NetBSD -current
63 * trees. It will then be tailored to the particular platform
64 * by flushing conditional code.
65 *
66 * Revision 1.10 1998/08/18 17:05:14 rvb
67 * Don't use __RCSID now
68 *
69 * Revision 1.9 1998/08/18 16:31:39 rvb
70 * Sync the code for NetBSD -current; test on 1.3 later
71 *
72 * Revision 1.8 98/01/31 20:53:10 rvb
73 * First version that works on FreeBSD 2.2.5
74 *
75 * Revision 1.7 98/01/23 11:53:39 rvb
76 * Bring RVB_CODA1_1 to HEAD
77 *
78 * Revision 1.6.2.4 98/01/23 11:21:02 rvb
79 * Sync with 2.2.5
80 *
81 * Revision 1.6.2.3 97/12/16 12:40:03 rvb
82 * Sync with 1.3
83 *
84 * Revision 1.6.2.2 97/12/09 16:07:10 rvb
85 * Sync with vfs/include/coda.h
86 *
87 * Revision 1.6.2.1 97/12/06 17:41:18 rvb
88 * Sync with peters coda.h
89 *
90 * Revision 1.6 97/12/05 10:39:13 rvb
91 * Read CHANGES
92 *
93 * Revision 1.5.4.7 97/11/25 08:08:43 rvb
94 * cfs_venus ... done; until cred/vattr change
95 *
96 * Revision 1.5.4.6 97/11/24 15:44:43 rvb
97 * Final cfs_venus.c w/o macros, but one locking bug
98 *
99 * Revision 1.5.4.5 97/11/20 11:46:38 rvb
100 * Capture current cfs_venus
101 *
102 * Revision 1.5.4.4 97/11/18 10:27:13 rvb
103 * cfs_nbsd.c is DEAD!!!; integrated into cfs_vf/vnops.c
104 * cfs_nb_foo and cfs_foo are joined
105 *
106 * Revision 1.5.4.3 97/11/13 22:02:57 rvb
107 * pass2 cfs_NetBSD.h mt
108 *
109 * Revision 1.5.4.2 97/11/12 12:09:35 rvb
110 * reorg pass1
111 *
112 * Revision 1.5.4.1 97/10/28 23:10:12 rvb
113 * >64Meg; venus can be killed!
114 *
115 * Revision 1.5 97/08/05 11:08:01 lily
116 * Removed cfsnc_replace, replaced it with a coda_find, unhash, and
117 * rehash. This fixes a cnode leak and a bug in which the fid is
118 * not actually replaced. (cfs_namecache.c, cfsnc.h, cfs_subr.c)
119 *
120 * Revision 1.4 96/12/12 22:10:57 bnoble
121 * Fixed the "downcall invokes venus operation" deadlock in all known cases.
122 * There may be more
123 *
124 * Revision 1.3 1996/11/08 18:06:09 bnoble
125 * Minor changes in vnode operation signature, VOP_UPDATE signature, and
126 * some newly defined bits in the include files.
127 *
128 * Revision 1.2 1996/01/02 16:56:50 bnoble
129 * Added support for Coda MiniCache and raw inode calls (final commit)
130 *
131 * Revision 1.1.2.1 1995/12/20 01:57:15 bnoble
132 * Added CODA-specific files
133 *
134 * Revision 3.1.1.1 1995/03/04 19:07:57 bnoble
135 * Branch for NetBSD port revisions
136 *
137 * Revision 3.1 1995/03/04 19:07:56 bnoble
138 * Bump to major revision 3 to prepare for NetBSD port
139 *
140 * Revision 2.3 1994/10/14 09:57:54 dcs
141 * Made changes 'cause sun4s have braindead compilers
142 *
143 * Revision 2.2 94/08/28 19:37:35 luqi
144 * Add a new CODA_REPLACE call to allow venus to replace a ViceFid in the
145 * mini-cache.
146 *
147 * In "cfs.h":
148 * Add CODA_REPLACE decl.
149 *
150 * In "cfs_namecache.c":
151 * Add routine cfsnc_replace.
152 *
153 * In "cfs_subr.c":
154 * Add case-statement to process CODA_REPLACE.
155 *
156 * In "cfsnc.h":
157 * Add decl for CODA_NC_REPLACE.
158 *
159 *
160 * Revision 2.1 94/07/21 16:25:15 satya
161 * Conversion to C++ 3.0; start of Coda Release 2.0
162 *
163 * Revision 1.2 92/10/27 17:58:21 lily
164 * merge kernel/latest and alpha/src/cfs
165 *
166 * Revision 2.3 92/09/30 14:16:20 mja
167 * call coda_flush instead of calling inode_uncache_try directly
168 * (from dcs). Also...
169 *
170 * Substituted rvb's history blurb so that we agree with Mach 2.5 sources.
171 * [91/02/09 jjk]
172 *
173 * Added contributors blurb.
174 * [90/12/13 jjk]
175 *
176 * Revision 2.2 90/07/05 11:26:30 mrt
177 * Created for the Coda File System.
178 * [90/05/23 dcs]
179 *
180 * Revision 1.3 90/05/31 17:01:24 dcs
181 * Prepare for merge with facilities kernel.
182 *
183 *
184 */
185
186 /*
187 * This module contains the routines to implement the CODA name cache. The
188 * purpose of this cache is to reduce the cost of translating pathnames
189 * into Vice FIDs. Each entry in the cache contains the name of the file,
190 * the vnode (FID) of the parent directory, and the cred structure of the
191 * user accessing the file.
192 *
193 * The first time a file is accessed, it is looked up by the local Venus
194 * which first insures that the user has access to the file. In addition
195 * we are guaranteed that Venus will invalidate any name cache entries in
196 * case the user no longer should be able to access the file. For these
197 * reasons we do not need to keep access list information as well as a
198 * cred structure for each entry.
199 *
200 * The table can be accessed through the routines cnc_init(), cnc_enter(),
201 * cnc_lookup(), cnc_rmfidcred(), cnc_rmfid(), cnc_rmcred(), and cnc_purge().
202 * There are several other routines which aid in the implementation of the
203 * hash table.
204 */
205
206 /*
207 * NOTES: rvb@cs
208 * 1. The name cache holds a reference to every vnode in it. Hence files can not be
209 * closed or made inactive until they are released.
210 * 2. coda_nc_name(cp) was added to get a name for a cnode pointer for debugging.
211 * 3. coda_nc_find() has debug code to detect when entries are stored with different
212 * credentials. We don't understand yet, if/how entries are NOT EQ but still
213 * EQUAL
214 * 4. I wonder if this name cache could be replace by the vnode name cache.
215 * The latter has no zapping functions, so probably not.
216 */
217
218 #include <sys/param.h>
219 #include <sys/errno.h>
220 #include <sys/malloc.h>
221 #include <sys/select.h>
222
223 #include <cfs/coda.h>
224 #include <cfs/cnode.h>
225 #include <cfs/cfsnc.h>
226
227 #ifndef insque
228 #include <sys/systm.h>
229 #endif /* insque */
230
231 /*
232 * Declaration of the name cache data structure.
233 */
234
235 int coda_nc_use = 1; /* Indicate use of CODA Name Cache */
236
237 int coda_nc_size = CODA_NC_CACHESIZE; /* size of the cache */
238 int coda_nc_hashsize = CODA_NC_HASHSIZE; /* size of the primary hash */
239
240 struct coda_cache *coda_nc_heap; /* pointer to the cache entries */
241 struct coda_hash *coda_nc_hash; /* hash table of cfscache pointers */
242 struct coda_lru coda_nc_lru; /* head of lru chain */
243
244 struct coda_nc_statistics coda_nc_stat; /* Keep various stats */
245
246 /*
247 * for testing purposes
248 */
249 int coda_nc_debug = 0;
250
251 /*
252 * Entry points for the CODA Name Cache
253 */
254 static struct coda_cache *
255 coda_nc_find(struct cnode *dcp, const char *name, int namelen,
256 struct ucred *cred, int hash);
257 static void
258 coda_nc_remove(struct coda_cache *cncp, enum dc_status dcstat);
259
260 /*
261 * Initialize the cache, the LRU structure and the Hash structure(s)
262 */
263
264 #define TOTAL_CACHE_SIZE (sizeof(struct coda_cache) * coda_nc_size)
265 #define TOTAL_HASH_SIZE (sizeof(struct coda_hash) * coda_nc_hashsize)
266
267 int coda_nc_initialized = 0; /* Initially the cache has not been initialized */
268
269 void
270 coda_nc_init(void)
271 {
272 int i;
273
274 /* zero the statistics structure */
275
276 bzero(&coda_nc_stat, (sizeof(struct coda_nc_statistics)));
277
278 printf("CODA NAME CACHE: CACHE %d, HASH TBL %d\n", CODA_NC_CACHESIZE, CODA_NC_HASHSIZE);
279 CODA_ALLOC(coda_nc_heap, struct coda_cache *, TOTAL_CACHE_SIZE);
280 CODA_ALLOC(coda_nc_hash, struct coda_hash *, TOTAL_HASH_SIZE);
281
282 coda_nc_lru.lru_next =
283 coda_nc_lru.lru_prev = (struct coda_cache *)LRU_PART(&coda_nc_lru);
284
285
286 for (i=0; i < coda_nc_size; i++) { /* initialize the heap */
287 CODA_NC_LRUINS(&coda_nc_heap[i], &coda_nc_lru);
288 CODA_NC_HSHNUL(&coda_nc_heap[i]);
289 coda_nc_heap[i].cp = coda_nc_heap[i].dcp = (struct cnode *)0;
290 }
291
292 for (i=0; i < coda_nc_hashsize; i++) { /* initialize the hashtable */
293 CODA_NC_HSHNUL((struct coda_cache *)&coda_nc_hash[i]);
294 }
295
296 coda_nc_initialized++;
297 }
298
299 /*
300 * Auxillary routines -- shouldn't be entry points
301 */
302
303 static struct coda_cache *
304 coda_nc_find(dcp, name, namelen, cred, hash)
305 struct cnode *dcp;
306 const char *name;
307 int namelen;
308 struct ucred *cred;
309 int hash;
310 {
311 /*
312 * hash to find the appropriate bucket, look through the chain
313 * for the right entry (especially right cred, unless cred == 0)
314 */
315 struct coda_cache *cncp;
316 int count = 1;
317
318 CODA_NC_DEBUG(CODA_NC_FIND,
319 myprintf(("coda_nc_find(dcp %p, name %s, len %d, cred %p, hash %d\n",
320 dcp, name, namelen, cred, hash));)
321
322 for (cncp = coda_nc_hash[hash].hash_next;
323 cncp != (struct coda_cache *)&coda_nc_hash[hash];
324 cncp = cncp->hash_next, count++)
325 {
326
327 if ((CODA_NAMEMATCH(cncp, name, namelen, dcp)) &&
328 ((cred == 0) || (cncp->cred == cred)))
329 {
330 /* compare cr_uid instead */
331 coda_nc_stat.Search_len += count;
332 return(cncp);
333 }
334 #ifdef DEBUG
335 else if (CODA_NAMEMATCH(cncp, name, namelen, dcp)) {
336 printf("coda_nc_find: name %s, new cred = %p, cred = %p\n",
337 name, cred, cncp->cred);
338 printf("nref %d, nuid %d, ngid %d // oref %d, ocred %d, ogid %d\n",
339 cred->cr_ref, cred->cr_uid, cred->cr_gid,
340 cncp->cred->cr_ref, cncp->cred->cr_uid, cncp->cred->cr_gid);
341 print_cred(cred);
342 print_cred(cncp->cred);
343 }
344 #endif
345 }
346
347 return((struct coda_cache *)0);
348 }
349
350 /*
351 * Enter a new (dir cnode, name) pair into the cache, updating the
352 * LRU and Hash as needed.
353 */
354 void
355 coda_nc_enter(dcp, name, namelen, cred, cp)
356 struct cnode *dcp;
357 const char *name;
358 int namelen;
359 struct ucred *cred;
360 struct cnode *cp;
361 {
362 struct coda_cache *cncp;
363 int hash;
364
365 if (coda_nc_use == 0) /* Cache is off */
366 return;
367
368 CODA_NC_DEBUG(CODA_NC_ENTER,
369 myprintf(("Enter: dcp %p cp %p name %s cred %p \n",
370 dcp, cp, name, cred)); )
371
372 if (namelen > CODA_NC_NAMELEN) {
373 CODA_NC_DEBUG(CODA_NC_ENTER,
374 myprintf(("long name enter %s\n",name));)
375 coda_nc_stat.long_name_enters++; /* record stats */
376 return;
377 }
378
379 hash = CODA_NC_HASH(name, namelen, dcp);
380 cncp = coda_nc_find(dcp, name, namelen, cred, hash);
381 if (cncp != (struct coda_cache *) 0) {
382 coda_nc_stat.dbl_enters++; /* duplicate entry */
383 return;
384 }
385
386 coda_nc_stat.enters++; /* record the enters statistic */
387
388 /* Grab the next element in the lru chain */
389 cncp = CODA_NC_LRUGET(coda_nc_lru);
390
391 CODA_NC_LRUREM(cncp); /* remove it from the lists */
392
393 if (CODA_NC_VALID(cncp)) {
394 /* Seems really ugly, but we have to decrement the appropriate
395 hash bucket length here, so we have to find the hash bucket
396 */
397 coda_nc_hash[CODA_NC_HASH(cncp->name, cncp->namelen, cncp->dcp)].length--;
398
399 coda_nc_stat.lru_rm++; /* zapped a valid entry */
400 CODA_NC_HSHREM(cncp);
401 vrele(CTOV(cncp->dcp));
402 vrele(CTOV(cncp->cp));
403 crfree(cncp->cred);
404 }
405
406 /*
407 * Put a hold on the current vnodes and fill in the cache entry.
408 */
409 vref(CTOV(cp));
410 vref(CTOV(dcp));
411 crhold(cred);
412 cncp->dcp = dcp;
413 cncp->cp = cp;
414 cncp->namelen = namelen;
415 cncp->cred = cred;
416
417 bcopy(name, cncp->name, (unsigned)namelen);
418
419 /* Insert into the lru and hash chains. */
420
421 CODA_NC_LRUINS(cncp, &coda_nc_lru);
422 CODA_NC_HSHINS(cncp, &coda_nc_hash[hash]);
423 coda_nc_hash[hash].length++; /* Used for tuning */
424
425 CODA_NC_DEBUG(CODA_NC_PRINTCODA_NC, print_coda_nc(); )
426 }
427
428 /*
429 * Find the (dir cnode, name) pair in the cache, if it's cred
430 * matches the input, return it, otherwise return 0
431 */
432 struct cnode *
433 coda_nc_lookup(dcp, name, namelen, cred)
434 struct cnode *dcp;
435 const char *name;
436 int namelen;
437 struct ucred *cred;
438 {
439 int hash;
440 struct coda_cache *cncp;
441
442 if (coda_nc_use == 0) /* Cache is off */
443 return((struct cnode *) 0);
444
445 if (namelen > CODA_NC_NAMELEN) {
446 CODA_NC_DEBUG(CODA_NC_LOOKUP,
447 myprintf(("long name lookup %s\n",name));)
448 coda_nc_stat.long_name_lookups++; /* record stats */
449 return((struct cnode *) 0);
450 }
451
452 /* Use the hash function to locate the starting point,
453 then the search routine to go down the list looking for
454 the correct cred.
455 */
456
457 hash = CODA_NC_HASH(name, namelen, dcp);
458 cncp = coda_nc_find(dcp, name, namelen, cred, hash);
459 if (cncp == (struct coda_cache *) 0) {
460 coda_nc_stat.misses++; /* record miss */
461 return((struct cnode *) 0);
462 }
463
464 coda_nc_stat.hits++;
465
466 /* put this entry at the end of the LRU */
467 CODA_NC_LRUREM(cncp);
468 CODA_NC_LRUINS(cncp, &coda_nc_lru);
469
470 /* move it to the front of the hash chain */
471 /* don't need to change the hash bucket length */
472 CODA_NC_HSHREM(cncp);
473 CODA_NC_HSHINS(cncp, &coda_nc_hash[hash]);
474
475 CODA_NC_DEBUG(CODA_NC_LOOKUP,
476 printf("lookup: dcp %p, name %s, cred %p = cp %p\n",
477 dcp, name, cred, cncp->cp); )
478
479 return(cncp->cp);
480 }
481
482 static void
483 coda_nc_remove(cncp, dcstat)
484 struct coda_cache *cncp;
485 enum dc_status dcstat;
486 {
487 /*
488 * remove an entry -- vrele(cncp->dcp, cp), crfree(cred),
489 * remove it from it's hash chain, and
490 * place it at the head of the lru list.
491 */
492 CODA_NC_DEBUG(CODA_NC_REMOVE,
493 myprintf(("coda_nc_remove %s from parent %lx.%lx.%lx\n",
494 cncp->name, (cncp->dcp)->c_fid.Volume,
495 (cncp->dcp)->c_fid.Vnode, (cncp->dcp)->c_fid.Unique));)
496
497 CODA_NC_HSHREM(cncp);
498
499 CODA_NC_HSHNUL(cncp); /* have it be a null chain */
500 if ((dcstat == IS_DOWNCALL) && (CTOV(cncp->dcp)->v_usecount == 1)) {
501 cncp->dcp->c_flags |= C_PURGING;
502 }
503 vrele(CTOV(cncp->dcp));
504
505 if ((dcstat == IS_DOWNCALL) && (CTOV(cncp->cp)->v_usecount == 1)) {
506 cncp->cp->c_flags |= C_PURGING;
507 }
508 vrele(CTOV(cncp->cp));
509
510 crfree(cncp->cred);
511 bzero(DATA_PART(cncp),DATA_SIZE);
512
513 /* Put the null entry just after the least-recently-used entry */
514 /* LRU_TOP adjusts the pointer to point to the top of the structure. */
515 CODA_NC_LRUREM(cncp);
516 CODA_NC_LRUINS(cncp, LRU_TOP(coda_nc_lru.lru_prev));
517 }
518
519 /*
520 * Remove all entries with a parent which has the input fid.
521 */
522 void
523 coda_nc_zapParentfid(fid, dcstat)
524 ViceFid *fid;
525 enum dc_status dcstat;
526 {
527 /* To get to a specific fid, we might either have another hashing
528 function or do a sequential search through the cache for the
529 appropriate entries. The later may be acceptable since I don't
530 think callbacks or whatever Case 1 covers are frequent occurences.
531 */
532 struct coda_cache *cncp, *ncncp;
533 int i;
534
535 if (coda_nc_use == 0) /* Cache is off */
536 return;
537
538 CODA_NC_DEBUG(CODA_NC_ZAPPFID,
539 myprintf(("ZapParent: fid 0x%lx, 0x%lx, 0x%lx \n",
540 fid->Volume, fid->Vnode, fid->Unique)); )
541
542 coda_nc_stat.zapPfids++;
543
544 for (i = 0; i < coda_nc_hashsize; i++) {
545
546 /*
547 * Need to save the hash_next pointer in case we remove the
548 * entry. remove causes hash_next to point to itself.
549 */
550
551 for (cncp = coda_nc_hash[i].hash_next;
552 cncp != (struct coda_cache *)&coda_nc_hash[i];
553 cncp = ncncp) {
554 ncncp = cncp->hash_next;
555 if ((cncp->dcp->c_fid.Volume == fid->Volume) &&
556 (cncp->dcp->c_fid.Vnode == fid->Vnode) &&
557 (cncp->dcp->c_fid.Unique == fid->Unique)) {
558 coda_nc_hash[i].length--; /* Used for tuning */
559 coda_nc_remove(cncp, dcstat);
560 }
561 }
562 }
563 }
564
565 /*
566 * Remove all entries which have the same fid as the input
567 */
568 void
569 coda_nc_zapfid(fid, dcstat)
570 ViceFid *fid;
571 enum dc_status dcstat;
572 {
573 /* See comment for zapParentfid. This routine will be used
574 if attributes are being cached.
575 */
576 struct coda_cache *cncp, *ncncp;
577 int i;
578
579 if (coda_nc_use == 0) /* Cache is off */
580 return;
581
582 CODA_NC_DEBUG(CODA_NC_ZAPFID,
583 myprintf(("Zapfid: fid 0x%lx, 0x%lx, 0x%lx \n",
584 fid->Volume, fid->Vnode, fid->Unique)); )
585
586 coda_nc_stat.zapFids++;
587
588 for (i = 0; i < coda_nc_hashsize; i++) {
589 for (cncp = coda_nc_hash[i].hash_next;
590 cncp != (struct coda_cache *)&coda_nc_hash[i];
591 cncp = ncncp) {
592 ncncp = cncp->hash_next;
593 if ((cncp->cp->c_fid.Volume == fid->Volume) &&
594 (cncp->cp->c_fid.Vnode == fid->Vnode) &&
595 (cncp->cp->c_fid.Unique == fid->Unique)) {
596 coda_nc_hash[i].length--; /* Used for tuning */
597 coda_nc_remove(cncp, dcstat);
598 }
599 }
600 }
601 }
602
603 /*
604 * Remove all entries which match the fid and the cred
605 */
606 void
607 coda_nc_zapvnode(fid, cred, dcstat)
608 ViceFid *fid;
609 struct ucred *cred;
610 enum dc_status dcstat;
611 {
612 /* See comment for zapfid. I don't think that one would ever
613 want to zap a file with a specific cred from the kernel.
614 We'll leave this one unimplemented.
615 */
616 if (coda_nc_use == 0) /* Cache is off */
617 return;
618
619 CODA_NC_DEBUG(CODA_NC_ZAPVNODE,
620 myprintf(("Zapvnode: fid 0x%lx, 0x%lx, 0x%lx cred %p\n",
621 fid->Volume, fid->Vnode, fid->Unique, cred)); )
622
623 }
624
625 /*
626 * Remove all entries which have the (dir vnode, name) pair
627 */
628 void
629 coda_nc_zapfile(dcp, name, namelen)
630 struct cnode *dcp;
631 const char *name;
632 int namelen;
633 {
634 /* use the hash function to locate the file, then zap all
635 entries of it regardless of the cred.
636 */
637 struct coda_cache *cncp;
638 int hash;
639
640 if (coda_nc_use == 0) /* Cache is off */
641 return;
642
643 CODA_NC_DEBUG(CODA_NC_ZAPFILE,
644 myprintf(("Zapfile: dcp %p name %s \n",
645 dcp, name)); )
646
647 if (namelen > CODA_NC_NAMELEN) {
648 coda_nc_stat.long_remove++; /* record stats */
649 return;
650 }
651
652 coda_nc_stat.zapFile++;
653
654 hash = CODA_NC_HASH(name, namelen, dcp);
655 cncp = coda_nc_find(dcp, name, namelen, 0, hash);
656
657 while (cncp) {
658 coda_nc_hash[hash].length--; /* Used for tuning */
659 /* 1.3 */
660 coda_nc_remove(cncp, NOT_DOWNCALL);
661 cncp = coda_nc_find(dcp, name, namelen, 0, hash);
662 }
663 }
664
665 /*
666 * Remove all the entries for a particular user. Used when tokens expire.
667 * A user is determined by his/her effective user id (id_uid).
668 */
669 void
670 coda_nc_purge_user(uid, dcstat)
671 vuid_t uid;
672 enum dc_status dcstat;
673 {
674 /*
675 * I think the best approach is to go through the entire cache
676 * via HASH or whatever and zap all entries which match the
677 * input cred. Or just flush the whole cache. It might be
678 * best to go through on basis of LRU since cache will almost
679 * always be full and LRU is more straightforward.
680 */
681
682 struct coda_cache *cncp, *ncncp;
683 int hash;
684
685 if (coda_nc_use == 0) /* Cache is off */
686 return;
687
688 CODA_NC_DEBUG(CODA_NC_PURGEUSER,
689 myprintf(("ZapDude: uid %lx\n", uid)); )
690 coda_nc_stat.zapUsers++;
691
692 for (cncp = CODA_NC_LRUGET(coda_nc_lru);
693 cncp != (struct coda_cache *)(&coda_nc_lru);
694 cncp = ncncp) {
695 ncncp = CODA_NC_LRUGET(*cncp);
696
697 if ((CODA_NC_VALID(cncp)) &&
698 ((cncp->cred)->cr_uid == uid)) {
699 /* Seems really ugly, but we have to decrement the appropriate
700 hash bucket length here, so we have to find the hash bucket
701 */
702 hash = CODA_NC_HASH(cncp->name, cncp->namelen, cncp->dcp);
703 coda_nc_hash[hash].length--; /* For performance tuning */
704
705 coda_nc_remove(cncp, dcstat);
706 }
707 }
708 }
709
710 /*
711 * Flush the entire name cache. In response to a flush of the Venus cache.
712 */
713 void
714 coda_nc_flush(dcstat)
715 enum dc_status dcstat;
716 {
717 /* One option is to deallocate the current name cache and
718 call init to start again. Or just deallocate, then rebuild.
719 Or again, we could just go through the array and zero the
720 appropriate fields.
721 */
722
723 /*
724 * Go through the whole lru chain and kill everything as we go.
725 * I don't use remove since that would rebuild the lru chain
726 * as it went and that seemed unneccesary.
727 */
728 struct coda_cache *cncp;
729 int i;
730
731 if (coda_nc_use == 0) /* Cache is off */
732 return;
733
734 coda_nc_stat.Flushes++;
735
736 for (cncp = CODA_NC_LRUGET(coda_nc_lru);
737 cncp != (struct coda_cache *)&coda_nc_lru;
738 cncp = CODA_NC_LRUGET(*cncp)) {
739 if (CODA_NC_VALID(cncp)) {
740
741 CODA_NC_HSHREM(cncp); /* only zero valid nodes */
742 CODA_NC_HSHNUL(cncp);
743 if ((dcstat == IS_DOWNCALL)
744 && (CTOV(cncp->dcp)->v_usecount == 1))
745 {
746 cncp->dcp->c_flags |= C_PURGING;
747 }
748 vrele(CTOV(cncp->dcp));
749
750 if (CTOV(cncp->cp)->v_flag & VTEXT) {
751 if (coda_vmflush(cncp->cp))
752 CODADEBUG(CODA_FLUSH,
753 myprintf(("coda_nc_flush: (%lx.%lx.%lx) busy\n", cncp->cp->c_fid.Volume, cncp->cp->c_fid.Vnode, cncp->cp->c_fid.Unique)); )
754 }
755
756 if ((dcstat == IS_DOWNCALL)
757 && (CTOV(cncp->cp)->v_usecount == 1))
758 {
759 cncp->cp->c_flags |= C_PURGING;
760 }
761 vrele(CTOV(cncp->cp));
762
763 crfree(cncp->cred);
764 bzero(DATA_PART(cncp),DATA_SIZE);
765 }
766 }
767
768 for (i = 0; i < coda_nc_hashsize; i++)
769 coda_nc_hash[i].length = 0;
770 }
771
772 /*
773 * Debugging routines
774 */
775
776 /*
777 * This routine should print out all the hash chains to the console.
778 */
779 void
780 print_coda_nc(void)
781 {
782 int hash;
783 struct coda_cache *cncp;
784
785 for (hash = 0; hash < coda_nc_hashsize; hash++) {
786 myprintf(("\nhash %d\n",hash));
787
788 for (cncp = coda_nc_hash[hash].hash_next;
789 cncp != (struct coda_cache *)&coda_nc_hash[hash];
790 cncp = cncp->hash_next) {
791 myprintf(("cp %p dcp %p cred %p name %s\n",
792 cncp->cp, cncp->dcp,
793 cncp->cred, cncp->name));
794 }
795 }
796 }
797
798 void
799 coda_nc_gather_stats(void)
800 {
801 int i, max = 0, sum = 0, temp, zeros = 0, ave, n;
802
803 for (i = 0; i < coda_nc_hashsize; i++) {
804 if (coda_nc_hash[i].length) {
805 sum += coda_nc_hash[i].length;
806 } else {
807 zeros++;
808 }
809
810 if (coda_nc_hash[i].length > max)
811 max = coda_nc_hash[i].length;
812 }
813
814 /*
815 * When computing the Arithmetic mean, only count slots which
816 * are not empty in the distribution.
817 */
818 coda_nc_stat.Sum_bucket_len = sum;
819 coda_nc_stat.Num_zero_len = zeros;
820 coda_nc_stat.Max_bucket_len = max;
821
822 if ((n = coda_nc_hashsize - zeros) > 0)
823 ave = sum / n;
824 else
825 ave = 0;
826
827 sum = 0;
828 for (i = 0; i < coda_nc_hashsize; i++) {
829 if (coda_nc_hash[i].length) {
830 temp = coda_nc_hash[i].length - ave;
831 sum += temp * temp;
832 }
833 }
834 coda_nc_stat.Sum2_bucket_len = sum;
835 }
836
837 /*
838 * The purpose of this routine is to allow the hash and cache sizes to be
839 * changed dynamically. This should only be used in controlled environments,
840 * it makes no effort to lock other users from accessing the cache while it
841 * is in an improper state (except by turning the cache off).
842 */
843 int
844 coda_nc_resize(hashsize, heapsize, dcstat)
845 int hashsize, heapsize;
846 enum dc_status dcstat;
847 {
848 if ((hashsize % 2) || (heapsize % 2)) { /* Illegal hash or cache sizes */
849 return(EINVAL);
850 }
851
852 coda_nc_use = 0; /* Turn the cache off */
853
854 coda_nc_flush(dcstat); /* free any cnodes in the cache */
855
856 /* WARNING: free must happen *before* size is reset */
857 CODA_FREE(coda_nc_heap,TOTAL_CACHE_SIZE);
858 CODA_FREE(coda_nc_hash,TOTAL_HASH_SIZE);
859
860 coda_nc_hashsize = hashsize;
861 coda_nc_size = heapsize;
862
863 coda_nc_init(); /* Set up a cache with the new size */
864
865 coda_nc_use = 1; /* Turn the cache back on */
866 return(0);
867 }
868
869 char coda_nc_name_buf[CODA_MAXNAMLEN+1];
870
871 void
872 coda_nc_name(struct cnode *cp)
873 {
874 struct coda_cache *cncp, *ncncp;
875 int i;
876
877 if (coda_nc_use == 0) /* Cache is off */
878 return;
879
880 for (i = 0; i < coda_nc_hashsize; i++) {
881 for (cncp = coda_nc_hash[i].hash_next;
882 cncp != (struct coda_cache *)&coda_nc_hash[i];
883 cncp = ncncp) {
884 ncncp = cncp->hash_next;
885 if (cncp->cp == cp) {
886 bcopy(cncp->name, coda_nc_name_buf, cncp->namelen);
887 coda_nc_name_buf[cncp->namelen] = 0;
888 printf(" is %s (%p,%p)@%p",
889 coda_nc_name_buf, cncp->cp, cncp->dcp, cncp);
890 }
891
892 }
893 }
894 }
895