Coda File System

your APIC patch and my Coda patch

From: Peter J. Braam <braam_at_cs.cmu.edu>
Date: Mon, 4 May 1998 19:03:20 -0400 (EDT)
Linus,

No more hangs and I really went at it the last few hours. I applied the
two changes you emailed to me and Ingo privately and things seem a lot
better now (several builds working in parallel etc). 

I also include a Coda patch largely prepared by a group of students from
Yale (Michihiro Kuramochi <kuramochi-michihiro_at_CS.YALE.EDU>, Xia Zhenyu
<zhenyu.xia_at_yale.edu>, and Zhanyong Wan <zhanyong.wan_at_yale.edu>) for their
OS course and somewhat edited by me.

It basically provides /proc/fs/coda with a few files with nice statistics
files etc.  We did make a directory /proc/fs to hold this stuff, and put our
Coda stuff underneath.  Other filesystems may want to follow suit.  We
have had a lot of good use from /proc, particularly /proc/sys to control
debugging options, hard and soft mounting _dynamically_. 

Patch instructions: 

rm include/linux/coda_sysctl.h
patch ... < coda.patch

I hope to prepare one more patch that solves two bugs we have seen before
2.2. 



- Peter -

diff -uNr linux-2.1.99/fs/coda/Makefile linux/fs/coda/Makefile
--- linux-2.1.99/fs/coda/Makefile	Wed Mar 18 00:19:04 1998
+++ linux/fs/coda/Makefile	Mon May  4 18:15:45 1998
@@ -4,7 +4,7 @@
 
 O_TARGET := coda.o
 O_OBJS   := psdev.o cache.o cnode.o inode.o dir.o file.o upcall.o coda_linux.o\
-            symlink.o pioctl.o sysctl.o
+            symlink.o pioctl.o sysctl.o stats.o
 M_OBJS   := $(O_TARGET)
 
 # If you want debugging output, please uncomment the following line
diff -uNr linux-2.1.99/fs/coda/cache.c linux/fs/coda/cache.c
--- linux-2.1.99/fs/coda/cache.c	Wed Mar 18 00:19:05 1998
+++ linux/fs/coda/cache.c	Mon May  4 18:15:04 1998
@@ -32,9 +32,6 @@
 static struct coda_cache * coda_cache_find(struct inode *inode);
 
 
-/* Keep various stats */
-struct cfsnc_statistics cfsnc_stat;
-
 /* insert a acl-cache entry in sb list */
 static void coda_ccinsert(struct coda_cache *el, struct super_block *sb)
 {
@@ -295,41 +292,3 @@
 
 
 
-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;
-} 
diff -uNr linux-2.1.99/fs/coda/coda_linux.c linux/fs/coda/coda_linux.c
--- linux-2.1.99/fs/coda/coda_linux.c	Wed Mar 18 00:19:05 1998
+++ linux/fs/coda/coda_linux.c	Mon May  4 18:15:06 1998
@@ -7,6 +7,7 @@
  * the Coda project. Contact Peter Braam (coda_at_cs.cmu.edu).
  */
 
+#include <linux/version.h>
 #include <linux/types.h>
 #include <linux/kernel.h>
 #include <linux/sched.h>
diff -uNr linux-2.1.99/fs/coda/dir.c linux/fs/coda/dir.c
--- linux-2.1.99/fs/coda/dir.c	Wed Mar 18 00:19:05 1998
+++ linux/fs/coda/dir.c	Mon May  4 18:15:07 1998
@@ -23,6 +23,7 @@
 #include <linux/coda_psdev.h>
 #include <linux/coda_fs_i.h>
 #include <linux/coda_cache.h>
+#include <linux/coda_proc.h>
 
 /* dir inode-ops */
 static int coda_create(struct inode *dir, struct dentry *new, int mode);
@@ -179,6 +180,8 @@
         int error;
  
         ENTRY;
