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