NetBSD/sys/coda/coda_namecache.c

719 lines
20 KiB
C
Raw Normal View History

/* $NetBSD: coda_namecache.c,v 1.25 2012/08/02 16:06:58 christos Exp $ */
1998-08-30 01:26:45 +04:00
1998-09-08 21:12:46 +04:00
/*
2005-02-27 01:58:54 +03:00
*
1998-09-08 21:12:46 +04:00
* Coda: an Experimental Distributed File System
* Release 3.1
2005-02-27 01:58:54 +03:00
*
1998-09-08 21:12:46 +04:00
* Copyright (c) 1987-1998 Carnegie Mellon University
* All Rights Reserved
2005-02-27 01:58:54 +03:00
*
1998-09-08 21:12:46 +04:00
* Permission to use, copy, modify and distribute this software and its
* documentation is hereby granted, provided that both the copyright
* notice and this permission notice appear in all copies of the
* software, derivative works or modified versions, and any portions
* thereof, and that both notices appear in supporting documentation, and
* that credit is given to Carnegie Mellon University in all documents
* and publicity pertaining to direct or indirect use of this code or its
* derivatives.
2005-02-27 01:58:54 +03:00
*
1998-09-08 21:12:46 +04:00
* CODA IS AN EXPERIMENTAL SOFTWARE SYSTEM AND IS KNOWN TO HAVE BUGS,
* SOME OF WHICH MAY HAVE SERIOUS CONSEQUENCES. CARNEGIE MELLON ALLOWS
* FREE USE OF THIS SOFTWARE IN ITS "AS IS" CONDITION. CARNEGIE MELLON
* DISCLAIMS ANY LIABILITY OF ANY KIND FOR ANY DAMAGES WHATSOEVER
* RESULTING DIRECTLY OR INDIRECTLY FROM THE USE OF THIS SOFTWARE OR OF
* ANY DERIVATIVE WORK.
2005-02-27 01:58:54 +03:00
*
1998-09-08 21:12:46 +04:00
* Carnegie Mellon encourages users of this software to return any
* improvements or extensions that they make, and to grant Carnegie
* Mellon the rights to redistribute these changes without encumbrance.
2005-02-27 01:58:54 +03:00
*
* @(#) coda/coda_namecache.c,v 1.1.1.1 1998/08/29 21:26:45 rvb Exp $
1998-09-08 21:12:46 +04:00
*/
1998-08-30 01:26:45 +04:00
2005-02-27 01:58:54 +03:00
/*
1998-08-30 01:26:45 +04:00
* Mach Operating System
* Copyright (c) 1990 Carnegie-Mellon University
* Copyright (c) 1989 Carnegie-Mellon University
* All rights reserved. The CMU software License Agreement specifies
* the terms and conditions for use and redistribution.
*/
/*
* This code was written for the Coda file system at Carnegie Mellon University.
* Contributers include David Steere, James Kistler, and M. Satyanarayanan.
*/
/*
* This module contains the routines to implement the CODA name cache. The
2005-02-27 01:58:54 +03:00
* purpose of this cache is to reduce the cost of translating pathnames
1998-08-30 01:26:45 +04:00
* into Vice FIDs. Each entry in the cache contains the name of the file,
* the vnode (FID) of the parent directory, and the cred structure of the
* user accessing the file.
*
* The first time a file is accessed, it is looked up by the local Venus
* which first insures that the user has access to the file. In addition
* we are guaranteed that Venus will invalidate any name cache entries in
* case the user no longer should be able to access the file. For these
* reasons we do not need to keep access list information as well as a
* cred structure for each entry.
*
* The table can be accessed through the routines cnc_init(), cnc_enter(),
* cnc_lookup(), cnc_rmfidcred(), cnc_rmfid(), cnc_rmcred(), and cnc_purge().
* There are several other routines which aid in the implementation of the
* hash table.
*/
/*
* NOTES: rvb@cs
* 1. The name cache holds a reference to every vnode in it. Hence files can not be
* closed or made inactive until they are released.
* 2. coda_nc_name(cp) was added to get a name for a cnode pointer for debugging.
* 3. coda_nc_find() has debug code to detect when entries are stored with different
1998-08-30 01:26:45 +04:00
* credentials. We don't understand yet, if/how entries are NOT EQ but still
* EQUAL
* 4. I wonder if this name cache could be replace by the vnode name cache.
* The latter has no zapping functions, so probably not.
*/
2001-11-13 02:08:56 +03:00
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: coda_namecache.c,v 1.25 2012/08/02 16:06:58 christos Exp $");
2001-11-13 02:08:56 +03:00
1998-08-30 01:26:45 +04:00
#include <sys/param.h>
#include <sys/errno.h>
#include <sys/malloc.h>
#include <sys/select.h>
2006-05-15 01:19:33 +04:00
#include <sys/kauth.h>
1998-08-30 01:26:45 +04:00
1998-09-15 06:02:55 +04:00
#include <coda/coda.h>
#include <coda/cnode.h>
#include <coda/coda_namecache.h>
#include <coda/coda_subr.h>
2005-02-27 01:58:54 +03:00
/*
1998-08-30 01:26:45 +04:00
* Declaration of the name cache data structure.
*/
int coda_nc_use = 1; /* Indicate use of CODA Name Cache */
1998-08-30 01:26:45 +04:00
int coda_nc_size = CODA_NC_CACHESIZE; /* size of the cache */
int coda_nc_hashsize = CODA_NC_HASHSIZE; /* size of the primary hash */
1998-08-30 01:26:45 +04:00
struct coda_cache *coda_nc_heap; /* pointer to the cache entries */
struct coda_hash *coda_nc_hash; /* hash table of cfscache pointers */
struct coda_lru coda_nc_lru; /* head of lru chain */
1998-08-30 01:26:45 +04:00
struct coda_nc_statistics coda_nc_stat; /* Keep various stats */
1998-08-30 01:26:45 +04:00
2005-02-27 01:58:54 +03:00
/*
1998-08-30 01:26:45 +04:00
* for testing purposes
*/
int coda_nc_debug = 0;
1998-08-30 01:26:45 +04:00
/*
* Entry points for the CODA Name Cache
1998-08-30 01:26:45 +04:00
*/
static struct coda_cache *
coda_nc_find(struct cnode *dcp, const char *name, int namelen,
2006-05-15 01:19:33 +04:00
kauth_cred_t cred, int hash);
1998-08-30 01:26:45 +04:00
static void
coda_nc_remove(struct coda_cache *cncp, enum dc_status dcstat);
1998-08-30 01:26:45 +04:00
2005-02-27 01:58:54 +03:00
/*
1998-08-30 01:26:45 +04:00
* Initialize the cache, the LRU structure and the Hash structure(s)
*/
#define TOTAL_CACHE_SIZE (sizeof(struct coda_cache) * coda_nc_size)
#define TOTAL_HASH_SIZE (sizeof(struct coda_hash) * coda_nc_hashsize)
1998-08-30 01:26:45 +04:00
int coda_nc_initialized = 0; /* Initially the cache has not been initialized */
1998-08-30 01:26:45 +04:00
void
coda_nc_init(void)
1998-08-30 01:26:45 +04:00
{
int i;
/* zero the statistics structure */
2005-02-27 01:58:54 +03:00
2001-07-18 20:12:31 +04:00
memset(&coda_nc_stat, 0, (sizeof(struct coda_nc_statistics)));
1998-08-30 01:26:45 +04:00
#ifdef CODA_VERBOSE
printf("CODA NAME CACHE: CACHE %d, HASH TBL %d\n", CODA_NC_CACHESIZE, CODA_NC_HASHSIZE);
#endif
CODA_ALLOC(coda_nc_heap, struct coda_cache *, TOTAL_CACHE_SIZE);
CODA_ALLOC(coda_nc_hash, struct coda_hash *, TOTAL_HASH_SIZE);
2005-02-27 01:58:54 +03:00
memset(coda_nc_heap, 0, TOTAL_CACHE_SIZE);
memset(coda_nc_hash, 0, TOTAL_HASH_SIZE);
2005-02-27 01:58:54 +03:00
TAILQ_INIT(&coda_nc_lru.head);
2005-02-27 01:58:54 +03:00
for (i=0; i < coda_nc_size; i++) { /* initialize the heap */
TAILQ_INSERT_HEAD(&coda_nc_lru.head, &coda_nc_heap[i], lru);
1998-08-30 01:26:45 +04:00
}
2005-02-27 01:58:54 +03:00
for (i=0; i < coda_nc_hashsize; i++) { /* initialize the hashtable */
LIST_INIT(&coda_nc_hash[i].head);
1998-08-30 01:26:45 +04:00
}
2005-02-27 01:58:54 +03:00
coda_nc_initialized++;
1998-08-30 01:26:45 +04:00
}
/*
* Auxillary routines -- shouldn't be entry points
*/
static struct coda_cache *
coda_nc_find(struct cnode *dcp, const char *name, int namelen,
2006-05-15 01:19:33 +04:00
kauth_cred_t cred, int hash)
1998-08-30 01:26:45 +04:00
{
2005-02-27 01:58:54 +03:00
/*
1998-08-30 01:26:45 +04:00
* hash to find the appropriate bucket, look through the chain
2005-02-27 01:58:54 +03:00
* for the right entry (especially right cred, unless cred == 0)
1998-08-30 01:26:45 +04:00
*/
struct coda_cache *cncp;
1998-08-30 01:26:45 +04:00
int count = 1;
2005-02-27 01:58:54 +03:00
CODA_NC_DEBUG(CODA_NC_FIND,
myprintf(("coda_nc_find(dcp %p, name %s, len %d, cred %p, hash %d\n",
dcp, name, namelen, cred, hash));)
1998-08-30 01:26:45 +04:00
LIST_FOREACH(cncp, &coda_nc_hash[hash].head, hash)
1998-08-30 01:26:45 +04:00
{
if ((CODA_NAMEMATCH(cncp, name, namelen, dcp)) &&
2005-02-27 01:58:54 +03:00
((cred == 0) || (cncp->cred == cred)))
{
1998-08-30 01:26:45 +04:00
/* compare cr_uid instead */
coda_nc_stat.Search_len += count;
1998-08-30 01:26:45 +04:00
return(cncp);
}
#ifdef DEBUG
else if (CODA_NAMEMATCH(cncp, name, namelen, dcp)) {
printf("coda_nc_find: name %s, new cred = %p, cred = %p\n",
1998-08-30 01:26:45 +04:00
name, cred, cncp->cred);
printf("nref %d, nuid %d, ngid %d // oref %d, ocred %d, ogid %d\n",
2006-05-15 01:19:33 +04:00
kauth_cred_getrefcnt(cred),
kauth_cred_geteuid(cred),
kauth_cred_getegid(cred),
kauth_cred_getrefcnt(cncp->cred),
kauth_cred_geteuid(cncp->cred),
kauth_cred_getegid(cncp->cred));
coda_print_cred(cred);
coda_print_cred(cncp->cred);
1998-08-30 01:26:45 +04:00
}
#endif
count++;
1998-08-30 01:26:45 +04:00
}
return((struct coda_cache *)0);
1998-08-30 01:26:45 +04:00
}
/*
* Enter a new (dir cnode, name) pair into the cache, updating the
* LRU and Hash as needed.
*/
void
coda_nc_enter(struct cnode *dcp, const char *name, int namelen,
2006-05-15 01:19:33 +04:00
kauth_cred_t cred, struct cnode *cp)
1998-08-30 01:26:45 +04:00
{
struct coda_cache *cncp;
1998-08-30 01:26:45 +04:00
int hash;
2005-02-27 01:58:54 +03:00
if (coda_nc_use == 0) /* Cache is off */
1998-08-30 01:26:45 +04:00
return;
2005-02-27 01:58:54 +03:00
CODA_NC_DEBUG(CODA_NC_ENTER,
1998-08-30 01:26:45 +04:00
myprintf(("Enter: dcp %p cp %p name %s cred %p \n",
dcp, cp, name, cred)); )
2005-02-27 01:58:54 +03:00
if (namelen > CODA_NC_NAMELEN) {
2005-02-27 01:58:54 +03:00
CODA_NC_DEBUG(CODA_NC_ENTER,
1998-08-30 01:26:45 +04:00
myprintf(("long name enter %s\n",name));)
coda_nc_stat.long_name_enters++; /* record stats */
1998-08-30 01:26:45 +04:00
return;
}
2005-02-27 01:58:54 +03:00
hash = CODA_NC_HASH(name, namelen, dcp);
cncp = coda_nc_find(dcp, name, namelen, cred, hash);
2005-02-27 01:58:54 +03:00
if (cncp != (struct coda_cache *) 0) {
coda_nc_stat.dbl_enters++; /* duplicate entry */
1998-08-30 01:26:45 +04:00
return;
}
2005-02-27 01:58:54 +03:00
coda_nc_stat.enters++; /* record the enters statistic */
2005-02-27 01:58:54 +03:00
1998-08-30 01:26:45 +04:00
/* Grab the next element in the lru chain */
cncp = TAILQ_FIRST(&coda_nc_lru.head);
TAILQ_REMOVE(&coda_nc_lru.head, cncp, lru);
2005-02-27 01:58:54 +03:00
if (CODA_NC_VALID(cncp)) {
1998-08-30 01:26:45 +04:00
/* Seems really ugly, but we have to decrement the appropriate
hash bucket length here, so we have to find the hash bucket
*/
coda_nc_hash[CODA_NC_HASH(cncp->name, cncp->namelen, cncp->dcp)].length--;
2005-02-27 01:58:54 +03:00
coda_nc_stat.lru_rm++; /* zapped a valid entry */
LIST_REMOVE(cncp, hash);
2005-02-27 01:58:54 +03:00
vrele(CTOV(cncp->dcp));
1998-08-30 01:26:45 +04:00
vrele(CTOV(cncp->cp));
2006-05-15 01:19:33 +04:00
kauth_cred_free(cncp->cred);
1998-08-30 01:26:45 +04:00
}
2005-02-27 01:58:54 +03:00
1998-08-30 01:26:45 +04:00
/*
* Put a hold on the current vnodes and fill in the cache entry.
*/
vref(CTOV(cp));
vref(CTOV(dcp));
2006-05-15 01:19:33 +04:00
kauth_cred_hold(cred);
1998-08-30 01:26:45 +04:00
cncp->dcp = dcp;
cncp->cp = cp;
cncp->namelen = namelen;
cncp->cred = cred;
2005-02-27 01:58:54 +03:00
memcpy(cncp->name, name, (unsigned)namelen);
2005-02-27 01:58:54 +03:00
1998-08-30 01:26:45 +04:00
/* Insert into the lru and hash chains. */
TAILQ_INSERT_TAIL(&coda_nc_lru.head, cncp, lru);
LIST_INSERT_HEAD(&coda_nc_hash[hash].head, cncp, hash);
coda_nc_hash[hash].length++; /* Used for tuning */
2005-02-27 01:58:54 +03:00
CODA_NC_DEBUG(CODA_NC_PRINTCODA_NC, print_coda_nc(); )
1998-08-30 01:26:45 +04:00
}
/*
* Find the (dir cnode, name) pair in the cache, if it's cred
* matches the input, return it, otherwise return 0
*/
struct cnode *
coda_nc_lookup(struct cnode *dcp, const char *name, int namelen,
2006-05-15 01:19:33 +04:00
kauth_cred_t cred)
1998-08-30 01:26:45 +04:00
{
int hash;
struct coda_cache *cncp;
1998-08-30 01:26:45 +04:00
if (coda_nc_use == 0) /* Cache is off */
1998-08-30 01:26:45 +04:00
return((struct cnode *) 0);
if (namelen > CODA_NC_NAMELEN) {
2005-02-27 01:58:54 +03:00
CODA_NC_DEBUG(CODA_NC_LOOKUP,
1998-08-30 01:26:45 +04:00
myprintf(("long name lookup %s\n",name));)
coda_nc_stat.long_name_lookups++; /* record stats */
1998-08-30 01:26:45 +04:00
return((struct cnode *) 0);
}
/* Use the hash function to locate the starting point,
then the search routine to go down the list looking for
the correct cred.
*/
hash = CODA_NC_HASH(name, namelen, dcp);
cncp = coda_nc_find(dcp, name, namelen, cred, hash);
if (cncp == (struct coda_cache *) 0) {
coda_nc_stat.misses++; /* record miss */
1998-08-30 01:26:45 +04:00
return((struct cnode *) 0);
}
coda_nc_stat.hits++;
1998-08-30 01:26:45 +04:00
/* put this entry at the end of the LRU */
TAILQ_REMOVE(&coda_nc_lru.head, cncp, lru);
TAILQ_INSERT_TAIL(&coda_nc_lru.head, cncp, lru);
1998-08-30 01:26:45 +04:00
/* move it to the front of the hash chain */
/* don't need to change the hash bucket length */
LIST_REMOVE(cncp, hash);
LIST_INSERT_HEAD(&coda_nc_hash[hash].head, cncp, hash);
1998-08-30 01:26:45 +04:00
2005-02-27 01:58:54 +03:00
CODA_NC_DEBUG(CODA_NC_LOOKUP,
1998-08-30 01:26:45 +04:00
printf("lookup: dcp %p, name %s, cred %p = cp %p\n",
dcp, name, cred, cncp->cp); )
return(cncp->cp);
}
static void
coda_nc_remove(struct coda_cache *cncp, enum dc_status dcstat)
1998-08-30 01:26:45 +04:00
{
2005-02-27 01:58:54 +03:00
/*
1998-08-30 01:26:45 +04:00
* remove an entry -- vrele(cncp->dcp, cp), crfree(cred),
* remove it from it's hash chain, and
* place it at the head of the lru list.
*/
CODA_NC_DEBUG(CODA_NC_REMOVE,
myprintf(("coda_nc_remove %s from parent %s\n",
cncp->name, coda_f2s(&cncp->dcp->c_fid))); )
2005-02-27 01:58:54 +03:00
1998-08-30 01:26:45 +04:00
LIST_REMOVE(cncp, hash);
memset(&cncp->hash, 0, sizeof(cncp->hash));
1998-08-30 01:26:45 +04:00
if ((dcstat == IS_DOWNCALL) && (CTOV(cncp->dcp)->v_usecount == 1)) {
cncp->dcp->c_flags |= C_PURGING;
}
2005-02-27 01:58:54 +03:00
vrele(CTOV(cncp->dcp));
1998-08-30 01:26:45 +04:00
if ((dcstat == IS_DOWNCALL) && (CTOV(cncp->cp)->v_usecount == 1)) {
cncp->cp->c_flags |= C_PURGING;
}
2005-02-27 01:58:54 +03:00
vrele(CTOV(cncp->cp));
1998-08-30 01:26:45 +04:00
2006-05-15 01:19:33 +04:00
kauth_cred_free(cncp->cred);
2001-07-18 20:12:31 +04:00
memset(DATA_PART(cncp), 0, DATA_SIZE);
1998-08-30 01:26:45 +04:00
/* move the null entry to the front for reuse */
TAILQ_REMOVE(&coda_nc_lru.head, cncp, lru);
TAILQ_INSERT_HEAD(&coda_nc_lru.head, cncp, lru);
1998-08-30 01:26:45 +04:00
}
/*
* Remove all entries with a parent which has the input fid.
*/
void
coda_nc_zapParentfid(CodaFid *fid, enum dc_status dcstat)
1998-08-30 01:26:45 +04:00
{
/* To get to a specific fid, we might either have another hashing
function or do a sequential search through the cache for the
appropriate entries. The later may be acceptable since I don't
think callbacks or whatever Case 1 covers are frequent occurrences.
1998-08-30 01:26:45 +04:00
*/
struct coda_cache *cncp, *ncncp;
1998-08-30 01:26:45 +04:00
int i;
if (coda_nc_use == 0) /* Cache is off */
1998-08-30 01:26:45 +04:00
return;
2005-02-27 01:58:54 +03:00
CODA_NC_DEBUG(CODA_NC_ZAPPFID,
myprintf(("ZapParent: fid %s\n", coda_f2s(fid))); )
1998-08-30 01:26:45 +04:00
coda_nc_stat.zapPfids++;
1998-08-30 01:26:45 +04:00
for (i = 0; i < coda_nc_hashsize; i++) {
1998-08-30 01:26:45 +04:00
/*
* Need to save the hash_next pointer in case we remove the
* entry. remove causes hash_next to point to itself.
*/
ncncp = LIST_FIRST(&coda_nc_hash[i].head);
while ((cncp = ncncp) != NULL) {
ncncp = LIST_NEXT(cncp, hash);
if (coda_fid_eq(&(cncp->dcp->c_fid), fid)) {
coda_nc_hash[i].length--; /* Used for tuning */
2005-02-27 01:58:54 +03:00
coda_nc_remove(cncp, dcstat);
1998-08-30 01:26:45 +04:00
}
}
}
}
/*
* Remove all entries which have the same fid as the input
*/
void
coda_nc_zapfid(CodaFid *fid, enum dc_status dcstat)
1998-08-30 01:26:45 +04:00
{
/* See comment for zapParentfid. This routine will be used
2005-02-27 01:58:54 +03:00
if attributes are being cached.
1998-08-30 01:26:45 +04:00
*/
struct coda_cache *cncp, *ncncp;
1998-08-30 01:26:45 +04:00
int i;
if (coda_nc_use == 0) /* Cache is off */
1998-08-30 01:26:45 +04:00
return;
2005-02-27 01:58:54 +03:00
CODA_NC_DEBUG(CODA_NC_ZAPFID,
myprintf(("Zapfid: fid %s\n", coda_f2s(fid))); )
1998-08-30 01:26:45 +04:00
coda_nc_stat.zapFids++;
1998-08-30 01:26:45 +04:00
for (i = 0; i < coda_nc_hashsize; i++) {
ncncp = LIST_FIRST(&coda_nc_hash[i].head);
while ((cncp = ncncp) != NULL) {
ncncp = LIST_NEXT(cncp, hash);
if (coda_fid_eq(&cncp->cp->c_fid, fid)) {
coda_nc_hash[i].length--; /* Used for tuning */
2005-02-27 01:58:54 +03:00
coda_nc_remove(cncp, dcstat);
1998-08-30 01:26:45 +04:00
}
}
}
}
2005-02-27 01:58:54 +03:00
/*
1998-08-30 01:26:45 +04:00
* Remove all entries which match the fid and the cred
*/
void
coda_nc_zapvnode(CodaFid *fid, kauth_cred_t cred,
enum dc_status dcstat)
1998-08-30 01:26:45 +04:00
{
/* See comment for zapfid. I don't think that one would ever
want to zap a file with a specific cred from the kernel.
We'll leave this one unimplemented.
*/
if (coda_nc_use == 0) /* Cache is off */
1998-08-30 01:26:45 +04:00
return;
2005-02-27 01:58:54 +03:00
CODA_NC_DEBUG(CODA_NC_ZAPVNODE,
myprintf(("Zapvnode: fid %s cred %p\n",
coda_f2s(fid), cred)); )
1998-08-30 01:26:45 +04:00
}
/*
* Remove all entries which have the (dir vnode, name) pair
*/
void
coda_nc_zapfile(struct cnode *dcp, const char *name, int namelen)
1998-08-30 01:26:45 +04:00
{
/* use the hash function to locate the file, then zap all
entries of it regardless of the cred.
*/
struct coda_cache *cncp;
1998-08-30 01:26:45 +04:00
int hash;
if (coda_nc_use == 0) /* Cache is off */
1998-08-30 01:26:45 +04:00
return;
2005-02-27 01:58:54 +03:00
CODA_NC_DEBUG(CODA_NC_ZAPFILE,
1998-08-30 01:26:45 +04:00
myprintf(("Zapfile: dcp %p name %s \n",
dcp, name)); )
if (namelen > CODA_NC_NAMELEN) {
coda_nc_stat.long_remove++; /* record stats */
1998-08-30 01:26:45 +04:00
return;
}
coda_nc_stat.zapFile++;
1998-08-30 01:26:45 +04:00
hash = CODA_NC_HASH(name, namelen, dcp);
cncp = coda_nc_find(dcp, name, namelen, 0, hash);
1998-08-30 01:26:45 +04:00
while (cncp) {
coda_nc_hash[hash].length--; /* Used for tuning */
1998-08-30 01:26:45 +04:00
/* 1.3 */
coda_nc_remove(cncp, NOT_DOWNCALL);
cncp = coda_nc_find(dcp, name, namelen, 0, hash);
1998-08-30 01:26:45 +04:00
}
}
2005-02-27 01:58:54 +03:00
/*
1998-08-30 01:26:45 +04:00
* Remove all the entries for a particular user. Used when tokens expire.
* A user is determined by his/her effective user id (id_uid).
*/
void
coda_nc_purge_user(uid_t uid, enum dc_status dcstat)
1998-08-30 01:26:45 +04:00
{
2005-02-27 01:58:54 +03:00
/*
1998-08-30 01:26:45 +04:00
* I think the best approach is to go through the entire cache
* via HASH or whatever and zap all entries which match the
* input cred. Or just flush the whole cache. It might be
* best to go through on basis of LRU since cache will almost
2005-02-27 01:58:54 +03:00
* always be full and LRU is more straightforward.
1998-08-30 01:26:45 +04:00
*/
struct coda_cache *cncp, *ncncp;
1998-08-30 01:26:45 +04:00
int hash;
if (coda_nc_use == 0) /* Cache is off */
1998-08-30 01:26:45 +04:00
return;
2005-02-27 01:58:54 +03:00
CODA_NC_DEBUG(CODA_NC_PURGEUSER,
myprintf(("ZapDude: uid %x\n", uid)); )
coda_nc_stat.zapUsers++;
1998-08-30 01:26:45 +04:00
ncncp = TAILQ_FIRST(&coda_nc_lru.head);
while ((cncp = ncncp) != NULL) {
ncncp = TAILQ_NEXT(cncp, lru);
1998-08-30 01:26:45 +04:00
if ((CODA_NC_VALID(cncp)) &&
2006-05-15 01:19:33 +04:00
(kauth_cred_geteuid(cncp->cred) == uid)) {
1998-08-30 01:26:45 +04:00
/* Seems really ugly, but we have to decrement the appropriate
hash bucket length here, so we have to find the hash bucket
*/
hash = CODA_NC_HASH(cncp->name, cncp->namelen, cncp->dcp);
coda_nc_hash[hash].length--; /* For performance tuning */
1998-08-30 01:26:45 +04:00
2005-02-27 01:58:54 +03:00
coda_nc_remove(cncp, dcstat);
1998-08-30 01:26:45 +04:00
}
}
}
/*
* Flush the entire name cache. In response to a flush of the Venus cache.
*/
void
coda_nc_flush(enum dc_status dcstat)
1998-08-30 01:26:45 +04:00
{
/* One option is to deallocate the current name cache and
call init to start again. Or just deallocate, then rebuild.
2005-02-27 01:58:54 +03:00
Or again, we could just go through the array and zero the
appropriate fields.
1998-08-30 01:26:45 +04:00
*/
2005-02-27 01:58:54 +03:00
/*
1998-08-30 01:26:45 +04:00
* Go through the whole lru chain and kill everything as we go.
* I don't use remove since that would rebuild the lru chain
* as it went and that seemed unneccesary.
*/
struct coda_cache *cncp;
1998-08-30 01:26:45 +04:00
int i;
if (coda_nc_use == 0) /* Cache is off */
1998-08-30 01:26:45 +04:00
return;
coda_nc_stat.Flushes++;
1998-08-30 01:26:45 +04:00
TAILQ_FOREACH(cncp, &coda_nc_lru.head, lru) {
if (CODA_NC_VALID(cncp)) { /* only zero valid nodes */
LIST_REMOVE(cncp, hash);
memset(&cncp->hash, 0, sizeof(cncp->hash));
1998-08-30 01:26:45 +04:00
2005-02-27 01:58:54 +03:00
if ((dcstat == IS_DOWNCALL)
1998-08-30 01:26:45 +04:00
&& (CTOV(cncp->dcp)->v_usecount == 1))
{
cncp->dcp->c_flags |= C_PURGING;
}
2005-02-27 01:58:54 +03:00
vrele(CTOV(cncp->dcp));
1998-08-30 01:26:45 +04:00
if (CTOV(cncp->cp)->v_iflag & VI_TEXT) {
if (coda_vmflush(cncp->cp))
2005-02-27 01:58:54 +03:00
CODADEBUG(CODA_FLUSH,
myprintf(("coda_nc_flush: %s busy\n",
coda_f2s(&cncp->cp->c_fid))); )
1998-08-30 01:26:45 +04:00
}
2005-02-27 01:58:54 +03:00
if ((dcstat == IS_DOWNCALL)
1998-08-30 01:26:45 +04:00
&& (CTOV(cncp->cp)->v_usecount == 1))
{
cncp->cp->c_flags |= C_PURGING;
}
2005-02-27 01:58:54 +03:00
vrele(CTOV(cncp->cp));
1998-08-30 01:26:45 +04:00
2006-05-15 01:19:33 +04:00
kauth_cred_free(cncp->cred);
2001-07-18 20:12:31 +04:00
memset(DATA_PART(cncp), 0, DATA_SIZE);
1998-08-30 01:26:45 +04:00
}
}
for (i = 0; i < coda_nc_hashsize; i++)
coda_nc_hash[i].length = 0;
1998-08-30 01:26:45 +04:00
}
/*
* Debugging routines
*/
2005-02-27 01:58:54 +03:00
/*
1998-08-30 01:26:45 +04:00
* This routine should print out all the hash chains to the console.
*/
void
print_coda_nc(void)
1998-08-30 01:26:45 +04:00
{
int hash;
struct coda_cache *cncp;
1998-08-30 01:26:45 +04:00
for (hash = 0; hash < coda_nc_hashsize; hash++) {
1998-08-30 01:26:45 +04:00
myprintf(("\nhash %d\n",hash));
LIST_FOREACH(cncp, &coda_nc_hash[hash].head, hash) {
1998-08-30 01:26:45 +04:00
myprintf(("cp %p dcp %p cred %p name %s\n",
cncp->cp, cncp->dcp,
cncp->cred, cncp->name));
}
}
}
void
coda_nc_gather_stats(void)
1998-08-30 01:26:45 +04:00
{
int i, xmax = 0, sum = 0, temp, zeros = 0, ave, n;
1998-08-30 01:26:45 +04:00
for (i = 0; i < coda_nc_hashsize; i++) {
if (coda_nc_hash[i].length) {
sum += coda_nc_hash[i].length;
1998-08-30 01:26:45 +04:00
} else {
zeros++;
}
if (coda_nc_hash[i].length > xmax)
xmax = coda_nc_hash[i].length;
1998-08-30 01:26:45 +04:00
}
/*
2005-02-27 01:58:54 +03:00
* When computing the Arithmetic mean, only count slots which
1998-08-30 01:26:45 +04:00
* are not empty in the distribution.
*/
coda_nc_stat.Sum_bucket_len = sum;
coda_nc_stat.Num_zero_len = zeros;
coda_nc_stat.Max_bucket_len = xmax;
1998-08-30 01:26:45 +04:00
2005-02-27 01:58:54 +03:00
if ((n = coda_nc_hashsize - zeros) > 0)
1998-08-30 01:26:45 +04:00
ave = sum / n;
else
ave = 0;
sum = 0;
for (i = 0; i < coda_nc_hashsize; i++) {
if (coda_nc_hash[i].length) {
temp = coda_nc_hash[i].length - ave;
1998-08-30 01:26:45 +04:00
sum += temp * temp;
}
}
coda_nc_stat.Sum2_bucket_len = sum;
1998-08-30 01:26:45 +04:00
}
/*
* The purpose of this routine is to allow the hash and cache sizes to be
* changed dynamically. This should only be used in controlled environments,
* it makes no effort to lock other users from accessing the cache while it
* is in an improper state (except by turning the cache off).
*/
int
coda_nc_resize(int hashsize, int heapsize, enum dc_status dcstat)
1998-08-30 01:26:45 +04:00
{
if ((hashsize % 2) || (heapsize % 2)) { /* Illegal hash or cache sizes */
return(EINVAL);
2005-02-27 01:58:54 +03:00
}
coda_nc_use = 0; /* Turn the cache off */
2005-02-27 01:58:54 +03:00
coda_nc_flush(dcstat); /* free any cnodes in the cache */
2005-02-27 01:58:54 +03:00
1998-08-30 01:26:45 +04:00
/* WARNING: free must happen *before* size is reset */
CODA_FREE(coda_nc_heap,TOTAL_CACHE_SIZE);
CODA_FREE(coda_nc_hash,TOTAL_HASH_SIZE);
2005-02-27 01:58:54 +03:00
coda_nc_hashsize = hashsize;
coda_nc_size = heapsize;
2005-02-27 01:58:54 +03:00
coda_nc_init(); /* Set up a cache with the new size */
2005-02-27 01:58:54 +03:00
coda_nc_use = 1; /* Turn the cache back on */
1998-08-30 01:26:45 +04:00
return(0);
}
char coda_nc_name_buf[CODA_MAXNAMLEN+1];
1998-08-30 01:26:45 +04:00
void
coda_nc_name(struct cnode *cp)
1998-08-30 01:26:45 +04:00
{
struct coda_cache *cncp;
1998-08-30 01:26:45 +04:00
int i;
if (coda_nc_use == 0) /* Cache is off */
1998-08-30 01:26:45 +04:00
return;
for (i = 0; i < coda_nc_hashsize; i++) {
LIST_FOREACH(cncp, &coda_nc_hash[i].head, hash) {
1998-08-30 01:26:45 +04:00
if (cncp->cp == cp) {
memcpy(coda_nc_name_buf, cncp->name, cncp->namelen);
coda_nc_name_buf[cncp->namelen] = 0;
1998-08-30 01:26:45 +04:00
printf(" is %s (%p,%p)@%p",
coda_nc_name_buf, cncp->cp, cncp->dcp, cncp);
1998-08-30 01:26:45 +04:00
}
}
}
}