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