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