1 /*
2 *
3 * Coda: an Experimental Distributed File System
4 * Release 3.1
5 *
6 * Copyright (c) 1987-1998 Carnegie Mellon University
7 * All Rights Reserved
8 *
9 * Permission to use, copy, modify and distribute this software and its
10 * documentation is hereby granted, provided that both the copyright
11 * notice and this permission notice appear in all copies of the
12 * software, derivative works or modified versions, and any portions
13 * thereof, and that both notices appear in supporting documentation, and
14 * that credit is given to Carnegie Mellon University in all documents
15 * and publicity pertaining to direct or indirect use of this code or its
16 * derivatives.
17 *
18 * CODA IS AN EXPERIMENTAL SOFTWARE SYSTEM AND IS KNOWN TO HAVE BUGS,
19 * SOME OF WHICH MAY HAVE SERIOUS CONSEQUENCES. CARNEGIE MELLON ALLOWS
20 * FREE USE OF THIS SOFTWARE IN ITS "AS IS" CONDITION. CARNEGIE MELLON
21 * DISCLAIMS ANY LIABILITY OF ANY KIND FOR ANY DAMAGES WHATSOEVER
22 * RESULTING DIRECTLY OR INDIRECTLY FROM THE USE OF THIS SOFTWARE OR OF
23 * ANY DERIVATIVE WORK.
24 *
25 * Carnegie Mellon encourages users of this software to return any
26 * improvements or extensions that they make, and to grant Carnegie
27 * Mellon the rights to redistribute these changes without encumbrance.
28 *
29 * @(#) src/sys/coda/coda_namecache.c,v 1.1.1.1 1998/08/29 21:14:52 rvb Exp $
30 * $FreeBSD$
31 *
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.8 1998/10/28 19:33:50 rvb
51 * Venus must be passed O_CREAT flag on VOP_OPEN iff this is
52 * a creat so that we can will allow a mode 444 file to be
53 * written into. Sync with the latest coda.h and deal with
54 * collateral damage.
55 *
56 * Revision 1.7 1998/09/28 20:52:58 rvb
57 * Cleanup and fix THE bug
58 *
59 * Revision 1.6 1998/09/25 17:38:31 rvb
60 * Put "stray" printouts under DIAGNOSTIC. Make everything build
61 * with DEBUG on. Add support for lkm. (The macro's don't work
62 * for me; for a good chuckle look at the end of coda_fbsd.c.)
63 *
64 * Revision 1.5 1998/09/13 13:57:59 rvb
65 * Finish conversion of cfs -> coda
66 *
67 * Revision 1.4 1998/09/11 18:50:17 rvb
68 * All the references to cfs, in symbols, structs, and strings
69 * have been changed to coda. (Same for CFS.)
70 *
71 * Revision 1.2 1998/09/02 19:09:53 rvb
72 * Pass2 complete
73 *
74 * Revision 1.1.1.1 1998/08/29 21:14:52 rvb
75 * Very Preliminary Coda
76 *
77 * Revision 1.11 1998/08/28 18:12:16 rvb
78 * Now it also works on FreeBSD -current. This code will be
79 * committed to the FreeBSD -current and NetBSD -current
80 * trees. It will then be tailored to the particular platform
81 * by flushing conditional code.
82 *
83 * Revision 1.10 1998/08/18 17:05:14 rvb
84 * Don't use __RCSID now
85 *
86 * Revision 1.9 1998/08/18 16:31:39 rvb
87 * Sync the code for NetBSD -current; test on 1.3 later
88 *
89 * Revision 1.8 98/01/31 20:53:10 rvb
90 * First version that works on FreeBSD 2.2.5
91 *
92 * Revision 1.7 98/01/23 11:53:39 rvb
93 * Bring RVB_CODA1_1 to HEAD
94 *
95 * Revision 1.6.2.4 98/01/23 11:21:02 rvb
96 * Sync with 2.2.5
97 *
98 * Revision 1.6.2.3 97/12/16 12:40:03 rvb
99 * Sync with 1.3
100 *
101 * Revision 1.6.2.2 97/12/09 16:07:10 rvb
102 * Sync with vfs/include/coda.h
103 *
104 * Revision 1.6.2.1 97/12/06 17:41:18 rvb
105 * Sync with peters coda.h
106 *
107 * Revision 1.6 97/12/05 10:39:13 rvb
108 * Read CHANGES
109 *
110 * Revision 1.5.4.7 97/11/25 08:08:43 rvb
111 * cfs_venus ... done; until cred/vattr change
112 *
113 * Revision 1.5.4.6 97/11/24 15:44:43 rvb
114 * Final cfs_venus.c w/o macros, but one locking bug
115 *
116 * Revision 1.5.4.5 97/11/20 11:46:38 rvb
117 * Capture current cfs_venus
118 *
119 * Revision 1.5.4.4 97/11/18 10:27:13 rvb
120 * cfs_nbsd.c is DEAD!!!; integrated into cfs_vf/vnops.c
121 * cfs_nb_foo and cfs_foo are joined
122 *
123 * Revision 1.5.4.3 97/11/13 22:02:57 rvb
124 * pass2 cfs_NetBSD.h mt
125 *
126 * Revision 1.5.4.2 97/11/12 12:09:35 rvb
127 * reorg pass1
128 *
129 * Revision 1.5.4.1 97/10/28 23:10:12 rvb
130 * >64Meg; venus can be killed!
131 *
132 * Revision 1.5 97/08/05 11:08:01 lily
133 * Removed cfsnc_replace, replaced it with a coda_find, unhash, and
134 * rehash. This fixes a cnode leak and a bug in which the fid is
135 * not actually replaced. (cfs_namecache.c, cfsnc.h, cfs_subr.c)
136 *
137 * Revision 1.4 96/12/12 22:10:57 bnoble
138 * Fixed the "downcall invokes venus operation" deadlock in all known cases.
139 * There may be more
140 *
141 * Revision 1.3 1996/11/08 18:06:09 bnoble
142 * Minor changes in vnode operation signature, VOP_UPDATE signature, and
143 * some newly defined bits in the include files.
144 *
145 * Revision 1.2 1996/01/02 16:56:50 bnoble
146 * Added support for Coda MiniCache and raw inode calls (final commit)
147 *
148 * Revision 1.1.2.1 1995/12/20 01:57:15 bnoble
149 * Added CODA-specific files
150 *
151 * Revision 3.1.1.1 1995/03/04 19:07:57 bnoble
152 * Branch for NetBSD port revisions
153 *
154 * Revision 3.1 1995/03/04 19:07:56 bnoble
155 * Bump to major revision 3 to prepare for NetBSD port
156 *
157 * Revision 2.3 1994/10/14 09:57:54 dcs
158 * Made changes 'cause sun4s have braindead compilers
159 *
160 * Revision 2.2 94/08/28 19:37:35 luqi
161 * Add a new CODA_REPLACE call to allow venus to replace a ViceFid in the
162 * mini-cache.
163 *
164 * In "cfs.h":
165 * Add CODA_REPLACE decl.
166 *
167 * In "cfs_namecache.c":
168 * Add routine cfsnc_replace.
169 *
170 * In "cfs_subr.c":
171 * Add case-statement to process CODA_REPLACE.
172 *
173 * In "cfsnc.h":
174 * Add decl for CODA_NC_REPLACE.
175 *
176 *
177 * Revision 2.1 94/07/21 16:25:15 satya
178 * Conversion to C++ 3.0; start of Coda Release 2.0
179 *
180 * Revision 1.2 92/10/27 17:58:21 lily
181 * merge kernel/latest and alpha/src/cfs
182 *
183 * Revision 2.3 92/09/30 14:16:20 mja
184 * call coda_flush instead of calling inode_uncache_try directly
185 * (from dcs). Also...
186 *
187 * Substituted rvb's history blurb so that we agree with Mach 2.5 sources.
188 * [91/02/09 jjk]
189 *
190 * Added contributors blurb.
191 * [90/12/13 jjk]
192 *
193 * Revision 2.2 90/07/05 11:26:30 mrt
194 * Created for the Coda File System.
195 * [90/05/23 dcs]
196 *
197 * Revision 1.3 90/05/31 17:01:24 dcs
198 * Prepare for merge with facilities kernel.
199 *
200 *
201 */
202
203 /*
204 * This module contains the routines to implement the CODA name cache. The
205 * purpose of this cache is to reduce the cost of translating pathnames
206 * into Vice FIDs. Each entry in the cache contains the name of the file,
207 * the vnode (FID) of the parent directory, and the cred structure of the
208 * user accessing the file.
209 *
210 * The first time a file is accessed, it is looked up by the local Venus
211 * which first insures that the user has access to the file. In addition
212 * we are guaranteed that Venus will invalidate any name cache entries in
213 * case the user no longer should be able to access the file. For these
214 * reasons we do not need to keep access list information as well as a
215 * cred structure for each entry.
216 *
217 * The table can be accessed through the routines cnc_init(), cnc_enter(),
218 * cnc_lookup(), cnc_rmfidcred(), cnc_rmfid(), cnc_rmcred(), and cnc_purge().
219 * There are several other routines which aid in the implementation of the
220 * hash table.
221 */
222
223 /*
224 * NOTES: rvb@cs
225 * 1. The name cache holds a reference to every vnode in it. Hence files can not be
226 * closed or made inactive until they are released.
227 * 2. coda_nc_name(cp) was added to get a name for a cnode pointer for debugging.
228 * 3. coda_nc_find() has debug code to detect when entries are stored with different
229 * credentials. We don't understand yet, if/how entries are NOT EQ but still
230 * EQUAL
231 * 4. I wonder if this name cache could be replace by the vnode name cache.
232 * The latter has no zapping functions, so probably not.
233 */
234
235 #include <sys/param.h>
236 #include <sys/errno.h>
237 #include <sys/malloc.h>
238 #include <sys/ucred.h>
239 #include <sys/select.h>
240
241 #ifndef insque
242 #include <sys/systm.h>
243 #endif /* insque */
244
245 #include <vm/vm.h>
246 #include <vm/vm_object.h>
247
248 #include <coda/coda.h>
249 #include <coda/cnode.h>
250 #include <coda/coda_namecache.h>
251
252 #ifdef DEBUG
253 #include <coda/coda_vnops.h>
254 #endif
255
256 /*
257 * Declaration of the name cache data structure.
258 */
259
260 int coda_nc_use = 1; /* Indicate use of CODA Name Cache */
261 int coda_nc_size = CODA_NC_CACHESIZE; /* size of the cache */
262 int coda_nc_hashsize = CODA_NC_HASHSIZE; /* size of the primary hash */
263
264 struct coda_cache *coda_nc_heap; /* pointer to the cache entries */
265 struct coda_hash *coda_nc_hash; /* hash table of coda_cache pointers */
266 struct coda_lru coda_nc_lru; /* head of lru chain */
267
268 struct coda_nc_statistics coda_nc_stat; /* Keep various stats */
269
270 /*
271 * for testing purposes
272 */
273 int coda_nc_debug = 0;
274
275 /*
276 * Entry points for the CODA Name Cache
277 */
278 static struct coda_cache *coda_nc_find(struct cnode *dcp, const char *name, int namelen,
279 struct ucred *cred, int hash);
280 static void 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 /*
591 * Remove all entries which have the same fid as the input
592 */
593 void
594 coda_nc_zapfid(fid, dcstat)
595 ViceFid *fid;
596 enum dc_status dcstat;
597 {
598 /* See comment for zapParentfid. This routine will be used
599 if attributes are being cached.
600 */
601 struct coda_cache *cncp, *ncncp;
602 int i;
603
604 if (coda_nc_use == 0) /* Cache is off */
605 return;
606
607 CODA_NC_DEBUG(CODA_NC_ZAPFID,
608 myprintf(("Zapfid: fid 0x%lx, 0x%lx, 0x%lx \n",
609 fid->Volume, fid->Vnode, fid->Unique)); )
610
611 coda_nc_stat.zapFids++;
612
613 for (i = 0; i < coda_nc_hashsize; i++) {
614 for (cncp = coda_nc_hash[i].hash_next;
615 cncp != (struct coda_cache *)&coda_nc_hash[i];
616 cncp = ncncp) {
617 ncncp = cncp->hash_next;
618 if ((cncp->cp->c_fid.Volume == fid->Volume) &&
619 (cncp->cp->c_fid.Vnode == fid->Vnode) &&
620 (cncp->cp->c_fid.Unique == fid->Unique)) {
621 coda_nc_hash[i].length--; /* Used for tuning */
622 coda_nc_remove(cncp, dcstat);
623 }
624 }
625 }
626 }
627
628 /*
629 * Remove all entries which match the fid and the cred
630 */
631 void
632 coda_nc_zapvnode(fid, cred, dcstat)
633 ViceFid *fid;
634 struct ucred *cred;
635 enum dc_status dcstat;
636 {
637 /* See comment for zapfid. I don't think that one would ever
638 want to zap a file with a specific cred from the kernel.
639 We'll leave this one unimplemented.
640 */
641 if (coda_nc_use == 0) /* Cache is off */
642 return;
643
644 CODA_NC_DEBUG(CODA_NC_ZAPVNODE,
645 myprintf(("Zapvnode: fid 0x%lx, 0x%lx, 0x%lx cred %p\n",
646 fid->Volume, fid->Vnode, fid->Unique, cred)); )
647
648 }
649
650 /*
651 * Remove all entries which have the (dir vnode, name) pair
652 */
653 void
654 coda_nc_zapfile(dcp, name, namelen)
655 struct cnode *dcp;
656 const char *name;
657 int namelen;
658 {
659 /* use the hash function to locate the file, then zap all
660 entries of it regardless of the cred.
661 */
662 struct coda_cache *cncp;
663 int hash;
664
665 if (coda_nc_use == 0) /* Cache is off */
666 return;
667
668 CODA_NC_DEBUG(CODA_NC_ZAPFILE,
669 myprintf(("Zapfile: dcp %p name %s \n",
670 dcp, name)); )
671
672 if (namelen > CODA_NC_NAMELEN) {
673 coda_nc_stat.long_remove++; /* record stats */
674 return;
675 }
676
677 coda_nc_stat.zapFile++;
678
679 hash = CODA_NC_HASH(name, namelen, dcp);
680 cncp = coda_nc_find(dcp, name, namelen, 0, hash);
681
682 while (cncp) {
683 coda_nc_hash[hash].length--; /* Used for tuning */
684
685 coda_nc_remove(cncp, NOT_DOWNCALL);
686 cncp = coda_nc_find(dcp, name, namelen, 0, hash);
687 }
688 }
689
690 /*
691 * Remove all the entries for a particular user. Used when tokens expire.
692 * A user is determined by his/her effective user id (id_uid).
693 */
694 void
695 coda_nc_purge_user(uid, dcstat)
696 vuid_t uid;
697 enum dc_status dcstat;
698 {
699 /*
700 * I think the best approach is to go through the entire cache
701 * via HASH or whatever and zap all entries which match the
702 * input cred. Or just flush the whole cache. It might be
703 * best to go through on basis of LRU since cache will almost
704 * always be full and LRU is more straightforward.
705 */
706
707 struct coda_cache *cncp, *ncncp;
708 int hash;
709
710 if (coda_nc_use == 0) /* Cache is off */
711 return;
712
713 CODA_NC_DEBUG(CODA_NC_PURGEUSER,
714 myprintf(("ZapDude: uid %x\n", uid)); )
715 coda_nc_stat.zapUsers++;
716
717 for (cncp = CODA_NC_LRUGET(coda_nc_lru);
718 cncp != (struct coda_cache *)(&coda_nc_lru);
719 cncp = ncncp) {
720 ncncp = CODA_NC_LRUGET(*cncp);
721
722 if ((CODA_NC_VALID(cncp)) &&
723 ((cncp->cred)->cr_uid == uid)) {
724 /* Seems really ugly, but we have to decrement the appropriate
725 hash bucket length here, so we have to find the hash bucket
726 */
727 hash = CODA_NC_HASH(cncp->name, cncp->namelen, cncp->dcp);
728 coda_nc_hash[hash].length--; /* For performance tuning */
729
730 coda_nc_remove(cncp, dcstat);
731 }
732 }
733 }
734
735 /*
736 * Flush the entire name cache. In response to a flush of the Venus cache.
737 */
738 void
739 coda_nc_flush(dcstat)
740 enum dc_status dcstat;
741 {
742 /* One option is to deallocate the current name cache and
743 call init to start again. Or just deallocate, then rebuild.
744 Or again, we could just go through the array and zero the
745 appropriate fields.
746 */
747
748 /*
749 * Go through the whole lru chain and kill everything as we go.
750 * I don't use remove since that would rebuild the lru chain
751 * as it went and that seemed unneccesary.
752 */
753 struct coda_cache *cncp;
754 int i;
755
756 if (coda_nc_use == 0) /* Cache is off */
757 return;
758
759 coda_nc_stat.Flushes++;
760
761 for (cncp = CODA_NC_LRUGET(coda_nc_lru);
762 cncp != (struct coda_cache *)&coda_nc_lru;
763 cncp = CODA_NC_LRUGET(*cncp)) {
764 if (CODA_NC_VALID(cncp)) {
765
766 CODA_NC_HSHREM(cncp); /* only zero valid nodes */
767 CODA_NC_HSHNUL(cncp);
768 if ((dcstat == IS_DOWNCALL)
769 && (CTOV(cncp->dcp)->v_usecount == 1))
770 {
771 cncp->dcp->c_flags |= C_PURGING;
772 }
773 vrele(CTOV(cncp->dcp));
774
775 if (CTOV(cncp->cp)->v_flag & VTEXT) {
776 if (coda_vmflush(cncp->cp))
777 CODADEBUG(CODA_FLUSH,
778 myprintf(("coda_nc_flush: (%lx.%lx.%lx) busy\n", cncp->cp->c_fid.Volume, cncp->cp->c_fid.Vnode, cncp->cp->c_fid.Unique)); )
779 }
780
781 if ((dcstat == IS_DOWNCALL)
782 && (CTOV(cncp->cp)->v_usecount == 1))
783 {
784 cncp->cp->c_flags |= C_PURGING;
785 }
786 vrele(CTOV(cncp->cp));
787
788 crfree(cncp->cred);
789 bzero(DATA_PART(cncp),DATA_SIZE);
790 }
791 }
792
793 for (i = 0; i < coda_nc_hashsize; i++)
794 coda_nc_hash[i].length = 0;
795 }
796
797 /*
798 * Debugging routines
799 */
800
801 /*
802 * This routine should print out all the hash chains to the console.
803 */
804 void
805 print_coda_nc(void)
806 {
807 int hash;
808 struct coda_cache *cncp;
809
810 for (hash = 0; hash < coda_nc_hashsize; hash++) {
811 myprintf(("\nhash %d\n",hash));
812
813 for (cncp = coda_nc_hash[hash].hash_next;
814 cncp != (struct coda_cache *)&coda_nc_hash[hash];
815 cncp = cncp->hash_next) {
816 myprintf(("cp %p dcp %p cred %p name %s\n",
817 cncp->cp, cncp->dcp,
818 cncp->cred, cncp->name));
819 }
820 }
821 }
822
823 void
824 coda_nc_gather_stats(void)
825 {
826 int i, max = 0, sum = 0, temp, zeros = 0, ave, n;
827
828 for (i = 0; i < coda_nc_hashsize; i++) {
829 if (coda_nc_hash[i].length) {
830 sum += coda_nc_hash[i].length;
831 } else {
832 zeros++;
833 }
834
835 if (coda_nc_hash[i].length > max)
836 max = coda_nc_hash[i].length;
837 }
838
839 /*
840 * When computing the Arithmetic mean, only count slots which
841 * are not empty in the distribution.
842 */
843 coda_nc_stat.Sum_bucket_len = sum;
844 coda_nc_stat.Num_zero_len = zeros;
845 coda_nc_stat.Max_bucket_len = max;
846
847 if ((n = coda_nc_hashsize - zeros) > 0)
848 ave = sum / n;
849 else
850 ave = 0;
851
852 sum = 0;
853 for (i = 0; i < coda_nc_hashsize; i++) {
854 if (coda_nc_hash[i].length) {
855 temp = coda_nc_hash[i].length - ave;
856 sum += temp * temp;
857 }
858 }
859 coda_nc_stat.Sum2_bucket_len = sum;
860 }
861
862 /*
863 * The purpose of this routine is to allow the hash and cache sizes to be
864 * changed dynamically. This should only be used in controlled environments,
865 * it makes no effort to lock other users from accessing the cache while it
866 * is in an improper state (except by turning the cache off).
867 */
868 int
869 coda_nc_resize(hashsize, heapsize, dcstat)
870 int hashsize, heapsize;
871 enum dc_status dcstat;
872 {
873 if ((hashsize % 2) || (heapsize % 2)) { /* Illegal hash or cache sizes */
874 return(EINVAL);
875 }
876
877 coda_nc_use = 0; /* Turn the cache off */
878
879 coda_nc_flush(dcstat); /* free any cnodes in the cache */
880
881 /* WARNING: free must happen *before* size is reset */
882 CODA_FREE(coda_nc_heap,TOTAL_CACHE_SIZE);
883 CODA_FREE(coda_nc_hash,TOTAL_HASH_SIZE);
884
885 coda_nc_hashsize = hashsize;
886 coda_nc_size = heapsize;
887
888 coda_nc_init(); /* Set up a cache with the new size */
889
890 coda_nc_use = 1; /* Turn the cache back on */
891 return(0);
892 }
893
894 #ifdef DEBUG
895 char coda_nc_name_buf[CODA_MAXNAMLEN+1];
896
897 void
898 coda_nc_name(struct cnode *cp)
899 {
900 struct coda_cache *cncp, *ncncp;
901 int i;
902
903 if (coda_nc_use == 0) /* Cache is off */
904 return;
905
906 for (i = 0; i < coda_nc_hashsize; i++) {
907 for (cncp = coda_nc_hash[i].hash_next;
908 cncp != (struct coda_cache *)&coda_nc_hash[i];
909 cncp = ncncp) {
910 ncncp = cncp->hash_next;
911 if (cncp->cp == cp) {
912 bcopy(cncp->name, coda_nc_name_buf, cncp->namelen);
913 coda_nc_name_buf[cncp->namelen] = 0;
914 printf(" is %s (%p,%p)@%p",
915 coda_nc_name_buf, cncp->cp, cncp->dcp, cncp);
916 }
917
918 }
919 }
920 }
921 #endif
Cache object: 69270f6d47c5a2c274051b575add365a
|