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