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