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