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