summaryrefslogtreecommitdiffstats
path: root/fs/coda/cache.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/coda/cache.c')
-rw-r--r--fs/coda/cache.c349
1 files changed, 349 insertions, 0 deletions
diff --git a/fs/coda/cache.c b/fs/coda/cache.c
new file mode 100644
index 000000000..7673bfbdd
--- /dev/null
+++ b/fs/coda/cache.c
@@ -0,0 +1,349 @@
+/*
+ * Cache operations for Coda.
+ * 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>.
+ */
+
+#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 <asm/uaccess.h>
+#include <linux/string.h>
+#include <linux/list.h>
+
+#include <linux/coda.h>
+#include <linux/coda_linux.h>
+#include <linux/coda_psdev.h>
+#include <linux/coda_fs_i.h>
+#include <linux/coda_cache.h>
+
+/* Keep various stats */
+struct cfsnc_statistics cfsnc_stat;
+
+
+/* we need to call INIT_LIST_HEAD on cnp->c_cnhead and sbi->sbi_cchead */
+
+void coda_ccinsert(struct coda_cache *el, struct super_block *sb)
+{
+ struct coda_sb_info *sbi = coda_sbp(sb);
+ENTRY;
+ if ( !sbi || !el) {
+ printk("coda_ccinsert: NULL sbi or el!\n");
+ return ;
+ }
+
+ list_add(&el->cc_cclist, &sbi->sbi_cchead);
+}
+
+void coda_cninsert(struct coda_cache *el, struct coda_inode_info *cnp)
+{
+ENTRY;
+ if ( !cnp || !el) {
+ printk("coda_cninsert: NULL cnp or el!\n");
+ return ;
+ }
+ list_add(&el->cc_cnlist, &cnp->c_cnhead);
+}
+
+void coda_ccremove(struct coda_cache *el)
+{
+ ENTRY;
+ if (el->cc_cclist.next && el->cc_cclist.prev)
+ list_del(&el->cc_cclist);
+ else
+ printk("coda_cnremove: trying to remove 0 entry!");
+}
+
+void coda_cnremove(struct coda_cache *el)
+{
+ ENTRY;
+ if (el->cc_cnlist.next && el->cc_cnlist.prev)
+ list_del(&el->cc_cnlist);
+ else
+ printk("coda_cnremove: trying to remove 0 entry!");
+}
+
+
+void coda_cache_create(struct inode *inode, int mask)
+{
+ struct coda_inode_info *cnp = ITOC(inode);
+ struct super_block *sb = inode->i_sb;
+ struct coda_cache *cc = NULL;
+ ENTRY;
+ CODA_ALLOC(cc, struct coda_cache *, sizeof(*cc));
+
+ if ( !cc ) {
+ printk("Out of memory in coda_cache_enter!\n");
+ return;
+ }
+ coda_load_creds(&cc->cc_cred);
+ cc->cc_mask = mask;
+ coda_cninsert(cc, cnp);
+ coda_ccinsert(cc, sb);
+}
+
+struct coda_cache * coda_cache_find(struct inode *inode)
+{
+ struct coda_inode_info *cnp = ITOC(inode);
+ struct list_head *lh, *le;
+ struct coda_cache *cc = NULL;
+
+ le = lh = &cnp->c_cnhead;
+ while( (le = le->next ) != lh ) {
+ /* compare name and creds */
+ cc = list_entry(le, struct coda_cache, cc_cnlist);
+ if ( !coda_cred_ok(&cc->cc_cred) )
+ continue;
+ CDEBUG(D_CACHE, "HIT for ino %ld\n", inode->i_ino );
+ return cc; /* cache hit */
+ }
+ return NULL;
+}
+
+void coda_cache_enter(struct inode *inode, int mask)
+{
+ struct coda_cache *cc;
+
+ cc = coda_cache_find(inode);
+
+ if ( cc ) {
+ cc->cc_mask |= mask;
+ } else {
+ coda_cache_create(inode, mask);
+ }
+}
+
+void coda_cache_clear_cnp(struct coda_inode_info *cnp)
+{
+ struct list_head *lh, *le;
+ struct coda_cache *cc;
+
+ if ( !cnp ) {
+ printk("coda_cache_cnp_clear: NULL cnode\n");
+ return;
+ }
+
+ lh = le = &cnp->c_cnhead;
+ while ( (le = le->next ) != lh ) {
+ cc = list_entry(le, struct coda_cache, cc_cnlist);
+ coda_cnremove(cc);
+ coda_ccremove(cc);
+ CODA_FREE(cc, sizeof(*cc));
+ }
+}
+
+void coda_cache_clear_all(struct super_block *sb)
+{
+ struct list_head *lh, *le;
+ struct coda_cache *cc;
+ struct coda_sb_info *sbi = coda_sbp(sb);
+
+ if ( !sbi ) {
+ printk("coda_cache_clear_all: NULL sbi\n");
+ return;
+ }
+
+ lh = le = &sbi->sbi_cchead;
+ while ( (le = le->next ) != lh ) {
+ cc = list_entry(le, struct coda_cache, cc_cclist);
+ coda_cnremove(cc);
+ coda_ccremove(cc);
+ CODA_FREE(cc, sizeof(*cc));
+ }
+}
+
+void coda_cache_clear_cred(struct super_block *sb, struct coda_cred *cred)
+{
+ struct list_head *lh, *le;
+ struct coda_cache *cc;
+ struct coda_sb_info *sbi = coda_sbp(sb);
+
+ if ( !sbi ) {
+ printk("coda_cache_clear_all: NULL sbi\n");
+ return;
+ }
+
+ lh = le = &sbi->sbi_cchead;
+ while ( (le = le->next ) != lh ) {
+ cc = list_entry(le, struct coda_cache, cc_cclist);
+ if ( coda_cred_eq(&cc->cc_cred, cred)) {
+ coda_cnremove(cc);
+ coda_ccremove(cc);
+ CODA_FREE(cc, sizeof(*cc));
+ }
+ }
+}
+
+
+int coda_cache_check(struct inode *inode, int mask)
+{
+ struct coda_inode_info *cnp = ITOC(inode);
+ struct list_head *lh, *le;
+ struct coda_cache *cc = NULL;
+
+ le = lh = &cnp->c_cnhead;
+ while( (le = le->next ) != lh ) {
+ /* compare name and creds */
+ cc = list_entry(le, struct coda_cache, cc_cnlist);
+ if ( (cc->cc_mask & mask) != mask )
+ continue;
+ if ( !coda_cred_ok(&cc->cc_cred) )
+ continue;
+ CDEBUG(D_CACHE, "HIT for ino %ld\n", inode->i_ino );
+ return 1; /* cache hit */
+ }
+ CDEBUG(D_CACHE, "MISS for ino %ld\n", inode->i_ino );
+ return 0;
+}
+
+
+/* DENTRY related stuff */
+
+/* when the dentry count falls to 0 this is called. If Venus has
+ asked for it to be flushed, we take it out of the dentry hash
+ table with d_drop */
+
+static void coda_flag_children(struct dentry *parent)
+{
+ struct list_head *child;
+ struct coda_inode_info *cnp;
+ struct dentry *de;
+
+ child = parent->d_subdirs.next;
+ while ( child != &parent->d_subdirs ) {
+ de = list_entry(child, struct dentry, d_child);
+ cnp = ITOC(de->d_inode);
+ if (cnp)
+ cnp->c_flags |= C_ZAPFID;
+ CDEBUG(D_CACHE, "ZAPFID for %s\n", coda_f2s(&cnp->c_fid));
+
+ child = child->next;
+ }
+ return;
+}
+
+/* flag dentry and possibly children of a dentry with C_ZAPFID */
+void coda_dentry_delete(struct dentry *dentry)
+{
+ struct inode *inode = dentry->d_inode;
+ struct coda_inode_info *cnp = NULL;
+ ENTRY;
+
+ if (inode) {
+ cnp = ITOC(inode);
+ if ( cnp )
+ CHECK_CNODE(cnp);
+ } else {
+ CDEBUG(D_CACHE, "No inode for dentry_delete!\n");
+ return;
+ }
+
+
+ if ( !cnp ) {
+ printk("No cnode for dentry_delete!\n");
+ return;
+ }
+
+ if ( cnp->c_flags & (C_ZAPFID | C_ZAPDIR) )
+ d_drop(dentry);
+ if ( (cnp->c_flags & C_ZAPDIR) && S_ISDIR(inode->i_mode) ) {
+ coda_flag_children(dentry);
+ }
+ return;
+}
+
+static void coda_zap_cnode(struct coda_inode_info *cnp, int flags)
+{
+ cnp->c_flags |= flags;
+ coda_cache_clear_cnp(cnp);
+}
+
+
+
+/* the dache will notice the flags and drop entries (possibly with
+ children) the moment they are no longer in use */
+void coda_zapfid(struct ViceFid *fid, struct super_block *sb, int flag)
+{
+ struct inode *inode = NULL;
+ struct coda_inode_info *cnp;
+
+ ENTRY;
+
+ if ( !sb ) {
+ printk("coda_zapfid: no sb!\n");
+ return;
+ }
+
+ if ( !fid ) {
+ printk("coda_zapfid: no fid!\n");
+ return;
+ }
+
+ if ( coda_fid_is_volroot(fid) ) {
+ struct list_head *lh, *le;
+ struct coda_sb_info *sbi = coda_sbp(sb);
+ le = lh = &sbi->sbi_volroothead;
+ while ( (le = le->next) != lh ) {
+ cnp = list_entry(le, struct coda_inode_info, c_volrootlist);
+ if ( cnp->c_fid.Volume == fid->Volume)
+ coda_zap_cnode(cnp, flag);
+ }
+ return;
+ }
+
+
+ inode = coda_fid_to_inode(fid, sb);
+ if ( !inode ) {
+ CDEBUG(D_CACHE, "coda_zapfid: no inode!\n");
+ return;
+ }
+ cnp = ITOC(inode);
+ coda_zap_cnode(cnp, flag);
+}
+
+
+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;
+}