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