+	coda_vfs_stat.permission++;
+	coda_permission_stat.count++;
 
         if ( mask == 0 ) {
                 EXIT;
@@ -187,6 +190,7 @@
 
 	if ( coda_access_cache == 1 ) {
 		if ( coda_cache_check(inode, mask) ) {
+			coda_permission_stat.hit_count++;
 			return 0; 
 		}
 	}
@@ -221,6 +225,8 @@
 	struct ViceFid newfid;
 	struct coda_vattr attrs;
 
+	coda_vfs_stat.create++;
+
 	CDEBUG(D_INODE, "name: %s, length %d, mode %o\n",name, length, mode);
 
         if (!dir || !S_ISDIR(dir->i_mode)) {
@@ -274,6 +280,8 @@
 	struct ViceFid newfid;
 
 
+	coda_vfs_stat.mkdir++;
+
 	if (!dir || !S_ISDIR(dir->i_mode)) {
 		printk("coda_mkdir: inode is NULL or not a directory\n");
 		return -ENOENT;
@@ -329,6 +337,7 @@
 	int error;
 
         ENTRY;
+	coda_vfs_stat.link++;
 
 	if (coda_isroot(dir_inode) && coda_iscontrol(name, len))
 		return -EPERM;
@@ -373,6 +382,7 @@
         int error=0;
         
         ENTRY;
+	coda_vfs_stat.symlink++;
 
 	if (coda_isroot(dir_inode) && coda_iscontrol(name, len))
 		return -EPERM;
@@ -414,6 +424,7 @@
 	int len = de->d_name.len;
 
 	ENTRY;
+	coda_vfs_stat.unlink++;
 
         dircnp = ITOC(dir);
         CHECK_CNODE(dircnp);
@@ -446,6 +457,8 @@
 	int len = de->d_name.len;
         int error, rehash = 0;
 
+	coda_vfs_stat.rmdir++;
+
 	if (!dir || !S_ISDIR(dir->i_mode)) {
 		printk("coda_rmdir: inode is NULL or not a directory\n");
 		return -ENOENT;
@@ -502,6 +515,8 @@
         struct coda_inode_info *new_cnp, *old_cnp;
         int error, rehash = 0, update = 1;
 ENTRY;
+	coda_vfs_stat.rename++;
+
         old_cnp = ITOC(old_dir);
         CHECK_CNODE(old_cnp);
         new_cnp = ITOC(new_dir);
@@ -565,6 +580,7 @@
 	struct inode *inode=file->f_dentry->d_inode;
 
         ENTRY;
+	coda_vfs_stat.readdir++;
 
         if (!inode || !inode->i_sb || !S_ISDIR(inode->i_mode)) {
                 printk("coda_readdir: inode is NULL or not a directory\n");
@@ -606,6 +622,7 @@
 	unsigned short coda_flags = coda_flags_to_cflags(flags);
 
         ENTRY;
+	coda_vfs_stat.open++;
         
         CDEBUG(D_SPECIAL, "OPEN inode number: %ld, flags %o.\n", 
 	       f->f_dentry->d_inode->i_ino, flags);
@@ -659,6 +676,7 @@
 	unsigned short cflags = coda_flags_to_cflags(flags);
 
         ENTRY;
+	coda_vfs_stat.release++;
 
         cnp =ITOC(i);
         CHECK_CNODE(cnp);
diff -uNr linux-2.1.99/fs/coda/file.c linux/fs/coda/file.c
--- linux-2.1.99/fs/coda/file.c	Wed Mar 18 00:19:05 1998
+++ linux/fs/coda/file.c	Mon May  4 18:15:07 1998
@@ -23,6 +23,7 @@
 #include <linux/coda_fs_i.h>
 #include <linux/coda_psdev.h>
 #include <linux/coda_cache.h>
+#include <linux/coda_proc.h>
 
 /* file operations */
 static int coda_readpage(struct file *file, struct page * page);
@@ -83,6 +84,7 @@
         struct coda_inode_info *cii;
 
         ENTRY;
+	coda_vfs_stat.readpage++;
         
         cii = ITOC(coda_inode);
 
@@ -108,6 +110,8 @@
         struct coda_inode_info *cii;
 	int res;
 
+	coda_vfs_stat.file_mmap++;
+
         ENTRY;
 	cii = ITOC(file->f_dentry->d_inode);
 	cii->c_mmcount++;
@@ -126,7 +130,9 @@
         struct file  cont_file;
 	struct dentry cont_dentry;
         int result = 0;
-        ENTRY;
+
+	ENTRY;
+	coda_vfs_stat.file_read++;
 
         cnp = ITOC(coda_inode);
         CHECK_CNODE(cnp);
@@ -167,6 +173,7 @@
         int result = 0;
 
         ENTRY;
+	coda_vfs_stat.file_write++;
 
         cnp = ITOC(coda_inode);
         CHECK_CNODE(cnp);
@@ -205,6 +212,7 @@
 	struct dentry cont_dentry;
         int result = 0;
         ENTRY;
+	coda_vfs_stat.fsync++;
 
 	if (!(S_ISREG(coda_inode->i_mode) || S_ISDIR(coda_inode->i_mode) ||
 	      S_ISLNK(coda_inode->i_mode)))
diff -uNr linux-2.1.99/fs/coda/psdev.c linux/fs/coda/psdev.c
--- linux-2.1.99/fs/coda/psdev.c	Wed Mar 18 00:19:05 1998
+++ linux/fs/coda/psdev.c	Mon May  4 18:15:10 1998
@@ -44,7 +44,7 @@
 #include <linux/coda_fs_i.h>
 #include <linux/coda_psdev.h>
 #include <linux/coda_cache.h>
-#include <linux/coda_sysctl.h>
+#include <linux/coda_proc.h>
 
 
 /*
@@ -401,19 +401,106 @@
 
 #ifdef CONFIG_PROC_FS
 
-struct proc_dir_entry proc_coda = {
+struct proc_dir_entry proc_sys_root = {
+        PROC_SYS, 3, "sys",                     /* inode, name */
+        S_IFDIR | S_IRUGO | S_IXUGO, 2, 0, 0,   /* mode, nlink, uid, gid */
+        0, &proc_dir_inode_operations,          /* size, ops */
+        NULL, NULL,                             /* get_info, fill_inode */
+        NULL,                                   /* next */
+        NULL, NULL                              /* parent, subdir */
+};
+
+struct proc_dir_entry proc_fs_coda = {
+        PROC_FS_CODA, 4, "coda",
+        S_IFDIR | S_IRUGO | S_IXUGO, 2, 0, 0,
+        0, &proc_dir_inode_operations,
+	NULL, NULL,
+	NULL,
+	NULL, NULL
+};
+
+struct proc_dir_entry proc_sys_coda = {
         0, 4, "coda",
-        S_IFDIR | S_IRUGO | S_IXUGO | S_IWUSR, 2, 0, 0,
-        0, &proc_net_inode_operations,
+        S_IFDIR | S_IRUGO | S_IXUGO, 2, 0, 0,
+        0, &proc_dir_inode_operations,
+	NULL, NULL,
+	NULL,
+	NULL, NULL
+};
 
+struct proc_dir_entry proc_fs = {
+        PROC_FS, 2, "fs",
+        S_IFDIR | S_IRUGO | S_IXUGO, 2, 0, 0,
+        0, &proc_dir_inode_operations,
+	NULL, NULL,
+	NULL,
+	NULL, NULL
 };
 
+#if 0
 struct proc_dir_entry proc_coda_ncstats =  {
                 0 , 12, "coda-ncstats",
                 S_IFREG | S_IRUGO, 1, 0, 0,
                 0, &proc_net_inode_operations,
                 cfsnc_nc_info
         };
+#endif
+
+struct proc_dir_entry proc_coda_vfs =  {
+                PROC_VFS_STATS , 9, "vfs_stats",
+                S_IFREG | S_IRUGO, 1, 0, 0,
+                0, &proc_net_inode_operations,
+                coda_vfs_stats_get_info
+        };
+
+struct proc_dir_entry proc_coda_vfs_control =  {
+                0 , 9, "vfs_stats",
+                S_IFREG | S_IRUGO, 1, 0, 0,
+                0, &proc_net_inode_operations,
+                coda_vfs_stats_get_info
+        };
+
+struct proc_dir_entry proc_coda_upcall =  {
+                PROC_UPCALL_STATS , 12, "upcall_stats",
+                S_IFREG | S_IRUGO, 1, 0, 0,
+                0, &proc_net_inode_operations,
+                coda_upcall_stats_get_info
+        };
+
+struct proc_dir_entry proc_coda_upcall_control =  {
+                0 , 12, "upcall_stats",
+                S_IFREG | S_IRUGO, 1, 0, 0,
+                0, &proc_net_inode_operations,
+                coda_upcall_stats_get_info
+        };
+
+struct proc_dir_entry proc_coda_permission =  {
+                PROC_PERMISSION_STATS , 16, "permission_stats",
+                S_IFREG | S_IRUGO, 1, 0, 0,
+                0, &proc_net_inode_operations,
+                coda_permission_stats_get_info
+        };
+
+struct proc_dir_entry proc_coda_permission_control =  {
+                0 , 16, "permission_stats",
+                S_IFREG | S_IRUGO, 1, 0, 0,
+                0, &proc_net_inode_operations,
+                coda_permission_stats_get_info
+        };
+
+struct proc_dir_entry proc_coda_cache_inv =  {
+                PROC_CACHE_INV_STATS , 15, "cache_inv_stats",
+                S_IFREG | S_IRUGO, 1, 0, 0,
+                0, &proc_net_inode_operations,
+                coda_cache_inv_stats_get_info
+        };
+
+struct proc_dir_entry proc_coda_cache_inv_control =  {
+                0 , 15, "cache_inv_stats",
+                S_IFREG | S_IRUGO, 1, 0, 0,
+                0, &proc_net_inode_operations,
+                coda_cache_inv_stats_get_info
+        };
 
 #endif
 
@@ -429,9 +516,27 @@
 	memset(coda_super_info, 0, sizeof(coda_super_info));
 	memset(&coda_callstats, 0, sizeof(coda_callstats));
 
+	reset_coda_vfs_stats();
+	reset_coda_upcall_stats();
+	reset_coda_permission_stats();
+	reset_coda_cache_inv_stats();
+
 #ifdef CONFIG_PROC_FS
-	proc_register(&proc_root,&proc_coda);
-	proc_register(&proc_coda, &proc_coda_ncstats);
+	proc_register(&proc_root,&proc_fs);
+	proc_register(&proc_fs,&proc_fs_coda);
+	proc_register(&proc_fs_coda,&proc_coda_vfs);
+	proc_register(&proc_fs_coda,&proc_coda_upcall);
+	proc_register(&proc_fs_coda,&proc_coda_permission);
+	proc_register(&proc_fs_coda,&proc_coda_cache_inv);
+#if 0
+	proc_register(&proc_fs_coda, &proc_coda_ncstats);
+#endif
+	proc_register(&proc_sys_root,&proc_sys_coda);
+	proc_register(&proc_sys_coda,&proc_coda_vfs_control);
+	proc_register(&proc_sys_coda,&proc_coda_upcall_control);
+	proc_register(&proc_sys_coda,&proc_coda_permission_control);
+	proc_register(&proc_sys_coda,&proc_coda_cache_inv_control);
+
 	coda_sysctl_init();
 #endif 
 	return 0;
@@ -476,8 +581,22 @@
 
 #if CONFIG_PROC_FS
         coda_sysctl_clean();
-        proc_unregister(&proc_coda, proc_coda_ncstats.low_ino);
-	proc_unregister(&proc_root, proc_coda.low_ino);
+
+        proc_unregister(&proc_sys_coda, proc_coda_cache_inv_control.low_ino);
+        proc_unregister(&proc_sys_coda, proc_coda_permission_control.low_ino);
+        proc_unregister(&proc_sys_coda, proc_coda_upcall_control.low_ino);
+	proc_unregister(&proc_sys_coda,proc_coda_vfs_control.low_ino);
+	proc_unregister(&proc_sys_root, proc_sys_coda.low_ino);
+
+#if 0
+	proc_unregister(&proc_fs_coda, proc_coda_ncstats.low_ino);
+#endif
+        proc_unregister(&proc_fs_coda, proc_coda_cache_inv.low_ino);
+        proc_unregister(&proc_fs_coda, proc_coda_permission.low_ino);
+        proc_unregister(&proc_fs_coda, proc_coda_upcall.low_ino);
+        proc_unregister(&proc_fs_coda, proc_coda_vfs.low_ino);
+	proc_unregister(&proc_fs, proc_fs_coda.low_ino);
+	proc_unregister(&proc_root, proc_fs.low_ino);
 #endif 
 }
 
diff -uNr linux-2.1.99/fs/coda/stats.c linux/fs/coda/stats.c
--- linux-2.1.99/fs/coda/stats.c	Wed Dec 31 19:00:00 1969
+++ linux/fs/coda/stats.c	Mon May  4 18:15:10 1998
@@ -0,0 +1,417 @@
+/*
+ * stats.c
+ * 
+ * CODA operation statistics
+ *
+ * (c) March, 1998 Zhanyong Wan <zhanyong.wan_at_yale.edu>
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/sysctl.h>
+#include <linux/swapctl.h>
+#include <linux/proc_fs.h>
+#include <linux/malloc.h>
+#include <linux/stat.h>
+#include <linux/ctype.h>
+#include <asm/bitops.h>
+#include <asm/segment.h>
+#include <asm/uaccess.h>
+#include <linux/utsname.h>
+
+#include <linux/coda.h>
+#include <linux/coda_linux.h>
+#include <linux/coda_fs_i.h>
+#include <linux/coda_psdev.h>
+#include <linux/coda_cache.h>
+#include <linux/coda_proc.h>
+
+struct coda_vfs_stats		coda_vfs_stat;
+struct coda_permission_stats	coda_permission_stat;
+struct coda_cache_inv_stats	coda_cache_inv_stat;
+struct coda_upcall_stats_entry coda_upcall_stat[CFS_NCALLS];
+
+/* keep this in sync with coda.h! */
+char *coda_upcall_names[] = {
+	"totals      ",   /*  0 */
+	"noop        ",   /*  1 */
+	"root        ",   /*  2 */
+	"sync        ",   /*  3 */
+	"open        ",   /*  4 */
+	"close       ",   /*  5 */
+	"ioctl       ",   /*  6 */
+	"getattr     ",   /*  7 */
+	"setattr     ",   /*  8 */
+	"access      ",   /*  9 */
+	"lookup      ",   /* 10 */
+	"create      ",   /* 11 */
+	"remove      ",   /* 12 */
+	"link        ",   /* 13 */
+	"rename      ",   /* 14 */
+	"mkdir       ",   /* 15 */
+	"rmdir       ",   /* 16 */
+	"readdir     ",   /* 17 */
+	"symlink     ",   /* 18 */
+	"readlink    ",   /* 19 */
+	"fsync       ",   /* 20 */
+	"inactive    ",   /* 21 */
+	"vget        ",   /* 22 */
+	"signal      ",   /* 23 */
+	"replace     ",   /* 24 */
+	"flush       ",   /* 25 */
+	"purgeuser   ",   /* 26 */
+	"zapfile     ",   /* 27 */
+	"zapdir      ",   /* 28 */
+	"zapvnode    ",   /* 28 */
+	"purgefid    ",   /* 30 */
+	"open_by_path"    /* 31 */
+};
+
+
+
+
+void reset_coda_vfs_stats( void )
+{
+	memset( &coda_vfs_stat, 0, sizeof( coda_vfs_stat ) );
+}
+
+#if 0
+static void reset_upcall_entry( struct coda_upcall_stats_entry * pentry )
+{
+	pentry->count = 0;
+	pentry->time_sum = pentry->time_squared_sum = 0;
+}
+#endif
+
+void reset_coda_upcall_stats( void )
+{
+	memset( &coda_upcall_stat, 0, sizeof( coda_upcall_stat ) );
+}
+
+void reset_coda_permission_stats( void )
+{
+	memset( &coda_permission_stat, 0, sizeof( coda_permission_stat ) );
+}
+
+void reset_coda_cache_inv_stats( void )
+{
+	memset( &coda_cache_inv_stat, 0, sizeof( coda_cache_inv_stat ) );
+}
+
+
+void do_time_stats( struct coda_upcall_stats_entry * pentry, 
+		    unsigned long runtime )
+{
+	
+	unsigned long time = runtime * 1000 /HZ;	/* time in ms */
+	CDEBUG(D_SPECIAL, "time: %ld\n", time);
+
+	if ( pentry->count == 0 ) {
+		pentry->time_sum = pentry->time_squared_sum = 0;
+	}
+	
+	pentry->count++;
+	pentry->time_sum += time;
+	pentry->time_squared_sum += time*time;
+}
+
+
+
+void coda_upcall_stats(int opcode, long unsigned runtime) 
+{
+	struct coda_upcall_stats_entry * pentry;
+	
+	if ( opcode < 0 || opcode > CFS_NCALLS - 1) {
+		printk("Nasty opcode %d passed to coda_upcall_stats\n",
+		       opcode);
+		return;
+	}
+		
+	pentry = &coda_upcall_stat[opcode];
+	do_time_stats(pentry, runtime);
+
+        /* fill in the totals */
+	pentry = &coda_upcall_stat[0];
+	do_time_stats(pentry, runtime);
+
+}
+
+unsigned long get_time_average( const struct coda_upcall_stats_entry * pentry )
+{
+	return ( pentry->count == 0 ) ? 0 : pentry->time_sum / pentry->count;
+}
+
+static inline unsigned long absolute( unsigned long x )
+{
+	return x >= 0 ? x : -x;
+}
+
+static unsigned long sqr_root( unsigned long x )
+{
+	unsigned long y = x, r;
+	int n_bit = 0;
+  
+	if ( x == 0 )
+		return 0;
+	if ( x < 0)
+		x = -x;
+
+	while ( y ) {
+		y >>= 1;
+		n_bit++;
+	}
+  
+	r = 1 << (n_bit/2);
+  
+	while ( 1 ) {
+		r = (r + x/r)/2;
+		if ( r*r <= x && x < (r+1)*(r+1) )
+			break;
+	}
+  
+	return r;
+}
+
+unsigned long get_time_std_deviation( const struct coda_upcall_stats_entry * pentry )
+{
+	unsigned long time_avg;
+  
+	if ( pentry->count <= 1 )
+		return 0;
+  
+	time_avg = get_time_average( pentry );
+	return 
+	        sqr_root( (pentry->time_squared_sum / pentry->count) - 
+			    time_avg * time_avg );
+}
+
+int do_reset_coda_vfs_stats( ctl_table * table, int write, struct file * filp,
+			     void * buffer, size_t * lenp )
+{
+	if ( write ) {
+		reset_coda_vfs_stats();
+	}
+  
+	*lenp = 0;
+	return 0;
+}
+
+int do_reset_coda_upcall_stats( ctl_table * table, int write, 
+				struct file * filp, void * buffer, 
+				size_t * lenp )
+{
+	if ( write ) {
+		reset_coda_upcall_stats();
+	}
+  
+	*lenp = 0;
+	return 0;
+}
+
+int do_reset_coda_permission_stats( ctl_table * table, int write, 
+				    struct file * filp, void * buffer, 
+				    size_t * lenp )
+{
+	if ( write ) {
+		reset_coda_permission_stats();
+	}
+  
+	*lenp = 0;
+	return 0;
+}
+
+int do_reset_coda_cache_inv_stats( ctl_table * table, int write, 
+				   struct file * filp, void * buffer, 
+				   size_t * lenp )
+{
+	if ( write ) {
+		reset_coda_cache_inv_stats();
+	}
+  
+	*lenp = 0;
+	return 0;
+}
+
+int coda_vfs_stats_get_info( char * buffer, char ** start, off_t offset,
+			     int length, int dummy )
+{
+	int len=0;
+	off_t begin;
+	struct coda_vfs_stats * ps = & coda_vfs_stat;
+  
+  /* this works as long as we are below 1024 characters! */
+	len += sprintf( buffer,
+			"Coda VFS statistics\n"
+			"===================\n\n"
+			"File Operations:\n"
+			"\tfile_read\t%9d\n"
+			"\tfile_write\t%9d\n"
+			"\tfile_mmap\t%9d\n"
+			"\topen\t\t%9d\n"
+			"\trelase\t\t%9d\n"
+			"\tfsync\t\t%9d\n\n"
+			"Dir Operations:\n"
+			"\treaddir\t\t%9d\n\n"
+			"Inode Operations\n"
+			"\tcreate\t\t%9d\n"
+			"\tlookup\t\t%9d\n"
+			"\tlink\t\t%9d\n"
+			"\tunlink\t\t%9d\n"
+			"\tsymlink\t\t%9d\n"
+			"\tmkdir\t\t%9d\n"
+			"\trmdir\t\t%9d\n"
+			"\trename\t\t%9d\n"
+			"\tpermission\t%9d\n"
+			"\treadpage\t%9d\n",
+
+			/* file operations */
+			ps->file_read,
+			ps->file_write,
+			ps->file_mmap,
+			ps->open,
+			ps->release,
+			ps->fsync,
+
+			/* dir operations */
+			ps->readdir,
+		  
+			/* inode operations */
+			ps->create,
+			ps->lookup,
+			ps->link,
+			ps->unlink,
+			ps->symlink,
+			ps->mkdir,
+			ps->rmdir,
+			ps->rename,
+			ps->permission,
+			ps->readpage );
+  
+	begin = offset;
+	*start = buffer + begin;
+	len -= begin;
+
+	if ( len > length )
+		len = length;
+	if ( len < 0 )
+		len = 0;
+
+	return len;
+}
+
+int coda_upcall_stats_get_info( char * buffer, char ** start, off_t offset,
+				int length, int dummy )
+{
+	int len=0;
+	int i;
+	off_t begin;
+	off_t pos = 0;
+	char tmpbuf[80];
+	int tmplen = 0;
+
+	ENTRY;
+	/* this works as long as we are below 1024 characters! */
+	if ( offset < 80 ) 
+		len += sprintf( buffer,"%-79s\n",	"Coda upcall statistics");
+	if ( offset < 160) 
+		len += sprintf( buffer + len,"%-79s\n",	"======================");
+	if ( offset < 240) 
+		len += sprintf( buffer + len,"%-79s\n",	"upcall\t\t    count\tavg time(ms)\tstd deviation(ms)");
+	if ( offset < 320) 
+		len += sprintf( buffer + len,"%-79s\n",	"------\t\t    -----\t------------\t-----------------");
+	pos = 320; 
+	for ( i = 0 ; i < CFS_NCALLS ; i++ ) {
+		tmplen += sprintf(tmpbuf,"%s\t%9d\t%10ld\t%10ld", 
+				  coda_upcall_names[i],
+				  coda_upcall_stat[i].count, 
+				  get_time_average(&coda_upcall_stat[i]),
+				  coda_upcall_stat[i].time_squared_sum);
+		pos += 80;
+		if ( pos < offset ) 
+			continue; 
+		len += sprintf(buffer + len, "%-79s\n", tmpbuf);
+		if ( len >= length ) 
+			break; 
+	}
+  
+	begin = len- (pos - offset);
+	*start = buffer + begin;
+	len -= begin;
+
+	if ( len > length )
+		len = length;
+	if ( len < 0 )
+		len = 0;
+	EXIT;
+	return len;
+}
+
+int coda_permission_stats_get_info( char * buffer, char ** start, off_t offset,
+				    int length, int dummy )
+{
+	int len=0;
+	off_t begin;
+	struct coda_permission_stats * ps = & coda_permission_stat;
+  
+	/* this works as long as we are below 1024 characters! */
+	len += sprintf( buffer,
+			"Coda permission statistics\n"
+			"==========================\n\n"
+			"count\t\t%9d\n"
+			"hit count\t%9d\n",
+
+			ps->count,
+			ps->hit_count );
+  
+	begin = offset;
+	*start = buffer + begin;
+	len -= begin;
+
+	if ( len > length )
+		len = length;
+	if ( len < 0 )
+		len = 0;
+
+	return len;
+}
+
+int coda_cache_inv_stats_get_info( char * buffer, char ** start, off_t offset,
+				   int length, int dummy )
+{
+	int len=0;
+	off_t begin;
+	struct coda_cache_inv_stats * ps = & coda_cache_inv_stat;
+  
+	/* this works as long as we are below 1024 characters! */
+	len += sprintf( buffer,
+			"Coda cache invalidation statistics\n"
+			"==================================\n\n"
+			"flush\t\t%9d\n"
+			"purge user\t%9d\n"
+			"zap_dir\t\t%9d\n"
+			"zap_file\t%9d\n"
+			"zap_vnode\t%9d\n"
+			"purge_fid\t%9d\n"
+			"replace\t\t%9d\n",
+			ps->flush,
+			ps->purge_user,
+			ps->zap_dir,
+			ps->zap_file,
+			ps->zap_vnode,
+			ps->purge_fid,
+			ps->replace );
+  
+	begin = offset;
+	*start = buffer + begin;
+	len -= begin;
+
+	if ( len > length )
+		len = length;
+	if ( len < 0 )
+		len = 0;
+
+	return len;
+}
+
diff -uNr linux-2.1.99/fs/coda/symlink.c linux/fs/coda/symlink.c
--- linux-2.1.99/fs/coda/symlink.c	Wed Mar  4 18:14:32 1998
+++ linux/fs/coda/symlink.c	Mon May  4 18:15:11 1998
@@ -23,6 +23,7 @@
 #include <linux/coda_psdev.h>
 #include <linux/coda_fs_i.h>
 #include <linux/coda_cache.h>
+#include <linux/coda_proc.h>
 
 static int coda_readlink(struct dentry *de, char *buffer, int length);
 static struct dentry *coda_follow_link(struct dentry *, struct dentry *);
@@ -60,7 +61,7 @@
         ENTRY;
 
         cp = ITOC(inode);
-        CHECK_CNODE(cp);
+	coda_vfs_stat.readlink++;
 
         /* the maximum length we receive is len */
         if ( length > CFS_MAXPATHLEN ) 
@@ -93,11 +94,11 @@
 	unsigned int len;
 	char mem[CFS_MAXPATHLEN];
 	char *path;
-ENTRY;
+	ENTRY;
 	CDEBUG(D_INODE, "(%x/%ld)\n", inode->i_dev, inode->i_ino);
 	
         cnp = ITOC(inode);
-        CHECK_CNODE(cnp);
+	coda_vfs_stat.follow_link++;
 
 	len = CFS_MAXPATHLEN;
 	error = venus_readlink(inode->i_sb, &(cnp->c_fid), mem, &len);
diff -uNr linux-2.1.99/fs/coda/sysctl.c linux/fs/coda/sysctl.c
--- linux-2.1.99/fs/coda/sysctl.c	Wed Mar  4 18:14:32 1998
+++ linux/fs/coda/sysctl.c	Mon May  4 18:15:11 1998
@@ -26,7 +26,7 @@
 #include <linux/coda_fs_i.h>
 #include <linux/coda_psdev.h>
 #include <linux/coda_cache.h>
-#include <linux/coda_sysctl.h>
+#include <linux/coda_proc.h>
 extern int coda_debug;
 /* extern int cfsnc_use; */
 extern int coda_print_entry;
@@ -47,6 +47,10 @@
 #define CODA_TIMEOUT    3       /* timeout on upcalls to become intrble */
 #define CODA_MC         4       /* use/do not use the access cache */
 #define CODA_HARD       5       /* mount type "hard" or "soft" */
+#define CODA_VFS 	 6       /* vfs statistics */
+#define CODA_UPCALL 	 7       /* upcall statistics */
+#define CODA_PERMISSION	 8       /* permission statistics */
+#define CODA_CACHE_INV 	 9       /* cache invalidation statistics */
 
 
 
@@ -56,6 +60,10 @@
  	{CODA_MC, "accesscache", &coda_access_cache, sizeof(int), 0644, NULL, &coda_dointvec}, 
  	{CODA_TIMEOUT, "timeout", &coda_timeout, sizeof(int), 0644, NULL, &coda_dointvec},
  	{CODA_HARD, "hard", &coda_hard, sizeof(int), 0644, NULL, &coda_dointvec},
+ 	{CODA_VFS, "vfs_stats", NULL, 0, 0644, NULL, &do_reset_coda_vfs_stats},
+ 	{CODA_UPCALL, "upcall_stats", NULL, 0, 0644, NULL, &do_reset_coda_upcall_stats},
+ 	{CODA_PERMISSION, "permission_stats", NULL, 0, 0644, NULL, &do_reset_coda_permission_stats},
+ 	{CODA_CACHE_INV, "cache_inv_stats", NULL, 0, 0644, NULL, &do_reset_coda_cache_inv_stats},
 	{ 0 }
 };
 
diff -uNr linux-2.1.99/fs/coda/upcall.c linux/fs/coda/upcall.c
--- linux-2.1.99/fs/coda/upcall.c	Wed Mar 18 00:19:05 1998
+++ linux/fs/coda/upcall.c	Mon May  4 18:15:12 1998
@@ -37,6 +37,11 @@
 #include <linux/coda_psdev.h>
 #include <linux/coda_fs_i.h>
 #include <linux/coda_cache.h>
+#include <linux/coda_proc.h> 
+
+
+static int  coda_upcall(struct coda_sb_info *mntinfo, int inSize, int *outSize, 
+		       union inputArgs *buffer);
 
 #define UPARG(op)\
 do {\
@@ -68,10 +73,11 @@
         union inputArgs *inp;
         union outputArgs *outp;
         int insize, outsize, error;
-ENTRY;
+	ENTRY;
 
         insize = SIZE(root);
         UPARG(CFS_ROOT);
+
 	error = coda_upcall(coda_sbp(sb), insize, &outsize, inp);
 	
 	if (error) {
@@ -92,7 +98,7 @@
 {
         union inputArgs *inp;
         union outputArgs *outp;
-        int insize, outsize, error;
+	        int insize, outsize, error;
 ENTRY;
         insize = SIZE(getattr); 
 	UPARG(CFS_GETATTR);
@@ -458,8 +464,8 @@
 int venus_fsync(struct super_block *sb, struct ViceFid *fid)
 {
         union inputArgs *inp;
-        union outputArgs *outp;
-        int insize, outsize, error;
+        union outputArgs *outp; 
+	int insize, outsize, error;
 	
 	insize=SIZE(fsync);
 	UPARG(CFS_FSYNC);
@@ -476,8 +482,8 @@
 int venus_access(struct super_block *sb, struct ViceFid *fid, int mask)
 {
         union inputArgs *inp;
-        union outputArgs *outp;
-        int insize, outsize, error;
+        union outputArgs *outp; 
+	int insize, outsize, error;
 
 	insize = SIZE(access);
 	UPARG(CFS_ACCESS);
@@ -497,8 +503,8 @@
 		 unsigned int cmd, struct PioctlData *data)
 {
         union inputArgs *inp;
-        union outputArgs *outp;
-        int insize, outsize, error;
+        union outputArgs *outp;  
+	int insize, outsize, error;
 	int iocsize;
 
 	insize = VC_MAXMSGSIZE;
@@ -583,11 +589,13 @@
  * reply and return Venus' error, also POSITIVE. 
  * 
  */
-static inline void coda_waitfor_upcall(struct vmsg *vmp)
+static inline unsigned long coda_waitfor_upcall(struct vmsg *vmp)
 {
 	struct wait_queue	wait = { current, NULL };
+	unsigned long posttime;
 
 	vmp->vm_posttime = jiffies;
+	posttime = jiffies;
 
 	add_wait_queue(&vmp->vm_sleep, &wait);
 	for (;;) {
@@ -616,13 +624,17 @@
 	remove_wait_queue(&vmp->vm_sleep, &wait);
 	current->state = TASK_RUNNING;
 
-	return;
+	CDEBUG(D_SPECIAL, "posttime: %ld, returned: %ld\n", posttime, jiffies-posttime);
+	return 	(jiffies - posttime);
+
 }
 
 
-int coda_upcall(struct coda_sb_info *sbi, int inSize, int *outSize, 
+static int coda_upcall(struct coda_sb_info *sbi, 
+		int inSize, int *outSize, 
 		union inputArgs *buffer) 
 {
+	unsigned long runtime; 
 	struct vcomm *vcommp;
 	union outputArgs *out;
 	struct vmsg *vmp;
@@ -635,7 +647,6 @@
 	}
 	vcommp = sbi->sbi_vcomm;
 
-	clstats(((union inputArgs *)buffer)->ih.opcode);
 
 	if (!vcomm_open(vcommp))
                 return(ENODEV);
@@ -670,7 +681,8 @@
 	 * ENODEV.  */
 
 	/* Go to sleep.  Wake up on signals only after the timeout. */
-	coda_waitfor_upcall(vmp);
+	runtime = coda_waitfor_upcall(vmp);
+	coda_upcall_stats(((union inputArgs *)buffer)->ih.opcode, runtime);
 
 	CDEBUG(D_TIMING, "opc: %d time: %ld uniq: %d size: %d\n",
 	       vmp->vm_opcode, jiffies - vmp->vm_posttime, 
diff -uNr linux-2.1.99/include/linux/coda_proc.h linux/include/linux/coda_proc.h
--- linux-2.1.99/include/linux/coda_proc.h	Wed Dec 31 19:00:00 1969
+++ linux/include/linux/coda_proc.h	Mon May  4 18:16:25 1998
@@ -0,0 +1,144 @@
+/*
+ * coda_statis.h
+ * 
+ * CODA operation statistics
+ *
+ * (c) March, 1998
+ * by Michihiro Kuramochi, Zhenyu Xia and Zhanyong Wan
+ * zhanyong.wan_at_yale.edu
+ *
+ */
+
+#ifndef _CODA_PROC_H
+#define _CODA_PROC_H
+
+void coda_sysctl_init(void);
+void coda_sysctl_clean(void);
+void coda_upcall_stats(int opcode, unsigned long jiffies);
+
+#include <linux/sysctl.h>
+#include <linux/coda_fs_i.h>
+#include <linux/coda.h>
+
+/* these four files are presented to show the result of the statistics:
+ *
+ *	/proc/fs/coda/vfs_stats
+ *		      upcall_stats
+ *		      permission_stats
+ *		      cache_inv_stats
+ *
+ * these four files are presented to reset the statistics to 0:
+ *
+ *	/proc/sys/coda/vfs_stats
+ *		       upcall_stats
+ *		       permission_stats
+ *		       cache_inv_stats
+ */
+
+/* VFS operation statistics */
+struct coda_vfs_stats 
+{
+	/* file operations */
+	int file_read;
+	int file_write;
+	int file_mmap;
+	int open;
+	int release;
+	int fsync;
+
+	/* dir operations */
+	int readdir;
+  
+	/* inode operations */
+	int create;
+	int lookup;
+	int link;
+	int unlink;
+	int symlink;
+	int mkdir;
+	int rmdir;
+	int rename;
+	int permission;
+	int readpage;
+
+	/* symlink operatoins*/
+	int follow_link;
+	int readlink;
+};
+
+struct coda_upcall_stats_entry 
+{
+  int count;
+  unsigned long time_sum;
+  unsigned long time_squared_sum;
+};
+
+
+
+/* cache hits for permissions statistics */
+struct coda_permission_stats 
+{
+	int count;
+	int hit_count;
+};
+
+/* cache invalidation statistics */
+struct coda_cache_inv_stats
+{
+	int flush;
+	int purge_user;
+	int zap_dir;
+	int zap_file;
+	int zap_vnode;
+	int purge_fid;
+	int replace;
+};
+
+/* these global variables hold the actual statistics data */
+extern struct coda_vfs_stats		coda_vfs_stat;
+extern struct coda_permission_stats	coda_permission_stat;
+extern struct coda_cache_inv_stats	coda_cache_inv_stat;
+
+/* reset statistics to 0 */
+void reset_coda_vfs_stats( void );
+void reset_coda_upcall_stats( void );
+void reset_coda_permission_stats( void );
+void reset_coda_cache_inv_stats( void );
+
+/* some utitlities to make it easier for you to do statistics for time */
+void do_time_stats( struct coda_upcall_stats_entry * pentry, 
+		    unsigned long jiffy );
+/*
+double get_time_average( const struct coda_upcall_stats_entry * pentry );
+double get_time_std_deviation( const struct coda_upcall_stats_entry * pentry );
+*/
+unsigned long get_time_average( const struct coda_upcall_stats_entry * pentry );
+unsigned long get_time_std_deviation( const struct coda_upcall_stats_entry * pentry );
+
+/* like coda_dointvec, these functions are to be registered in the ctl_table
+ * data structure for /proc/sys/... files 
+ */
+int do_reset_coda_vfs_stats( ctl_table * table, int write, struct file * filp,
+			     void * buffer, size_t * lenp );
+int do_reset_coda_upcall_stats( ctl_table * table, int write, 
+				struct file * filp, void * buffer, 
+				size_t * lenp );
+int do_reset_coda_permission_stats( ctl_table * table, int write, 
+				    struct file * filp, void * buffer, 
+				    size_t * lenp );
+int do_reset_coda_cache_inv_stats( ctl_table * table, int write, 
+				   struct file * filp, void * buffer, 
+				   size_t * lenp );
+
+/* these functions are called to form the content of /proc/fs/coda/... files */
+int coda_vfs_stats_get_info( char * buffer, char ** start, off_t offset,
+			     int length, int dummy );
+int coda_upcall_stats_get_info( char * buffer, char ** start, off_t offset,
+				int length, int dummy );
+int coda_permission_stats_get_info( char * buffer, char ** start, off_t offset,
+				    int length, int dummy );
+int coda_cache_inv_stats_get_info( char * buffer, char ** start, off_t offset,
+				   int length, int dummy );
+
+
+#endif /* _CODA_PROC_H */
diff -uNr linux-2.1.99/include/linux/coda_psdev.h linux/include/linux/coda_psdev.h
--- linux-2.1.99/include/linux/coda_psdev.h	Fri Feb 20 21:28:23 1998
+++ linux/include/linux/coda_psdev.h	Mon May  4 18:16:26 1998
@@ -94,8 +94,6 @@
 int venus_pioctl(struct super_block *sb, struct ViceFid *fid,
 		 unsigned int cmd, struct PioctlData *data);
 int coda_downcall(int opcode, union outputArgs *out, struct super_block *sb);
-int coda_upcall(struct coda_sb_info *mntinfo, int inSize, 
-		int *outSize, union inputArgs *buffer);
 int venus_fsync(struct super_block *sb, struct ViceFid *fid);
 
 
diff -uNr linux-2.1.99/include/linux/proc_fs.h linux/include/linux/proc_fs.h
--- linux-2.1.99/include/linux/proc_fs.h	Thu Apr 30 15:51:42 1998
+++ linux/include/linux/proc_fs.h	Mon May  4 18:17:21 1998
@@ -51,6 +51,7 @@
 	PROC_PPC_HTAB,
 	PROC_SOUND,
 	PROC_MTRR, /* whether enabled or not */
+	PROC_FS
 };
 
 enum pid_directory_inos {
@@ -203,6 +204,19 @@
 	PROC_BUS_PCI = PROC_MCA_LAST,
 	PROC_BUS_PCI_DEVICES,
 	PROC_BUS_LAST
+};
+
+enum fs_directory_inos {
+	PROC_FS_CODA = PROC_MCA_LAST,
+	PROC_FS_LAST
+};
+
+enum fs_coda_directory_inos {
+	PROC_VFS_STATS = PROC_MCA_LAST,
+	PROC_UPCALL_STATS,
+	PROC_PERMISSION_STATS,
+	PROC_CACHE_INV_STATS,
+	PROC_CODA_FS_LAST
 };
 
 /* Finally, the dynamically allocatable proc entries are reserved: */
Received on 1998-05-04 20:07:17