diff options
author | Ralf Baechle <ralf@linux-mips.org> | 1997-12-16 06:06:25 +0000 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 1997-12-16 06:06:25 +0000 |
commit | aa944aa3453e47706685bc562711a9e87375941e (patch) | |
tree | 8fb37a65f205a90412917ca2b91c429263ef1790 /fs/coda/namecache.c | |
parent | 967c65a99059fd459b956c1588ce0ba227912c4e (diff) |
Merge with Linux 2.1.72, part 2.
The new signal code with exception of the code for the rt signals.
The definitions in <asm/siginfo.h> and <asm/ucontext.h> are currently
just stolen from the Alpha and will need to be overhauled.
Diffstat (limited to 'fs/coda/namecache.c')
-rw-r--r-- | fs/coda/namecache.c | 832 |
1 files changed, 832 insertions, 0 deletions
diff --git a/fs/coda/namecache.c b/fs/coda/namecache.c new file mode 100644 index 000000000..08f1ee9e7 --- /dev/null +++ b/fs/coda/namecache.c @@ -0,0 +1,832 @@ +/* + * Cache operations for Coda. + * Original version: (C) 1996 Peter Braam + * Rewritten for Linux 2.1: (C) 1997 Carnegie Mellon University + * + * Carnegie Mellon encourages users of this code to contribute improvements + * to the Coda project. Contact Peter Braam <coda@cs.cmu.edu>. + */ + +/* + * This module contains the routines to implement the CFS name cache. The + * purpose of this cache is to reduce the cost of translating pathnames + * 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. + */ + +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/fs.h> +#include <linux/stat.h> +#include <linux/errno.h> +#include <linux/locks.h> +#include <asm/segment.h> +#include <linux/string.h> + +#include <linux/coda.h> +#include <linux/coda_linux.h> +#include <linux/coda_cnode.h> +#include <linux/coda_namecache.h> + +int cfsnc_use; + +static struct cfscache * cfsnc_find(struct cnode *dcp, const char * name, int namelen, int hash); +static void cfsnc_remove(struct cfscache *cncp); +static inline int nchash(const char *, int, struct cnode *); +static inline int ncmatch(struct cfscache *, const char *, int, + struct cnode *); +static inline void hashins(struct cfscache *a, struct cfscache *pred); +static inline void hashrem(struct cfscache *a); +static inline void hashnull(struct cfscache *); +static inline void lrurem(struct cfscache *a); +static inline void lruins(struct cfscache *a, struct cfscache *pred); +static void cfsnc_gather_stats(void); + + +/* externals */ +extern int coda_fideq(ViceFid *fid1, ViceFid *fid2); +extern int coda_debug; +extern int coda_print_entry; +extern struct super_block *coda_super_block; + + + +/* + * Declaration of the name cache data structure. + */ + +int cfsnc_use = 0; /* Indicate use of CFS Name Cache */ +int cfsnc_size = CFSNC_CACHESIZE; /* size of the cache */ +int cfsnc_hashsize = CFSNC_HASHSIZE; /* size of the primary hash */ +int cfsnc_flushme = 0; +int cfsnc_procsize = 0; +static int cfsnc_force = 0; + +struct cfshash { + struct cfscache *hash_next, *hash_prev; + int length; +}; + +struct cfslruhead { + struct cfscache *dummy1, *dummy2; + struct cfscache *lru_next, *lru_prev; +}; + +struct cfscache *cfsncheap; /* pointer to the cache entries */ +struct cfshash *cfsnchash; /* hash table of cfscache pointers */ +struct cfslruhead cfsnc_lru; /* head of lru chain; prev = lru */ + +struct cfsnc_statistics cfsnc_stat; /* Keep various stats */ + +#define TOTAL_CACHE_SIZE (sizeof(struct cfscache) * cfsnc_size) +#define TOTAL_HASH_SIZE (sizeof(struct cfshash) * cfsnc_hashsize) +int cfsnc_initialized = 0; /* Initially the cache has not been initialized */ + +/* + * for testing purposes + */ +int cfsnc_debug = 1; + + +/* + * Auxillary routines -- shouldn't be entry points + */ + + +/* + * Hash function for the primary hash. + * First try -- (first + last letters + length + (int)cp) mod size + * 2nd try -- same, except dir fid.vnode instead of cp + */ +static inline int +nchash(const char *name, int namelen, struct cnode *cp) +{ + return ((name[0] + name[namelen-1] + + namelen + (int)(cp)) & (cfsnc_hashsize-1)); +} + +/* matching function */ +static inline int ncmatch(struct cfscache *cp, const char *name, int namelen, + struct cnode *dcp) +{ + return ((namelen == cp->namelen) && (dcp == cp->dcp) && + (memcmp(cp->name,name,namelen) == 0)); +} + +/* insert a behind pred */ +static inline void hashins(struct cfscache *a, struct cfscache *pred) +{ + a->hash_next = pred->hash_next; + pred->hash_next->hash_prev= a; + pred->hash_next = a; + a->hash_prev = pred; +} + +static inline void hashrem(struct cfscache *a) +{ + a->hash_prev->hash_next = a->hash_next; + a->hash_next->hash_prev = a->hash_prev; +} + +static inline void hashnull(struct cfscache *elem) { + elem->hash_next = elem; + elem->hash_prev = elem; +} + +static inline void lrurem(struct cfscache *a) +{ + a->lru_prev->lru_next = a->lru_next; + a->lru_next->lru_prev = a->lru_prev; +} + +static inline void lruins(struct cfscache *a, struct cfscache *pred) +{ + pred->lru_next->lru_prev= a; + a->lru_next = pred->lru_next; + + a->lru_prev = pred; + pred->lru_next = a; +} + +static struct cfscache * +cfsnc_find(struct cnode *dcp, const char * name, int namelen, int hash) +{ + /* + * hash to find the appropriate bucket, look through the chain + * for the right entry + */ + register struct cfscache *cncp; + int count = 1; + + CDEBUG(D_CACHE, "dcp 0x%x, name %s, len %d, hash %d\n", + (int)dcp, name, namelen, hash); + + for (cncp = cfsnchash[hash].hash_next; + cncp != (struct cfscache *)&cfsnchash[hash]; + cncp = cncp->hash_next, count++) + { + + if (ncmatch(cncp, name, namelen, dcp)) + { + cfsnc_stat.Search_len += count; + CDEBUG(D_CACHE, "dcp 0x%x,found.\n", (int) dcp); + return(cncp); + + } + } + CDEBUG(D_CACHE, "dcp 0x%x,not found.\n", (int) dcp); + return((struct cfscache *)0); +} + +static void +cfsnc_remove(struct cfscache *cncp) +{ + /* + * remove an entry -- VN_RELE(cncp->dcp, cp), crfree(cred), + * remove it from it's hash chain, and + * place it at the head of the lru list. + */ + CDEBUG(D_CACHE, "remove %s from parent %lx.%lx.%lx\n", + cncp->name, (cncp->dcp)->c_fid.Volume, + (cncp->dcp)->c_fid.Vnode, (cncp->dcp)->c_fid.Unique); + + hashrem(cncp); + hashnull(cncp); /* have it be a null chain */ + + /* VN_RELE(CTOV(cncp->dcp)); */ + iput(CTOI(cncp->cp)); + /* crfree(cncp->cred); */ + + memset(DATA_PART(cncp), 0 ,DATA_SIZE); + cncp->cp = NULL; + cncp->dcp = (struct cnode *) 0; + + /* Put the null entry just after the least-recently-used entry */ + lrurem(cncp); + lruins(cncp, cfsnc_lru.lru_prev); +} + + +/* + * Entry points for the CFS Name Cache + */ + +/* + * Initialize the cache, the LRU structure and the Hash structure(s) + */ +void +cfsnc_init(void) +{ + register int i; + + /* zero the statistics structure */ + cfsnc_procsize = 10000 * cfsnc_hashsize + cfsnc_size; + memset(&cfsnc_stat, 0, (sizeof(struct cfsnc_statistics))); + + CODA_ALLOC(cfsncheap, struct cfscache *, TOTAL_CACHE_SIZE); + CODA_ALLOC(cfsnchash, struct cfshash *, TOTAL_HASH_SIZE); + + cfsnc_lru.lru_next = cfsnc_lru.lru_prev = (struct cfscache *)&cfsnc_lru; + + /* initialize the heap */ + for (i=0; i < cfsnc_size; i++) { + lruins(&cfsncheap[i], (struct cfscache *) &cfsnc_lru); + hashnull(&cfsncheap[i]); + cfsncheap[i].cp = cfsncheap[i].dcp = (struct cnode *)0; + } + + for (i=0; i < cfsnc_hashsize; i++) { /* initialize the hashtable */ + hashnull((struct cfscache *)&cfsnchash[i]); + cfsnchash[i].length=0; /* bucket length */ + } + + cfsnc_initialized = 1; + CDEBUG(D_CACHE, "cfsnc_initialized is now 1.\n"); +} + +/* + * Enter a new (dir cnode, name) pair into the cache, updating the + * LRU and Hash as needed. + */ + +void +cfsnc_enter(struct cnode *dcp, register const char *name, int namelen, struct cnode *cp) +{ + register struct cfscache *cncp; + register int hash; + + if (cfsnc_use == 0) /* Cache is off */ + return; + + CDEBUG(D_CACHE, "dcp 0x%x cp 0x%x name %s, ind 0x%x \n", + (int)dcp, (int)cp, name, (int)cp->c_vnode); + + if (namelen > CFSNC_NAMELEN) { + CDEBUG(D_CACHE, "long name enter %s\n",name); + cfsnc_stat.long_name_enters++; /* record stats */ + return; + } + + hash = nchash(name, namelen, dcp); + CDEBUG(D_CACHE, "Calling find with name %s, dcp %d, hash %d\n", + name, (int) dcp, (int) hash); + + cncp = cfsnc_find(dcp, name, namelen, hash); + if (cncp != (struct cfscache *) 0) { + printk("cfsnc_enter: Duplicate cache entry; tell Peter.\n"); + cfsnc_stat.dbl_enters++; /* duplicate entry */ + return; + } + + cfsnc_stat.enters++; /* record the enters statistic */ + + /* Grab the lru element in the lru chain */ + cncp = cfsnc_lru.lru_prev; + + lrurem(cncp); /* remove it from the lists */ + + /* if cncp is on hash list remove it */ + if ( cncp->dcp != (struct cnode *) 0 ) { + /* We have to decrement the appropriate hash bucket length + here, so we have to find the hash bucket */ + cfsnchash[nchash(cncp->name, cncp->namelen, cncp->dcp)].length--; + cfsnc_stat.lru_rm++; /* zapped a valid entry */ + hashrem(cncp); + iput(CTOI(cncp->cp)); + /* VN_RELE(CTOV(cncp->dcp)); */ + /* crfree(cncp->cred); */ + } + /* + * Put a hold on the current vnodes and fill in the cache entry. + */ + iget((CTOI(cp))->i_sb, CTOI(cp)->i_ino); + /* VN_HOLD(CTOV(dcp)); */ + /* XXXX crhold(cred); */ + cncp->dcp = dcp; + cncp->cp = cp; + cncp->namelen = namelen; + /* cncp->cred = cred; */ + + memcpy(cncp->name, name, (unsigned)namelen); + + /* Insert into the lru and hash chains. */ + + lruins(cncp, (struct cfscache *) &cfsnc_lru); + hashins(cncp, (struct cfscache *)&cfsnchash[hash]); + cfsnchash[hash].length++; /* Used for tuning */ + CDEBUG(D_CACHE, "Entering:\n"); + coda_print_ce(cncp); +} + +/* + * Find the (dir cnode, name) pair in the cache, if it's cred + * matches the input, return it, otherwise return 0 + */ + +struct cnode * +cfsnc_lookup(struct cnode *dcp, register const char *name, int namelen) +{ + register int hash; + register struct cfscache *cncp; + /* this should go into a callback funcntion for /proc/sys + don't know how at the moment? */ + if (cfsnc_flushme == 1) { + cfsnc_flush(); + cfsnc_flushme = 0; + } + + if (cfsnc_procsize != 10000*cfsnc_hashsize + cfsnc_size ) { + int hsh = cfsnc_procsize/10000; + int siz = cfsnc_procsize%10000; + int rc; + + if ( (hsh > 1) && (siz > 2) ) { + rc = cfsnc_resize(hsh, siz); + if ( !rc ) { + printk("Coda:cache size (hash,size) (%d,%d)\n", + hsh, siz); + } else { + printk("Coda: cache resize failed\n"); + } + } + } + + if (cfsnc_use == 0) /* Cache is off */ + return((struct cnode *) 0); + + if (namelen > CFSNC_NAMELEN) { + CDEBUG(D_CACHE,"long name lookup %s\n",name); + cfsnc_stat.long_name_lookups++; /* record stats */ + 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 = nchash(name, namelen, dcp); + CDEBUG(D_CACHE, "Calling find with name %s, dcp %d, hash %d\n", + name, (int) dcp, (int) hash); + cncp = cfsnc_find(dcp, name, namelen, hash); + if (cncp == (struct cfscache *) 0) { + cfsnc_stat.misses++; /* record miss */ + return((struct cnode *) 0); + } + + cfsnc_stat.hits++; + + /* put this entry at the mru end of the LRU */ + lrurem(cncp); + lruins(cncp, (struct cfscache *) &cfsnc_lru); + + /* move it to the front of the hash chain */ + /* don't need to change the hash bucket length */ + hashrem(cncp); + hashins(cncp, (struct cfscache *) &cfsnchash[hash]); + + CDEBUG(D_CACHE, "lookup: dcp 0x%x, name %s, cp 0x%x\n", + (int) dcp, name, (int) cncp->cp); + + return(cncp->cp); +} + +/* + * Remove all entries with a parent which has the input fid. + */ + +void +cfsnc_zapParentfid(ViceFid *fid) +{ + /* 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 occurences. + */ + register struct cfscache *cncp, *ncncp; + register int i; + + if (cfsnc_use == 0) /* Cache is off */ + return; + + CDEBUG(D_CACHE, " fid 0x%lx, 0x%lx, 0x%lx \n", + fid->Volume, fid->Vnode, fid->Unique); + + cfsnc_stat.zapPfids++; + + for (i = 0; i < cfsnc_hashsize; i++) { + + /* + * Need to save the hash_next pointer in case we remove the + * entry. remove causes hash_next to point to itself. + */ + + for (cncp = cfsnchash[i].hash_next; + cncp != (struct cfscache *) &cfsnchash[i]; + cncp = ncncp) { + ncncp = cncp->hash_next; + if ( coda_fideq(&cncp->dcp->c_fid, fid) ) { + cfsnchash[i].length--; /* Used for tuning */ + cfsnc_remove(cncp); + } + } + } +} + +/* + * Remove all entries which have the same fid as the input + */ +void +cfsnc_zapfid(ViceFid *fid) +{ + /* See comment for zapParentfid. This routine will be used + if attributes are being cached. + */ + register struct cfscache *cncp, *ncncp; + register int i; + + if (cfsnc_use == 0) /* Cache is off */ + return; + + CDEBUG(D_CACHE, "Zapfid: fid 0x%lx, 0x%lx, 0x%lx \n", + fid->Volume, fid->Vnode, fid->Unique); + + cfsnc_stat.zapFids++; + + for (i = 0; i < cfsnc_hashsize; i++) { + for (cncp = cfsnchash[i].hash_next; + cncp != (struct cfscache *) &cfsnchash[i]; + cncp = ncncp) { + ncncp = cncp->hash_next; + if (coda_fideq(&(cncp->cp->c_fid), fid)) { + CDEBUG(D_CACHE, "Found cncp: name %s\n", cncp->name); + cfsnchash[i].length--; /* Used for tuning */ + cfsnc_remove(cncp); + } + } + } +} + + +/* + * Remove all entries which have the (dir vnode, name) pair + */ +void +cfsnc_zapfile(struct cnode *dcp, register const char *name, int length) +{ + /* use the hash function to locate the file, then zap all + entries of it regardless of the cred. + */ + register struct cfscache *cncp; + int hash; + + if (cfsnc_use == 0) /* Cache is off */ + return; + + CDEBUG(D_CACHE,"Zapfile: dcp 0x%x name %s \n", + (int) dcp, name); + + if (length > CFSNC_NAMELEN) { + cfsnc_stat.long_remove++; /* record stats */ + return; + } + + cfsnc_stat.zapFile++; + + hash = nchash(name, length, dcp); + /* remove entries: remember they might exist for more than a + single cred */ + while ( (cncp = cfsnc_find(dcp, name, length, hash)) != NULL ) { + cfsnchash[hash].length--; + cfsnc_remove(cncp); + } +} + +/* + * 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 +cfsnc_purge_user(struct CodaCred *cred) +{ + /* 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 always be full and LRU is more straightforward. + */ + + register struct cfscache *cncp; + int hash; + + if (cfsnc_use == 0) /* Cache is off */ + return; + + CDEBUG(D_CACHE,"ZapDude: uid %ld\n",cred->cr_uid); + cfsnc_stat.zapUsers++; + + for (cncp = cfsnc_lru.lru_next; + cncp != (struct cfscache *) &cfsnc_lru; + cncp = cncp->lru_next) { + + if ((CFSNC_VALID(cncp)) && + ((cncp->cred)->cr_uid == cred->cr_uid)) { + /* Seems really ugly, but we have to decrement the appropriate + hash bucket length here, so we have to find the hash bucket + */ + hash = nchash(cncp->name, cncp->namelen, cncp->dcp); + cfsnchash[hash].length--; /* For performance tuning */ + + cfsnc_remove(cncp); + } + } +} + +/* + * Flush the entire name cache. In response to a flush of the Venus cache. + */ + +void +cfsnc_flush(void) +{ + /* One option is to deallocate the current name cache and + call init to start again. Or just deallocate, then rebuild. + Or again, we could just go through the array and zero the + appropriate fields. + */ + + /* + * 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. + */ + register struct cfscache *cncp; + int i; + + if ((cfsnc_use == 0 || cfsnc_initialized == 0) && (cfsnc_force == 0) ) + return; + + cfsnc_stat.Flushes++; + + for (cncp = cfsnc_lru.lru_next; + cncp != (struct cfscache *) &cfsnc_lru; + cncp = cncp->lru_next) { + if ( cncp->cp ) { + hashrem(cncp); /* only zero valid nodes */ + hashnull(cncp); + iput(CTOI(cncp->cp)); + /* crfree(cncp->cred); */ + memset(DATA_PART(cncp), 0, DATA_SIZE); + } + } + + for (i = 0; i < cfsnc_hashsize; i++) + cfsnchash[i].length = 0; +} + +/* + * This routine replaces a ViceFid in the name cache with another. + * It is added to allow Venus during reintegration to replace + * locally allocated temp fids while disconnected with global fids + * even when the reference count on those fids are not zero. + */ +void +cfsnc_replace(ViceFid *f1, ViceFid *f2) +{ + /* + * Replace f1 with f2 throughout the name cache + */ + int hash; + register struct cfscache *cncp; + + CDEBUG(D_CACHE, + "cfsnc_replace fid_1 = (%lx.%lx.%lx) and fid_2 = (%lx.%lx.%lx)\n", + f1->Volume, f1->Vnode, f1->Unique, + f2->Volume, f2->Vnode, f2->Unique); + + for (hash = 0; hash < cfsnc_hashsize; hash++) { + for (cncp = cfsnchash[hash].hash_next; + cncp != (struct cfscache *) &cfsnchash[hash]; + cncp = cncp->hash_next) { + if (!memcmp(&cncp->cp->c_fid, f1, sizeof(ViceFid))) { + memcpy(&cncp->cp->c_fid, f2, sizeof(ViceFid)); + continue; /* no need to check cncp->dcp now */ + } + if (!memcmp(&cncp->dcp->c_fid, f1, sizeof(ViceFid))) + memcpy(&cncp->dcp->c_fid, f2, sizeof(ViceFid)); + } + } +} + +/* + * Debugging routines + */ + +/* + * This routine should print out all the hash chains to the console. + */ + +void +print_cfsnc(void) +{ + int hash; + register struct cfscache *cncp; + + for (hash = 0; hash < cfsnc_hashsize; hash++) { + printk("\nhash %d\n",hash); + + for (cncp = cfsnchash[hash].hash_next; + cncp != (struct cfscache *)&cfsnchash[hash]; + cncp = cncp->hash_next) { + printk("cp 0x%x dcp 0x%x cred 0x%x name %s ino %d count %d dev %d\n", + (int)cncp->cp, (int)cncp->dcp, + (int)cncp->cred, cncp->name, CTOI(cncp->cp)->i_count, CTOI(cncp->cp)->i_count, CTOI(cncp->cp)->i_dev); + } + } +} + +int +cfsnc_get_info(char *buffer, char **start, off_t offset, int length, int dummy) +{ + int hash; + int len=0; + off_t pos=0; + off_t begin; + struct cfscache *cncp; + char tmpbuf[80]; + + if (offset < 80) + len += sprintf(buffer, "%-79s\n", + "hash len volume vnode unique name ino pino ct"); + if ( !cfsnc_initialized ) { + *start = buffer; + return len; + } + pos = 80; + for (hash = 0; hash < cfsnc_hashsize; hash++) { + for (cncp = cfsnchash[hash].hash_next; + cncp != (struct cfscache *)&cfsnchash[hash]; + cncp = cncp->hash_next) { + pos += 80; + if (pos < offset) + continue; + sprintf(tmpbuf, "%4d %3d %8x %8x %8x %16s %10ld %10ld %2d", + hash, cfsnchash[hash].length, (int) cncp->cp->c_fid.Volume, + (int) cncp->cp->c_fid.Vnode, (int) cncp->cp->c_fid.Unique , cncp->name, + CTOI(cncp->cp)->i_ino, + CTOI(cncp->dcp)->i_ino, + CTOI(cncp->cp)->i_count); + len += sprintf(buffer+len, "%-79s\n", tmpbuf); + if(len >= length) + break; + } + if(len>= length) + break; + } + begin = len - (pos - offset); + *start = buffer + begin; + len -= begin; + if(len>length) + len = length; + return len; +} + +int +cfsnc_nc_info(char *buffer, char **start, off_t offset, int length, int dummy) +{ + int len=0; + off_t begin; + + cfsnc_gather_stats(); + + /* this works as long as we are below 1024 characters! */ + len += sprintf(buffer,"Coda minicache statistics\n\n"); + len += sprintf(buffer+len, "cfsnc_hits : %d\n", cfsnc_stat.hits); + len += sprintf(buffer+len, "cfsnc_misses : %d\n", cfsnc_stat.misses); + len += sprintf(buffer+len, "cfsnc_enters : %d\n", cfsnc_stat.enters); + len += sprintf(buffer+len, "cfsnc_dbl_enters : %d\n", cfsnc_stat.dbl_enters); + len += sprintf(buffer+len, "cfsnc_long_name_enters : %d\n", cfsnc_stat.long_name_enters); + len += sprintf(buffer+len, "cfsnc_long_name_lookups : %d\n", cfsnc_stat.long_name_lookups); + len += sprintf(buffer+len, "cfsnc_long_remove : %d\n", cfsnc_stat.long_remove); + len += sprintf(buffer+len, "cfsnc_lru_rm : %d\n", cfsnc_stat.lru_rm); + len += sprintf(buffer+len, "cfsnc_zapPfids : %d\n", cfsnc_stat.zapPfids); + len += sprintf(buffer+len, "cfsnc_zapFids : %d\n", cfsnc_stat.zapFids); + len += sprintf(buffer+len, "cfsnc_zapFile : %d\n", cfsnc_stat.zapFile); + len += sprintf(buffer+len, "cfsnc_zapUsers : %d\n", cfsnc_stat.zapUsers); + len += sprintf(buffer+len, "cfsnc_Flushes : %d\n", cfsnc_stat.Flushes); + len += sprintf(buffer+len, "cfsnc_SumLen : %d\n", cfsnc_stat.Sum_bucket_len); + len += sprintf(buffer+len, "cfsnc_Sum2Len : %d\n", cfsnc_stat.Sum2_bucket_len); + len += sprintf(buffer+len, "cfsnc_# 0 len : %d\n", cfsnc_stat.Num_zero_len); + len += sprintf(buffer+len, "cfsnc_MaxLen : %d\n", cfsnc_stat.Max_bucket_len); + len += sprintf(buffer+len, "cfsnc_SearchLen : %d\n", cfsnc_stat.Search_len); + begin = offset; + *start = buffer + begin; + len -= begin; + + if(len>length) + len = length; + if (len< 0) + len = 0; + return len; +} + + + +void +coda_print_ce(struct cfscache *ce) +{ +CDEBUG(D_CACHE, "cp 0x%x, dcp 0x%x, name %s, inod 0x%x, ino %d, count %d, dev %d\n", + (int)ce->cp, (int)ce->dcp, ce->name, (int)CTOI(ce->cp),(int)CTOI(ce->cp)->i_ino, CTOI(ce->cp)->i_count, CTOI(ce->cp)->i_dev); +} + +static void +cfsnc_gather_stats(void) +{ + int i, max = 0, sum = 0, temp, zeros = 0, ave, n; + + for (i = 0; i < cfsnc_hashsize; i++) { + if (cfsnchash[i].length) { + sum += cfsnchash[i].length; + } else { + zeros++; + } + + if (cfsnchash[i].length > max) + max = cfsnchash[i].length; + } + +/* + * When computing the Arithmetic mean, only count slots which + * are not empty in the distribution. + */ + cfsnc_stat.Sum_bucket_len = sum; + cfsnc_stat.Num_zero_len = zeros; + cfsnc_stat.Max_bucket_len = max; + + if ((n = cfsnc_hashsize - zeros) > 0) + ave = sum / n; + else + ave = 0; + + sum = 0; + for (i = 0; i < cfsnc_hashsize; i++) { + if (cfsnchash[i].length) { + temp = cfsnchash[i].length - ave; + sum += temp * temp; + } + } + cfsnc_stat.Sum2_bucket_len = sum; +} + +/* + * 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 +cfsnc_resize(int hashsize, int heapsize) +{ + if ( !cfsnc_use ) + return 0; + + if ((hashsize % 2) || (heapsize % 2)) { /* Illegal hash or cache sizes */ + return(EINVAL); + } + + cfsnc_use = 0; /* Turn the cache off */ + cfsnc_force = 1; /* otherwise we can't flush */ + + cfsnc_flush(); /* free any cnodes in the cache */ + cfsnc_force = 0; + + /* WARNING: free must happen *before* size is reset */ + CODA_FREE(cfsncheap,TOTAL_CACHE_SIZE); + CODA_FREE(cfsnchash,TOTAL_HASH_SIZE); + + cfsnc_hashsize = hashsize; + cfsnc_size = heapsize; + + cfsnc_init(); /* Set up a cache with the new size */ + + cfsnc_use = 1; /* Turn the cache back on */ + return(0); +} + + + |