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