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