1 /* $NetBSD: coda_namecache.c,v 1.23 2009/03/18 17:06:48 cegger Exp $ */
5 * Coda: an Experimental Distributed File System
8 * Copyright (c) 1987-1998 Carnegie Mellon University
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
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.
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.
31 * @(#) coda/coda_namecache.c,v 1.1.1.1 1998/08/29 21:26:45 rvb Exp $
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.
43 * This code was written for the Coda file system at Carnegie Mellon University.
44 * Contributers include David Steere, James Kistler, and M. Satyanarayanan.
48 * This module contains the routines to implement the CODA name cache. The
49 * purpose of this cache is to reduce the cost of translating pathnames
50 * into Vice FIDs. Each entry in the cache contains the name of the file,
51 * the vnode (FID) of the parent directory, and the cred structure of the
52 * user accessing the file.
54 * The first time a file is accessed, it is looked up by the local Venus
55 * which first insures that the user has access to the file. In addition
56 * we are guaranteed that Venus will invalidate any name cache entries in
57 * case the user no longer should be able to access the file. For these
58 * reasons we do not need to keep access list information as well as a
59 * cred structure for each entry.
61 * The table can be accessed through the routines cnc_init(), cnc_enter(),
62 * cnc_lookup(), cnc_rmfidcred(), cnc_rmfid(), cnc_rmcred(), and cnc_purge().
63 * There are several other routines which aid in the implementation of the
69 * 1. The name cache holds a reference to every vnode in it. Hence files can not be
70 * closed or made inactive until they are released.
71 * 2. coda_nc_name(cp) was added to get a name for a cnode pointer for debugging.
72 * 3. coda_nc_find() has debug code to detect when entries are stored with different
73 * credentials. We don't understand yet, if/how entries are NOT EQ but still
75 * 4. I wonder if this name cache could be replace by the vnode name cache.
76 * The latter has no zapping functions, so probably not.
79 #include <sys/cdefs.h>
80 __KERNEL_RCSID(0, "$NetBSD: coda_namecache.c,v 1.23 2009/03/18 17:06:48 cegger Exp $");
82 #include <sys/param.h>
83 #include <sys/errno.h>
84 #include <sys/malloc.h>
85 #include <sys/select.h>
86 #include <sys/kauth.h>
88 #include <coda/coda.h>
89 #include <coda/cnode.h>
90 #include <coda/coda_namecache.h>
93 #include <coda/coda_vnops.h>
97 * Declaration of the name cache data structure.
100 int coda_nc_use
= 1; /* Indicate use of CODA Name Cache */
102 int coda_nc_size
= CODA_NC_CACHESIZE
; /* size of the cache */
103 int coda_nc_hashsize
= CODA_NC_HASHSIZE
; /* size of the primary hash */
105 struct coda_cache
*coda_nc_heap
; /* pointer to the cache entries */
106 struct coda_hash
*coda_nc_hash
; /* hash table of cfscache pointers */
107 struct coda_lru coda_nc_lru
; /* head of lru chain */
109 struct coda_nc_statistics coda_nc_stat
; /* Keep various stats */
112 * for testing purposes
114 int coda_nc_debug
= 0;
117 * Entry points for the CODA Name Cache
119 static struct coda_cache
*
120 coda_nc_find(struct cnode
*dcp
, const char *name
, int namelen
,
121 kauth_cred_t cred
, int hash
);
123 coda_nc_remove(struct coda_cache
*cncp
, enum dc_status dcstat
);
126 * Initialize the cache, the LRU structure and the Hash structure(s)
129 #define TOTAL_CACHE_SIZE (sizeof(struct coda_cache) * coda_nc_size)
130 #define TOTAL_HASH_SIZE (sizeof(struct coda_hash) * coda_nc_hashsize)
132 int coda_nc_initialized
= 0; /* Initially the cache has not been initialized */
139 /* zero the statistics structure */
141 memset(&coda_nc_stat
, 0, (sizeof(struct coda_nc_statistics
)));
144 printf("CODA NAME CACHE: CACHE %d, HASH TBL %d\n", CODA_NC_CACHESIZE
, CODA_NC_HASHSIZE
);
146 CODA_ALLOC(coda_nc_heap
, struct coda_cache
*, TOTAL_CACHE_SIZE
);
147 CODA_ALLOC(coda_nc_hash
, struct coda_hash
*, TOTAL_HASH_SIZE
);
149 memset(coda_nc_heap
, 0, TOTAL_CACHE_SIZE
);
150 memset(coda_nc_hash
, 0, TOTAL_HASH_SIZE
);
152 TAILQ_INIT(&coda_nc_lru
.head
);
154 for (i
=0; i
< coda_nc_size
; i
++) { /* initialize the heap */
155 TAILQ_INSERT_HEAD(&coda_nc_lru
.head
, &coda_nc_heap
[i
], lru
);
158 for (i
=0; i
< coda_nc_hashsize
; i
++) { /* initialize the hashtable */
159 LIST_INIT(&coda_nc_hash
[i
].head
);
162 coda_nc_initialized
++;
166 * Auxillary routines -- shouldn't be entry points
169 static struct coda_cache
*
170 coda_nc_find(struct cnode
*dcp
, const char *name
, int namelen
,
171 kauth_cred_t cred
, int hash
)
174 * hash to find the appropriate bucket, look through the chain
175 * for the right entry (especially right cred, unless cred == 0)
177 struct coda_cache
*cncp
;
180 CODA_NC_DEBUG(CODA_NC_FIND
,
181 myprintf(("coda_nc_find(dcp %p, name %s, len %d, cred %p, hash %d\n",
182 dcp
, name
, namelen
, cred
, hash
));)
184 LIST_FOREACH(cncp
, &coda_nc_hash
[hash
].head
, hash
)
187 if ((CODA_NAMEMATCH(cncp
, name
, namelen
, dcp
)) &&
188 ((cred
== 0) || (cncp
->cred
== cred
)))
190 /* compare cr_uid instead */
191 coda_nc_stat
.Search_len
+= count
;
195 else if (CODA_NAMEMATCH(cncp
, name
, namelen
, dcp
)) {
196 printf("coda_nc_find: name %s, new cred = %p, cred = %p\n",
197 name
, cred
, cncp
->cred
);
198 printf("nref %d, nuid %d, ngid %d // oref %d, ocred %d, ogid %d\n",
199 kauth_cred_getrefcnt(cred
),
200 kauth_cred_geteuid(cred
),
201 kauth_cred_getegid(cred
),
202 kauth_cred_getrefcnt(cncp
->cred
),
203 kauth_cred_geteuid(cncp
->cred
),
204 kauth_cred_getegid(cncp
->cred
));
206 print_cred(cncp
->cred
);
212 return((struct coda_cache
*)0);
216 * Enter a new (dir cnode, name) pair into the cache, updating the
217 * LRU and Hash as needed.
220 coda_nc_enter(struct cnode
*dcp
, const char *name
, int namelen
,
221 kauth_cred_t cred
, struct cnode
*cp
)
223 struct coda_cache
*cncp
;
226 if (coda_nc_use
== 0) /* Cache is off */
229 CODA_NC_DEBUG(CODA_NC_ENTER
,
230 myprintf(("Enter: dcp %p cp %p name %s cred %p \n",
231 dcp
, cp
, name
, cred
)); )
233 if (namelen
> CODA_NC_NAMELEN
) {
234 CODA_NC_DEBUG(CODA_NC_ENTER
,
235 myprintf(("long name enter %s\n",name
));)
236 coda_nc_stat
.long_name_enters
++; /* record stats */
240 hash
= CODA_NC_HASH(name
, namelen
, dcp
);
241 cncp
= coda_nc_find(dcp
, name
, namelen
, cred
, hash
);
242 if (cncp
!= (struct coda_cache
*) 0) {
243 coda_nc_stat
.dbl_enters
++; /* duplicate entry */
247 coda_nc_stat
.enters
++; /* record the enters statistic */
249 /* Grab the next element in the lru chain */
250 cncp
= TAILQ_FIRST(&coda_nc_lru
.head
);
251 TAILQ_REMOVE(&coda_nc_lru
.head
, cncp
, lru
);
253 if (CODA_NC_VALID(cncp
)) {
254 /* Seems really ugly, but we have to decrement the appropriate
255 hash bucket length here, so we have to find the hash bucket
257 coda_nc_hash
[CODA_NC_HASH(cncp
->name
, cncp
->namelen
, cncp
->dcp
)].length
--;
259 coda_nc_stat
.lru_rm
++; /* zapped a valid entry */
260 LIST_REMOVE(cncp
, hash
);
261 vrele(CTOV(cncp
->dcp
));
262 vrele(CTOV(cncp
->cp
));
263 kauth_cred_free(cncp
->cred
);
267 * Put a hold on the current vnodes and fill in the cache entry.
271 kauth_cred_hold(cred
);
274 cncp
->namelen
= namelen
;
277 memcpy(cncp
->name
, name
, (unsigned)namelen
);
279 /* Insert into the lru and hash chains. */
280 TAILQ_INSERT_TAIL(&coda_nc_lru
.head
, cncp
, lru
);
281 LIST_INSERT_HEAD(&coda_nc_hash
[hash
].head
, cncp
, hash
);
282 coda_nc_hash
[hash
].length
++; /* Used for tuning */
284 CODA_NC_DEBUG(CODA_NC_PRINTCODA_NC
, print_coda_nc(); )
288 * Find the (dir cnode, name) pair in the cache, if it's cred
289 * matches the input, return it, otherwise return 0
292 coda_nc_lookup(struct cnode
*dcp
, const char *name
, int namelen
,
296 struct coda_cache
*cncp
;
298 if (coda_nc_use
== 0) /* Cache is off */
299 return((struct cnode
*) 0);
301 if (namelen
> CODA_NC_NAMELEN
) {
302 CODA_NC_DEBUG(CODA_NC_LOOKUP
,
303 myprintf(("long name lookup %s\n",name
));)
304 coda_nc_stat
.long_name_lookups
++; /* record stats */
305 return((struct cnode
*) 0);
308 /* Use the hash function to locate the starting point,
309 then the search routine to go down the list looking for
313 hash
= CODA_NC_HASH(name
, namelen
, dcp
);
314 cncp
= coda_nc_find(dcp
, name
, namelen
, cred
, hash
);
315 if (cncp
== (struct coda_cache
*) 0) {
316 coda_nc_stat
.misses
++; /* record miss */
317 return((struct cnode
*) 0);
322 /* put this entry at the end of the LRU */
323 TAILQ_REMOVE(&coda_nc_lru
.head
, cncp
, lru
);
324 TAILQ_INSERT_TAIL(&coda_nc_lru
.head
, cncp
, lru
);
326 /* move it to the front of the hash chain */
327 /* don't need to change the hash bucket length */
328 LIST_REMOVE(cncp
, hash
);
329 LIST_INSERT_HEAD(&coda_nc_hash
[hash
].head
, cncp
, hash
);
331 CODA_NC_DEBUG(CODA_NC_LOOKUP
,
332 printf("lookup: dcp %p, name %s, cred %p = cp %p\n",
333 dcp
, name
, cred
, cncp
->cp
); )
339 coda_nc_remove(struct coda_cache
*cncp
, enum dc_status dcstat
)
342 * remove an entry -- vrele(cncp->dcp, cp), crfree(cred),
343 * remove it from it's hash chain, and
344 * place it at the head of the lru list.
346 CODA_NC_DEBUG(CODA_NC_REMOVE
,
347 myprintf(("coda_nc_remove %s from parent %s\n",
348 cncp
->name
, coda_f2s(&cncp
->dcp
->c_fid
))); )
351 LIST_REMOVE(cncp
, hash
);
352 memset(&cncp
->hash
, 0, sizeof(cncp
->hash
));
354 if ((dcstat
== IS_DOWNCALL
) && (CTOV(cncp
->dcp
)->v_usecount
== 1)) {
355 cncp
->dcp
->c_flags
|= C_PURGING
;
357 vrele(CTOV(cncp
->dcp
));
359 if ((dcstat
== IS_DOWNCALL
) && (CTOV(cncp
->cp
)->v_usecount
== 1)) {
360 cncp
->cp
->c_flags
|= C_PURGING
;
362 vrele(CTOV(cncp
->cp
));
364 kauth_cred_free(cncp
->cred
);
365 memset(DATA_PART(cncp
), 0, DATA_SIZE
);
367 /* move the null entry to the front for reuse */
368 TAILQ_REMOVE(&coda_nc_lru
.head
, cncp
, lru
);
369 TAILQ_INSERT_HEAD(&coda_nc_lru
.head
, cncp
, lru
);
373 * Remove all entries with a parent which has the input fid.
376 coda_nc_zapParentfid(CodaFid
*fid
, enum dc_status dcstat
)
378 /* To get to a specific fid, we might either have another hashing
379 function or do a sequential search through the cache for the
380 appropriate entries. The later may be acceptable since I don't
381 think callbacks or whatever Case 1 covers are frequent occurrences.
383 struct coda_cache
*cncp
, *ncncp
;
386 if (coda_nc_use
== 0) /* Cache is off */
389 CODA_NC_DEBUG(CODA_NC_ZAPPFID
,
390 myprintf(("ZapParent: fid %s\n", coda_f2s(fid
))); )
392 coda_nc_stat
.zapPfids
++;
394 for (i
= 0; i
< coda_nc_hashsize
; i
++) {
397 * Need to save the hash_next pointer in case we remove the
398 * entry. remove causes hash_next to point to itself.
401 ncncp
= LIST_FIRST(&coda_nc_hash
[i
].head
);
402 while ((cncp
= ncncp
) != NULL
) {
403 ncncp
= LIST_NEXT(cncp
, hash
);
405 if (coda_fid_eq(&(cncp
->dcp
->c_fid
), fid
)) {
406 coda_nc_hash
[i
].length
--; /* Used for tuning */
407 coda_nc_remove(cncp
, dcstat
);
414 * Remove all entries which have the same fid as the input
417 coda_nc_zapfid(CodaFid
*fid
, enum dc_status dcstat
)
419 /* See comment for zapParentfid. This routine will be used
420 if attributes are being cached.
422 struct coda_cache
*cncp
, *ncncp
;
425 if (coda_nc_use
== 0) /* Cache is off */
428 CODA_NC_DEBUG(CODA_NC_ZAPFID
,
429 myprintf(("Zapfid: fid %s\n", coda_f2s(fid
))); )
431 coda_nc_stat
.zapFids
++;
433 for (i
= 0; i
< coda_nc_hashsize
; i
++) {
435 ncncp
= LIST_FIRST(&coda_nc_hash
[i
].head
);
436 while ((cncp
= ncncp
) != NULL
) {
437 ncncp
= LIST_NEXT(cncp
, hash
);
439 if (coda_fid_eq(&cncp
->cp
->c_fid
, fid
)) {
440 coda_nc_hash
[i
].length
--; /* Used for tuning */
441 coda_nc_remove(cncp
, dcstat
);
448 * Remove all entries which match the fid and the cred
451 coda_nc_zapvnode(CodaFid
*fid
, kauth_cred_t cred
,
452 enum dc_status dcstat
)
454 /* See comment for zapfid. I don't think that one would ever
455 want to zap a file with a specific cred from the kernel.
456 We'll leave this one unimplemented.
458 if (coda_nc_use
== 0) /* Cache is off */
461 CODA_NC_DEBUG(CODA_NC_ZAPVNODE
,
462 myprintf(("Zapvnode: fid %s cred %p\n",
463 coda_f2s(fid
), cred
)); )
467 * Remove all entries which have the (dir vnode, name) pair
470 coda_nc_zapfile(struct cnode
*dcp
, const char *name
, int namelen
)
472 /* use the hash function to locate the file, then zap all
473 entries of it regardless of the cred.
475 struct coda_cache
*cncp
;
478 if (coda_nc_use
== 0) /* Cache is off */
481 CODA_NC_DEBUG(CODA_NC_ZAPFILE
,
482 myprintf(("Zapfile: dcp %p name %s \n",
485 if (namelen
> CODA_NC_NAMELEN
) {
486 coda_nc_stat
.long_remove
++; /* record stats */
490 coda_nc_stat
.zapFile
++;
492 hash
= CODA_NC_HASH(name
, namelen
, dcp
);
493 cncp
= coda_nc_find(dcp
, name
, namelen
, 0, hash
);
496 coda_nc_hash
[hash
].length
--; /* Used for tuning */
498 coda_nc_remove(cncp
, NOT_DOWNCALL
);
499 cncp
= coda_nc_find(dcp
, name
, namelen
, 0, hash
);
504 * Remove all the entries for a particular user. Used when tokens expire.
505 * A user is determined by his/her effective user id (id_uid).
508 coda_nc_purge_user(uid_t uid
, enum dc_status dcstat
)
511 * I think the best approach is to go through the entire cache
512 * via HASH or whatever and zap all entries which match the
513 * input cred. Or just flush the whole cache. It might be
514 * best to go through on basis of LRU since cache will almost
515 * always be full and LRU is more straightforward.
518 struct coda_cache
*cncp
, *ncncp
;
521 if (coda_nc_use
== 0) /* Cache is off */
524 CODA_NC_DEBUG(CODA_NC_PURGEUSER
,
525 myprintf(("ZapDude: uid %x\n", uid
)); )
526 coda_nc_stat
.zapUsers
++;
528 ncncp
= TAILQ_FIRST(&coda_nc_lru
.head
);
529 while ((cncp
= ncncp
) != NULL
) {
530 ncncp
= TAILQ_NEXT(cncp
, lru
);
532 if ((CODA_NC_VALID(cncp
)) &&
533 (kauth_cred_geteuid(cncp
->cred
) == uid
)) {
534 /* Seems really ugly, but we have to decrement the appropriate
535 hash bucket length here, so we have to find the hash bucket
537 hash
= CODA_NC_HASH(cncp
->name
, cncp
->namelen
, cncp
->dcp
);
538 coda_nc_hash
[hash
].length
--; /* For performance tuning */
540 coda_nc_remove(cncp
, dcstat
);
546 * Flush the entire name cache. In response to a flush of the Venus cache.
549 coda_nc_flush(enum dc_status dcstat
)
551 /* One option is to deallocate the current name cache and
552 call init to start again. Or just deallocate, then rebuild.
553 Or again, we could just go through the array and zero the
558 * Go through the whole lru chain and kill everything as we go.
559 * I don't use remove since that would rebuild the lru chain
560 * as it went and that seemed unneccesary.
562 struct coda_cache
*cncp
;
565 if (coda_nc_use
== 0) /* Cache is off */
568 coda_nc_stat
.Flushes
++;
570 TAILQ_FOREACH(cncp
, &coda_nc_lru
.head
, lru
) {
571 if (CODA_NC_VALID(cncp
)) { /* only zero valid nodes */
572 LIST_REMOVE(cncp
, hash
);
573 memset(&cncp
->hash
, 0, sizeof(cncp
->hash
));
575 if ((dcstat
== IS_DOWNCALL
)
576 && (CTOV(cncp
->dcp
)->v_usecount
== 1))
578 cncp
->dcp
->c_flags
|= C_PURGING
;
580 vrele(CTOV(cncp
->dcp
));
582 if (CTOV(cncp
->cp
)->v_iflag
& VI_TEXT
) {
583 if (coda_vmflush(cncp
->cp
))
584 CODADEBUG(CODA_FLUSH
,
585 myprintf(("coda_nc_flush: %s busy\n",
586 coda_f2s(&cncp
->cp
->c_fid
))); )
589 if ((dcstat
== IS_DOWNCALL
)
590 && (CTOV(cncp
->cp
)->v_usecount
== 1))
592 cncp
->cp
->c_flags
|= C_PURGING
;
594 vrele(CTOV(cncp
->cp
));
596 kauth_cred_free(cncp
->cred
);
597 memset(DATA_PART(cncp
), 0, DATA_SIZE
);
601 for (i
= 0; i
< coda_nc_hashsize
; i
++)
602 coda_nc_hash
[i
].length
= 0;
610 * This routine should print out all the hash chains to the console.
616 struct coda_cache
*cncp
;
618 for (hash
= 0; hash
< coda_nc_hashsize
; hash
++) {
619 myprintf(("\nhash %d\n",hash
));
621 LIST_FOREACH(cncp
, &coda_nc_hash
[hash
].head
, hash
) {
622 myprintf(("cp %p dcp %p cred %p name %s\n",
624 cncp
->cred
, cncp
->name
));
630 coda_nc_gather_stats(void)
632 int i
, xmax
= 0, sum
= 0, temp
, zeros
= 0, ave
, n
;
634 for (i
= 0; i
< coda_nc_hashsize
; i
++) {
635 if (coda_nc_hash
[i
].length
) {
636 sum
+= coda_nc_hash
[i
].length
;
641 if (coda_nc_hash
[i
].length
> xmax
)
642 xmax
= coda_nc_hash
[i
].length
;
646 * When computing the Arithmetic mean, only count slots which
647 * are not empty in the distribution.
649 coda_nc_stat
.Sum_bucket_len
= sum
;
650 coda_nc_stat
.Num_zero_len
= zeros
;
651 coda_nc_stat
.Max_bucket_len
= xmax
;
653 if ((n
= coda_nc_hashsize
- zeros
) > 0)
659 for (i
= 0; i
< coda_nc_hashsize
; i
++) {
660 if (coda_nc_hash
[i
].length
) {
661 temp
= coda_nc_hash
[i
].length
- ave
;
665 coda_nc_stat
.Sum2_bucket_len
= sum
;
669 * The purpose of this routine is to allow the hash and cache sizes to be
670 * changed dynamically. This should only be used in controlled environments,
671 * it makes no effort to lock other users from accessing the cache while it
672 * is in an improper state (except by turning the cache off).
675 coda_nc_resize(int hashsize
, int heapsize
, enum dc_status dcstat
)
677 if ((hashsize
% 2) || (heapsize
% 2)) { /* Illegal hash or cache sizes */
681 coda_nc_use
= 0; /* Turn the cache off */
683 coda_nc_flush(dcstat
); /* free any cnodes in the cache */
685 /* WARNING: free must happen *before* size is reset */
686 CODA_FREE(coda_nc_heap
,TOTAL_CACHE_SIZE
);
687 CODA_FREE(coda_nc_hash
,TOTAL_HASH_SIZE
);
689 coda_nc_hashsize
= hashsize
;
690 coda_nc_size
= heapsize
;
692 coda_nc_init(); /* Set up a cache with the new size */
694 coda_nc_use
= 1; /* Turn the cache back on */
698 char coda_nc_name_buf
[CODA_MAXNAMLEN
+1];
701 coda_nc_name(struct cnode
*cp
)
703 struct coda_cache
*cncp
;
706 if (coda_nc_use
== 0) /* Cache is off */
709 for (i
= 0; i
< coda_nc_hashsize
; i
++) {
711 LIST_FOREACH(cncp
, &coda_nc_hash
[i
].head
, hash
) {
712 if (cncp
->cp
== cp
) {
713 memcpy(coda_nc_name_buf
, cncp
->name
, cncp
->namelen
);
714 coda_nc_name_buf
[cncp
->namelen
] = 0;
715 printf(" is %s (%p,%p)@%p",
716 coda_nc_name_buf
, cncp
->cp
, cncp
->dcp
, cncp
);