3 * Coda: an Experimental Distributed File System
6 * Copyright (c) 1987-1998 Carnegie Mellon University
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
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.
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.
29 * @(#) src/sys/coda/coda_namecache.c,v 1.1.1.1 1998/08/29 21:14:52 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 filesystem 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/param.h>
80 #include <sys/systm.h>
81 #include <sys/errno.h>
83 #include <sys/malloc.h>
84 #include <sys/mutex.h>
85 #include <sys/ucred.h>
88 #include <vm/vm_object.h>
90 #include <coda/coda.h>
91 #include <coda/cnode.h>
92 #include <coda/coda_namecache.h>
95 #include <coda/coda_vnops.h>
99 * Declaration of the name cache data structure.
102 int coda_nc_use = 1; /* Indicate use of CODA Name Cache */
103 int coda_nc_size = CODA_NC_CACHESIZE; /* size of the cache */
104 int coda_nc_hashsize = CODA_NC_HASHSIZE; /* size of the primary hash */
106 struct coda_cache *coda_nc_heap; /* pointer to the cache entries */
107 struct coda_hash *coda_nc_hash; /* hash table of coda_cache pointers */
108 struct coda_lru coda_nc_lru; /* head of lru chain */
110 struct coda_nc_statistics coda_nc_stat; /* Keep various stats */
113 * for testing purposes
115 int coda_nc_debug = 0;
118 * Entry points for the CODA Name Cache
120 static struct coda_cache *coda_nc_find(struct cnode *dcp, const char *name, int namelen,
121 struct ucred *cred, int hash);
122 static void coda_nc_remove(struct coda_cache *cncp, enum dc_status dcstat);
125 * Initialize the cache, the LRU structure and the Hash structure(s)
128 #define TOTAL_CACHE_SIZE (sizeof(struct coda_cache) * coda_nc_size)
129 #define TOTAL_HASH_SIZE (sizeof(struct coda_hash) * coda_nc_hashsize)
131 int coda_nc_initialized = 0; /* Initially the cache has not been initialized */
138 /* zero the statistics structure */
140 bzero(&coda_nc_stat, (sizeof(struct coda_nc_statistics)));
143 printf("CODA NAME CACHE: CACHE %d, HASH TBL %d\n", CODA_NC_CACHESIZE, CODA_NC_HASHSIZE);
145 CODA_ALLOC(coda_nc_heap, struct coda_cache *, TOTAL_CACHE_SIZE);
146 CODA_ALLOC(coda_nc_hash, struct coda_hash *, TOTAL_HASH_SIZE);
148 coda_nc_lru.lru_next =
149 coda_nc_lru.lru_prev = (struct coda_cache *)LRU_PART(&coda_nc_lru);
152 for (i=0; i < coda_nc_size; i++) { /* initialize the heap */
153 CODA_NC_LRUINS(&coda_nc_heap[i], &coda_nc_lru);
154 CODA_NC_HSHNUL(&coda_nc_heap[i]);
155 coda_nc_heap[i].cp = coda_nc_heap[i].dcp = (struct cnode *)0;
158 for (i=0; i < coda_nc_hashsize; i++) { /* initialize the hashtable */
159 CODA_NC_HSHNUL((struct coda_cache *)&coda_nc_hash[i]);
162 coda_nc_initialized++;
166 * Auxillary routines -- shouldn't be entry points
169 static struct coda_cache *
170 coda_nc_find(dcp, name, namelen, cred, hash)
178 * hash to find the appropriate bucket, look through the chain
179 * for the right entry (especially right cred, unless cred == 0)
181 struct coda_cache *cncp;
184 CODA_NC_DEBUG(CODA_NC_FIND,
185 myprintf(("coda_nc_find(dcp %p, name %s, len %d, cred %p, hash %d\n",
186 dcp, name, namelen, cred, hash));)
188 for (cncp = coda_nc_hash[hash].hash_next;
189 cncp != (struct coda_cache *)&coda_nc_hash[hash];
190 cncp = cncp->hash_next, count++)
193 if ((CODA_NAMEMATCH(cncp, name, namelen, dcp)) &&
194 ((cred == 0) || (cncp->cred == cred)))
196 /* compare cr_uid instead */
197 coda_nc_stat.Search_len += count;
201 else if (CODA_NAMEMATCH(cncp, name, namelen, dcp)) {
202 printf("coda_nc_find: name %s, new cred = %p, cred = %p\n",
203 name, cred, cncp->cred);
204 printf("nref %d, nuid %d, ngid %d // oref %d, ocred %d, ogid %d\n",
205 cred->cr_ref, cred->cr_uid, cred->cr_gid,
206 cncp->cred->cr_ref, cncp->cred->cr_uid, cncp->cred->cr_gid);
208 print_cred(cncp->cred);
213 return((struct coda_cache *)0);
217 * Enter a new (dir cnode, name) pair into the cache, updating the
218 * LRU and Hash as needed.
221 coda_nc_enter(dcp, name, namelen, cred, cp)
228 struct coda_cache *cncp;
231 if (coda_nc_use == 0) /* Cache is off */
234 CODA_NC_DEBUG(CODA_NC_ENTER,
235 myprintf(("Enter: dcp %p cp %p name %s cred %p \n",
236 dcp, cp, name, cred)); )
238 if (namelen > CODA_NC_NAMELEN) {
239 CODA_NC_DEBUG(CODA_NC_ENTER,
240 myprintf(("long name enter %s\n",name));)
241 coda_nc_stat.long_name_enters++; /* record stats */
245 hash = CODA_NC_HASH(name, namelen, dcp);
246 cncp = coda_nc_find(dcp, name, namelen, cred, hash);
247 if (cncp != (struct coda_cache *) 0) {
248 coda_nc_stat.dbl_enters++; /* duplicate entry */
252 coda_nc_stat.enters++; /* record the enters statistic */
254 /* Grab the next element in the lru chain */
255 cncp = CODA_NC_LRUGET(coda_nc_lru);
257 CODA_NC_LRUREM(cncp); /* remove it from the lists */
259 if (CODA_NC_VALID(cncp)) {
260 /* Seems really ugly, but we have to decrement the appropriate
261 hash bucket length here, so we have to find the hash bucket
263 coda_nc_hash[CODA_NC_HASH(cncp->name, cncp->namelen, cncp->dcp)].length--;
265 coda_nc_stat.lru_rm++; /* zapped a valid entry */
266 CODA_NC_HSHREM(cncp);
267 vrele(CTOV(cncp->dcp));
268 vrele(CTOV(cncp->cp));
273 * Put a hold on the current vnodes and fill in the cache entry.
279 cncp->namelen = namelen;
280 cncp->cred = crhold(cred);
282 bcopy(name, cncp->name, (unsigned)namelen);
284 /* Insert into the lru and hash chains. */
286 CODA_NC_LRUINS(cncp, &coda_nc_lru);
287 CODA_NC_HSHINS(cncp, &coda_nc_hash[hash]);
288 coda_nc_hash[hash].length++; /* Used for tuning */
290 CODA_NC_DEBUG(CODA_NC_PRINTCODA_NC, print_coda_nc(); )
294 * Find the (dir cnode, name) pair in the cache, if it's cred
295 * matches the input, return it, otherwise return 0
298 coda_nc_lookup(dcp, name, namelen, cred)
305 struct coda_cache *cncp;
307 if (coda_nc_use == 0) /* Cache is off */
308 return((struct cnode *) 0);
310 if (namelen > CODA_NC_NAMELEN) {
311 CODA_NC_DEBUG(CODA_NC_LOOKUP,
312 myprintf(("long name lookup %s\n",name));)
313 coda_nc_stat.long_name_lookups++; /* record stats */
314 return((struct cnode *) 0);
317 /* Use the hash function to locate the starting point,
318 then the search routine to go down the list looking for
322 hash = CODA_NC_HASH(name, namelen, dcp);
323 cncp = coda_nc_find(dcp, name, namelen, cred, hash);
324 if (cncp == (struct coda_cache *) 0) {
325 coda_nc_stat.misses++; /* record miss */
326 return((struct cnode *) 0);
331 /* put this entry at the end of the LRU */
332 CODA_NC_LRUREM(cncp);
333 CODA_NC_LRUINS(cncp, &coda_nc_lru);
335 /* move it to the front of the hash chain */
336 /* don't need to change the hash bucket length */
337 CODA_NC_HSHREM(cncp);
338 CODA_NC_HSHINS(cncp, &coda_nc_hash[hash]);
340 CODA_NC_DEBUG(CODA_NC_LOOKUP,
341 printf("lookup: dcp %p, name %s, cred %p = cp %p\n",
342 dcp, name, cred, cncp->cp); )
348 coda_nc_remove(cncp, dcstat)
349 struct coda_cache *cncp;
350 enum dc_status dcstat;
353 * remove an entry -- vrele(cncp->dcp, cp), crfree(cred),
354 * remove it from it's hash chain, and
355 * place it at the head of the lru list.
357 CODA_NC_DEBUG(CODA_NC_REMOVE,
358 myprintf(("coda_nc_remove %s from parent %lx.%lx.%lx\n",
359 cncp->name, (cncp->dcp)->c_fid.Volume,
360 (cncp->dcp)->c_fid.Vnode, (cncp->dcp)->c_fid.Unique));)
362 CODA_NC_HSHREM(cncp);
364 CODA_NC_HSHNUL(cncp); /* have it be a null chain */
365 if ((dcstat == IS_DOWNCALL) && (vrefcnt(CTOV(cncp->dcp)) == 1)) {
366 cncp->dcp->c_flags |= C_PURGING;
368 vrele(CTOV(cncp->dcp));
370 if ((dcstat == IS_DOWNCALL) && (vrefcnt(CTOV(cncp->cp)) == 1)) {
371 cncp->cp->c_flags |= C_PURGING;
373 vrele(CTOV(cncp->cp));
376 bzero(DATA_PART(cncp),DATA_SIZE);
378 /* Put the null entry just after the least-recently-used entry */
379 /* LRU_TOP adjusts the pointer to point to the top of the structure. */
380 CODA_NC_LRUREM(cncp);
381 CODA_NC_LRUINS(cncp, LRU_TOP(coda_nc_lru.lru_prev));
385 * Remove all entries with a parent which has the input fid.
388 coda_nc_zapParentfid(fid, dcstat)
390 enum dc_status dcstat;
392 /* To get to a specific fid, we might either have another hashing
393 function or do a sequential search through the cache for the
394 appropriate entries. The later may be acceptable since I don't
395 think callbacks or whatever Case 1 covers are frequent occurences.
397 struct coda_cache *cncp, *ncncp;
400 if (coda_nc_use == 0) /* Cache is off */
403 CODA_NC_DEBUG(CODA_NC_ZAPPFID,
404 myprintf(("ZapParent: fid 0x%lx, 0x%lx, 0x%lx \n",
405 fid->Volume, fid->Vnode, fid->Unique)); )
407 coda_nc_stat.zapPfids++;
409 for (i = 0; i < coda_nc_hashsize; i++) {
412 * Need to save the hash_next pointer in case we remove the
413 * entry. remove causes hash_next to point to itself.
416 for (cncp = coda_nc_hash[i].hash_next;
417 cncp != (struct coda_cache *)&coda_nc_hash[i];
419 ncncp = cncp->hash_next;
420 if ((cncp->dcp->c_fid.Volume == fid->Volume) &&
421 (cncp->dcp->c_fid.Vnode == fid->Vnode) &&
422 (cncp->dcp->c_fid.Unique == fid->Unique)) {
423 coda_nc_hash[i].length--; /* Used for tuning */
424 coda_nc_remove(cncp, dcstat);
432 * Remove all entries which have the same fid as the input
435 coda_nc_zapfid(fid, dcstat)
437 enum dc_status dcstat;
439 /* See comment for zapParentfid. This routine will be used
440 if attributes are being cached.
442 struct coda_cache *cncp, *ncncp;
445 if (coda_nc_use == 0) /* Cache is off */
448 CODA_NC_DEBUG(CODA_NC_ZAPFID,
449 myprintf(("Zapfid: fid 0x%lx, 0x%lx, 0x%lx \n",
450 fid->Volume, fid->Vnode, fid->Unique)); )
452 coda_nc_stat.zapFids++;
454 for (i = 0; i < coda_nc_hashsize; i++) {
455 for (cncp = coda_nc_hash[i].hash_next;
456 cncp != (struct coda_cache *)&coda_nc_hash[i];
458 ncncp = cncp->hash_next;
459 if ((cncp->cp->c_fid.Volume == fid->Volume) &&
460 (cncp->cp->c_fid.Vnode == fid->Vnode) &&
461 (cncp->cp->c_fid.Unique == fid->Unique)) {
462 coda_nc_hash[i].length--; /* Used for tuning */
463 coda_nc_remove(cncp, dcstat);
470 * Remove all entries which match the fid and the cred
473 coda_nc_zapvnode(fid, cred, dcstat)
476 enum dc_status dcstat;
478 /* See comment for zapfid. I don't think that one would ever
479 want to zap a file with a specific cred from the kernel.
480 We'll leave this one unimplemented.
482 if (coda_nc_use == 0) /* Cache is off */
485 CODA_NC_DEBUG(CODA_NC_ZAPVNODE,
486 myprintf(("Zapvnode: fid 0x%lx, 0x%lx, 0x%lx cred %p\n",
487 fid->Volume, fid->Vnode, fid->Unique, cred)); )
492 * Remove all entries which have the (dir vnode, name) pair
495 coda_nc_zapfile(dcp, name, namelen)
500 /* use the hash function to locate the file, then zap all
501 entries of it regardless of the cred.
503 struct coda_cache *cncp;
506 if (coda_nc_use == 0) /* Cache is off */
509 CODA_NC_DEBUG(CODA_NC_ZAPFILE,
510 myprintf(("Zapfile: dcp %p name %s \n",
513 if (namelen > CODA_NC_NAMELEN) {
514 coda_nc_stat.long_remove++; /* record stats */
518 coda_nc_stat.zapFile++;
520 hash = CODA_NC_HASH(name, namelen, dcp);
521 cncp = coda_nc_find(dcp, name, namelen, 0, hash);
524 coda_nc_hash[hash].length--; /* Used for tuning */
526 coda_nc_remove(cncp, NOT_DOWNCALL);
527 cncp = coda_nc_find(dcp, name, namelen, 0, hash);
532 * Remove all the entries for a particular user. Used when tokens expire.
533 * A user is determined by his/her effective user id (id_uid).
536 coda_nc_purge_user(uid, dcstat)
538 enum dc_status dcstat;
541 * I think the best approach is to go through the entire cache
542 * via HASH or whatever and zap all entries which match the
543 * input cred. Or just flush the whole cache. It might be
544 * best to go through on basis of LRU since cache will almost
545 * always be full and LRU is more straightforward.
548 struct coda_cache *cncp, *ncncp;
551 if (coda_nc_use == 0) /* Cache is off */
554 CODA_NC_DEBUG(CODA_NC_PURGEUSER,
555 myprintf(("ZapDude: uid %x\n", uid)); )
556 coda_nc_stat.zapUsers++;
558 for (cncp = CODA_NC_LRUGET(coda_nc_lru);
559 cncp != (struct coda_cache *)(&coda_nc_lru);
561 ncncp = CODA_NC_LRUGET(*cncp);
563 if ((CODA_NC_VALID(cncp)) &&
564 ((cncp->cred)->cr_uid == uid)) {
565 /* Seems really ugly, but we have to decrement the appropriate
566 hash bucket length here, so we have to find the hash bucket
568 hash = CODA_NC_HASH(cncp->name, cncp->namelen, cncp->dcp);
569 coda_nc_hash[hash].length--; /* For performance tuning */
571 coda_nc_remove(cncp, dcstat);
577 * Flush the entire name cache. In response to a flush of the Venus cache.
580 coda_nc_flush(dcstat)
581 enum dc_status dcstat;
583 /* One option is to deallocate the current name cache and
584 call init to start again. Or just deallocate, then rebuild.
585 Or again, we could just go through the array and zero the
590 * Go through the whole lru chain and kill everything as we go.
591 * I don't use remove since that would rebuild the lru chain
592 * as it went and that seemed unneccesary.
594 struct coda_cache *cncp;
597 if (coda_nc_use == 0) /* Cache is off */
600 coda_nc_stat.Flushes++;
602 for (cncp = CODA_NC_LRUGET(coda_nc_lru);
603 cncp != (struct coda_cache *)&coda_nc_lru;
604 cncp = CODA_NC_LRUGET(*cncp)) {
605 if (CODA_NC_VALID(cncp)) {
607 CODA_NC_HSHREM(cncp); /* only zero valid nodes */
608 CODA_NC_HSHNUL(cncp);
609 if ((dcstat == IS_DOWNCALL)
610 && (vrefcnt(CTOV(cncp->dcp)) == 1))
612 cncp->dcp->c_flags |= C_PURGING;
614 vrele(CTOV(cncp->dcp));
616 ASSERT_VOP_LOCKED(CTOV(cncp->cp), "coda_nc_flush");
617 if (CTOV(cncp->cp)->v_vflag & VV_TEXT) {
618 if (coda_vmflush(cncp->cp))
619 CODADEBUG(CODA_FLUSH,
620 myprintf(("coda_nc_flush: (%lx.%lx.%lx) busy\n", cncp->cp->c_fid.Volume, cncp->cp->c_fid.Vnode, cncp->cp->c_fid.Unique)); )
623 if ((dcstat == IS_DOWNCALL)
624 && (vrefcnt(CTOV(cncp->cp)) == 1))
626 cncp->cp->c_flags |= C_PURGING;
628 vrele(CTOV(cncp->cp));
631 bzero(DATA_PART(cncp),DATA_SIZE);
635 for (i = 0; i < coda_nc_hashsize; i++)
636 coda_nc_hash[i].length = 0;
644 * This routine should print out all the hash chains to the console.
650 struct coda_cache *cncp;
652 for (hash = 0; hash < coda_nc_hashsize; hash++) {
653 myprintf(("\nhash %d\n",hash));
655 for (cncp = coda_nc_hash[hash].hash_next;
656 cncp != (struct coda_cache *)&coda_nc_hash[hash];
657 cncp = cncp->hash_next) {
658 myprintf(("cp %p dcp %p cred %p name %s\n",
660 cncp->cred, cncp->name));
666 coda_nc_gather_stats(void)
668 int i, max = 0, sum = 0, temp, zeros = 0, ave, n;
670 for (i = 0; i < coda_nc_hashsize; i++) {
671 if (coda_nc_hash[i].length) {
672 sum += coda_nc_hash[i].length;
677 if (coda_nc_hash[i].length > max)
678 max = coda_nc_hash[i].length;
682 * When computing the Arithmetic mean, only count slots which
683 * are not empty in the distribution.
685 coda_nc_stat.Sum_bucket_len = sum;
686 coda_nc_stat.Num_zero_len = zeros;
687 coda_nc_stat.Max_bucket_len = max;
689 if ((n = coda_nc_hashsize - zeros) > 0)
695 for (i = 0; i < coda_nc_hashsize; i++) {
696 if (coda_nc_hash[i].length) {
697 temp = coda_nc_hash[i].length - ave;
701 coda_nc_stat.Sum2_bucket_len = sum;
705 * The purpose of this routine is to allow the hash and cache sizes to be
706 * changed dynamically. This should only be used in controlled environments,
707 * it makes no effort to lock other users from accessing the cache while it
708 * is in an improper state (except by turning the cache off).
711 coda_nc_resize(hashsize, heapsize, dcstat)
712 int hashsize, heapsize;
713 enum dc_status dcstat;
715 if ((hashsize % 2) || (heapsize % 2)) { /* Illegal hash or cache sizes */
719 coda_nc_use = 0; /* Turn the cache off */
721 coda_nc_flush(dcstat); /* free any cnodes in the cache */
723 /* WARNING: free must happen *before* size is reset */
724 CODA_FREE(coda_nc_heap,TOTAL_CACHE_SIZE);
725 CODA_FREE(coda_nc_hash,TOTAL_HASH_SIZE);
727 coda_nc_hashsize = hashsize;
728 coda_nc_size = heapsize;
730 coda_nc_init(); /* Set up a cache with the new size */
732 coda_nc_use = 1; /* Turn the cache back on */
737 char coda_nc_name_buf[CODA_MAXNAMLEN+1];
740 coda_nc_name(struct cnode *cp)
742 struct coda_cache *cncp, *ncncp;
745 if (coda_nc_use == 0) /* Cache is off */
748 for (i = 0; i < coda_nc_hashsize; i++) {
749 for (cncp = coda_nc_hash[i].hash_next;
750 cncp != (struct coda_cache *)&coda_nc_hash[i];
752 ncncp = cncp->hash_next;
753 if (cncp->cp == cp) {
754 bcopy(cncp->name, coda_nc_name_buf, cncp->namelen);
755 coda_nc_name_buf[cncp->namelen] = 0;
756 printf(" is %s (%p,%p)@%p",
757 coda_nc_name_buf, cncp->cp, cncp->dcp, cncp);