Linux-2.6.12-rc2

Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.

Let it rip!
diff --git a/fs/coda/upcall.c b/fs/coda/upcall.c
new file mode 100644
index 0000000..1bae996
--- /dev/null
+++ b/fs/coda/upcall.c
@@ -0,0 +1,914 @@
+/*
+ * Mostly platform independent upcall operations to Venus:
+ *  -- upcalls
+ *  -- upcall routines
+ *
+ * Linux 2.0 version
+ * Copyright (C) 1996 Peter J. Braam <braam@maths.ox.ac.uk>, 
+ * Michael Callahan <callahan@maths.ox.ac.uk> 
+ * 
+ * Redone for Linux 2.1
+ * Copyright (C) 1997 Carnegie Mellon University
+ *
+ * Carnegie Mellon University encourages users of this code to contribute
+ * improvements to the Coda project. Contact Peter Braam <coda@cs.cmu.edu>.
+ */
+
+#include <asm/system.h>
+#include <linux/signal.h>
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/time.h>
+#include <linux/fs.h>
+#include <linux/file.h>
+#include <linux/stat.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <asm/uaccess.h>
+#include <linux/vmalloc.h>
+#include <linux/vfs.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>
+#include <linux/coda_proc.h> 
+
+#define upc_alloc() kmalloc(sizeof(struct upc_req), GFP_KERNEL)
+#define upc_free(r) kfree(r)
+
+static int coda_upcall(struct coda_sb_info *mntinfo, int inSize, int *outSize, 
+		       union inputArgs *buffer);
+
+static void *alloc_upcall(int opcode, int size)
+{
+	union inputArgs *inp;
+
+	CODA_ALLOC(inp, union inputArgs *, size);
+        if (!inp)
+		return ERR_PTR(-ENOMEM);
+
+        inp->ih.opcode = opcode;
+	inp->ih.pid = current->pid;
+	inp->ih.pgid = process_group(current);
+#ifdef CONFIG_CODA_FS_OLD_API
+	memset(&inp->ih.cred, 0, sizeof(struct coda_cred));
+	inp->ih.cred.cr_fsuid = current->fsuid;
+#else
+	inp->ih.uid = current->fsuid;
+#endif
+	return (void*)inp;
+}
+
+#define UPARG(op)\
+do {\
+	inp = (union inputArgs *)alloc_upcall(op, insize); \
+        if (IS_ERR(inp)) { return PTR_ERR(inp); }\
+        outp = (union outputArgs *)(inp); \
+        outsize = insize; \
+} while (0)
+
+#define INSIZE(tag) sizeof(struct coda_ ## tag ## _in)
+#define OUTSIZE(tag) sizeof(struct coda_ ## tag ## _out)
+#define SIZE(tag)  max_t(unsigned int, INSIZE(tag), OUTSIZE(tag))
+
+
+/* the upcalls */
+int venus_rootfid(struct super_block *sb, struct CodaFid *fidp)
+{
+        union inputArgs *inp;
+        union outputArgs *outp;
+        int insize, outsize, error;
+
+        insize = SIZE(root);
+        UPARG(CODA_ROOT);
+
+	error = coda_upcall(coda_sbp(sb), insize, &outsize, inp);
+	
+	if (error) {
+	        printk("coda_get_rootfid: error %d\n", error);
+	} else {
+		*fidp = outp->coda_root.VFid;
+	}
+
+	CODA_FREE(inp, insize);
+	return error;
+}
+
+int venus_getattr(struct super_block *sb, struct CodaFid *fid, 
+		     struct coda_vattr *attr) 
+{
+        union inputArgs *inp;
+        union outputArgs *outp;
+        int insize, outsize, error;
+
+        insize = SIZE(getattr); 
+	UPARG(CODA_GETATTR);
+        inp->coda_getattr.VFid = *fid;
+
+        error = coda_upcall(coda_sbp(sb), insize, &outsize, inp);
+	
+	*attr = outp->coda_getattr.attr;
+
+	CODA_FREE(inp, insize);
+        return error;
+}
+
+int venus_setattr(struct super_block *sb, struct CodaFid *fid, 
+		  struct coda_vattr *vattr)
+{
+        union inputArgs *inp;
+        union outputArgs *outp;
+        int insize, outsize, error;
+	
+	insize = SIZE(setattr);
+	UPARG(CODA_SETATTR);
+
+        inp->coda_setattr.VFid = *fid;
+	inp->coda_setattr.attr = *vattr;
+
+        error = coda_upcall(coda_sbp(sb), insize, &outsize, inp);
+
+        CODA_FREE(inp, insize);
+        return error;
+}
+
+int venus_lookup(struct super_block *sb, struct CodaFid *fid, 
+		    const char *name, int length, int * type, 
+		    struct CodaFid *resfid)
+{
+        union inputArgs *inp;
+        union outputArgs *outp;
+        int insize, outsize, error;
+	int offset;
+
+	offset = INSIZE(lookup);
+        insize = max_t(unsigned int, offset + length +1, OUTSIZE(lookup));
+	UPARG(CODA_LOOKUP);
+
+        inp->coda_lookup.VFid = *fid;
+	inp->coda_lookup.name = offset;
+	inp->coda_lookup.flags = CLU_CASE_SENSITIVE;
+        /* send Venus a null terminated string */
+        memcpy((char *)(inp) + offset, name, length);
+        *((char *)inp + offset + length) = '\0';
+
+        error = coda_upcall(coda_sbp(sb), insize, &outsize, inp);
+
+	*resfid = outp->coda_lookup.VFid;
+	*type = outp->coda_lookup.vtype;
+
+	CODA_FREE(inp, insize);
+	return error;
+}
+
+int venus_store(struct super_block *sb, struct CodaFid *fid, int flags,
+                vuid_t uid)
+{
+        union inputArgs *inp;
+        union outputArgs *outp;
+        int insize, outsize, error;
+#ifdef CONFIG_CODA_FS_OLD_API
+	struct coda_cred cred = { 0, };
+	cred.cr_fsuid = uid;
+#endif
+	
+	insize = SIZE(store);
+	UPARG(CODA_STORE);
+	
+#ifdef CONFIG_CODA_FS_OLD_API
+	memcpy(&(inp->ih.cred), &cred, sizeof(cred));
+#else
+	inp->ih.uid = uid;
+#endif
+	
+        inp->coda_store.VFid = *fid;
+        inp->coda_store.flags = flags;
+
+        error = coda_upcall(coda_sbp(sb), insize, &outsize, inp);
+
+	CODA_FREE(inp, insize);
+        return error;
+}
+
+int venus_release(struct super_block *sb, struct CodaFid *fid, int flags)
+{
+        union inputArgs *inp;
+        union outputArgs *outp;
+        int insize, outsize, error;
+	
+	insize = SIZE(release);
+	UPARG(CODA_RELEASE);
+	
+	inp->coda_release.VFid = *fid;
+	inp->coda_release.flags = flags;
+
+	error = coda_upcall(coda_sbp(sb), insize, &outsize, inp);
+
+	CODA_FREE(inp, insize);
+	return error;
+}
+
+int venus_close(struct super_block *sb, struct CodaFid *fid, int flags,
+                vuid_t uid)
+{
+	union inputArgs *inp;
+	union outputArgs *outp;
+	int insize, outsize, error;
+#ifdef CONFIG_CODA_FS_OLD_API
+	struct coda_cred cred = { 0, };
+	cred.cr_fsuid = uid;
+#endif
+	
+	insize = SIZE(release);
+	UPARG(CODA_CLOSE);
+	
+#ifdef CONFIG_CODA_FS_OLD_API
+	memcpy(&(inp->ih.cred), &cred, sizeof(cred));
+#else
+	inp->ih.uid = uid;
+#endif
+	
+        inp->coda_close.VFid = *fid;
+        inp->coda_close.flags = flags;
+
+        error = coda_upcall(coda_sbp(sb), insize, &outsize, inp);
+
+	CODA_FREE(inp, insize);
+        return error;
+}
+
+int venus_open(struct super_block *sb, struct CodaFid *fid,
+		  int flags, struct file **fh)
+{
+        union inputArgs *inp;
+        union outputArgs *outp;
+        int insize, outsize, error;
+       
+	insize = SIZE(open_by_fd);
+	UPARG(CODA_OPEN_BY_FD);
+
+        inp->coda_open.VFid = *fid;
+        inp->coda_open.flags = flags;
+
+        error = coda_upcall(coda_sbp(sb), insize, &outsize, inp);
+
+	*fh = outp->coda_open_by_fd.fh;
+
+	CODA_FREE(inp, insize);
+	return error;
+}	
+
+int venus_mkdir(struct super_block *sb, struct CodaFid *dirfid, 
+		   const char *name, int length, 
+		   struct CodaFid *newfid, struct coda_vattr *attrs)
+{
+        union inputArgs *inp;
+        union outputArgs *outp;
+        int insize, outsize, error;
+        int offset;
+
+	offset = INSIZE(mkdir);
+	insize = max_t(unsigned int, offset + length + 1, OUTSIZE(mkdir));
+	UPARG(CODA_MKDIR);
+
+        inp->coda_mkdir.VFid = *dirfid;
+        inp->coda_mkdir.attr = *attrs;
+	inp->coda_mkdir.name = offset;
+        /* Venus must get null terminated string */
+        memcpy((char *)(inp) + offset, name, length);
+        *((char *)inp + offset + length) = '\0';
+        
+        error = coda_upcall(coda_sbp(sb), insize, &outsize, inp);
+
+	*attrs = outp->coda_mkdir.attr;
+	*newfid = outp->coda_mkdir.VFid;
+
+	CODA_FREE(inp, insize);
+	return error;        
+}
+
+
+int venus_rename(struct super_block *sb, struct CodaFid *old_fid, 
+		 struct CodaFid *new_fid, size_t old_length, 
+		 size_t new_length, const char *old_name, 
+		 const char *new_name)
+{
+	union inputArgs *inp;
+        union outputArgs *outp;
+        int insize, outsize, error; 
+	int offset, s;
+	
+	offset = INSIZE(rename);
+	insize = max_t(unsigned int, offset + new_length + old_length + 8,
+		     OUTSIZE(rename)); 
+ 	UPARG(CODA_RENAME);
+
+        inp->coda_rename.sourceFid = *old_fid;
+        inp->coda_rename.destFid =  *new_fid;
+        inp->coda_rename.srcname = offset;
+
+        /* Venus must receive an null terminated string */
+        s = ( old_length & ~0x3) +4; /* round up to word boundary */
+        memcpy((char *)(inp) + offset, old_name, old_length);
+        *((char *)inp + offset + old_length) = '\0';
+
+        /* another null terminated string for Venus */
+        offset += s;
+        inp->coda_rename.destname = offset;
+        s = ( new_length & ~0x3) +4; /* round up to word boundary */
+        memcpy((char *)(inp) + offset, new_name, new_length);
+        *((char *)inp + offset + new_length) = '\0';
+
+        error = coda_upcall(coda_sbp(sb), insize, &outsize, inp);
+
+	CODA_FREE(inp, insize);
+	return error;
+}
+
+int venus_create(struct super_block *sb, struct CodaFid *dirfid, 
+		 const char *name, int length, int excl, int mode,
+		 struct CodaFid *newfid, struct coda_vattr *attrs) 
+{
+        union inputArgs *inp;
+        union outputArgs *outp;
+        int insize, outsize, error;
+        int offset;
+
+        offset = INSIZE(create);
+	insize = max_t(unsigned int, offset + length + 1, OUTSIZE(create));
+	UPARG(CODA_CREATE);
+
+        inp->coda_create.VFid = *dirfid;
+        inp->coda_create.attr.va_mode = mode;
+	inp->coda_create.excl = excl;
+        inp->coda_create.mode = mode;
+        inp->coda_create.name = offset;
+
+        /* Venus must get null terminated string */
+        memcpy((char *)(inp) + offset, name, length);
+        *((char *)inp + offset + length) = '\0';
+                
+        error = coda_upcall(coda_sbp(sb), insize, &outsize, inp);
+
+	*attrs = outp->coda_create.attr;
+	*newfid = outp->coda_create.VFid;
+
+	CODA_FREE(inp, insize);
+	return error;        
+}
+
+int venus_rmdir(struct super_block *sb, struct CodaFid *dirfid, 
+		    const char *name, int length)
+{
+        union inputArgs *inp;
+        union outputArgs *outp;
+        int insize, outsize, error;
+        int offset;
+
+        offset = INSIZE(rmdir);
+	insize = max_t(unsigned int, offset + length + 1, OUTSIZE(rmdir));
+	UPARG(CODA_RMDIR);
+
+        inp->coda_rmdir.VFid = *dirfid;
+        inp->coda_rmdir.name = offset;
+        memcpy((char *)(inp) + offset, name, length);
+	*((char *)inp + offset + length) = '\0';
+        
+        error = coda_upcall(coda_sbp(sb), insize, &outsize, inp);
+
+	CODA_FREE(inp, insize);
+	return error;
+}
+
+int venus_remove(struct super_block *sb, struct CodaFid *dirfid, 
+		    const char *name, int length)
+{
+        union inputArgs *inp;
+        union outputArgs *outp;
+        int error=0, insize, outsize, offset;
+
+        offset = INSIZE(remove);
+	insize = max_t(unsigned int, offset + length + 1, OUTSIZE(remove));
+	UPARG(CODA_REMOVE);
+
+        inp->coda_remove.VFid = *dirfid;
+        inp->coda_remove.name = offset;
+        memcpy((char *)(inp) + offset, name, length);
+	*((char *)inp + offset + length) = '\0';
+        
+        error = coda_upcall(coda_sbp(sb), insize, &outsize, inp);
+
+	CODA_FREE(inp, insize);
+	return error;
+}
+
+int venus_readlink(struct super_block *sb, struct CodaFid *fid, 
+		      char *buffer, int *length)
+{ 
+        union inputArgs *inp;
+        union outputArgs *outp;
+        int insize, outsize, error;
+        int retlen;
+        char *result;
+        
+	insize = max_t(unsigned int,
+		     INSIZE(readlink), OUTSIZE(readlink)+ *length + 1);
+	UPARG(CODA_READLINK);
+
+        inp->coda_readlink.VFid = *fid;
+    
+        error =  coda_upcall(coda_sbp(sb), insize, &outsize, inp);
+	
+	if (! error) {
+                retlen = outp->coda_readlink.count;
+		if ( retlen > *length )
+		        retlen = *length;
+		*length = retlen;
+		result =  (char *)outp + (long)outp->coda_readlink.data;
+		memcpy(buffer, result, retlen);
+		*(buffer + retlen) = '\0';
+	}
+        
+        CODA_FREE(inp, insize);
+        return error;
+}
+
+
+
+int venus_link(struct super_block *sb, struct CodaFid *fid, 
+		  struct CodaFid *dirfid, const char *name, int len )
+{
+        union inputArgs *inp;
+        union outputArgs *outp;
+        int insize, outsize, error;
+        int offset;
+
+	offset = INSIZE(link);
+	insize = max_t(unsigned int, offset  + len + 1, OUTSIZE(link));
+        UPARG(CODA_LINK);
+
+        inp->coda_link.sourceFid = *fid;
+        inp->coda_link.destFid = *dirfid;
+        inp->coda_link.tname = offset;
+
+        /* make sure strings are null terminated */
+        memcpy((char *)(inp) + offset, name, len);
+        *((char *)inp + offset + len) = '\0';
+        
+        error = coda_upcall(coda_sbp(sb), insize, &outsize, inp);
+
+	CODA_FREE(inp, insize);
+        return error;
+}
+
+int venus_symlink(struct super_block *sb, struct CodaFid *fid,
+		     const char *name, int len,
+		     const char *symname, int symlen)
+{
+        union inputArgs *inp;
+        union outputArgs *outp;
+        int insize, outsize, error;
+        int offset, s;
+
+        offset = INSIZE(symlink);
+	insize = max_t(unsigned int, offset + len + symlen + 8, OUTSIZE(symlink));
+	UPARG(CODA_SYMLINK);
+        
+        /*        inp->coda_symlink.attr = *tva; XXXXXX */ 
+        inp->coda_symlink.VFid = *fid;
+
+	/* Round up to word boundary and null terminate */
+        inp->coda_symlink.srcname = offset;
+        s = ( symlen  & ~0x3 ) + 4; 
+        memcpy((char *)(inp) + offset, symname, symlen);
+        *((char *)inp + offset + symlen) = '\0';
+        
+	/* Round up to word boundary and null terminate */
+        offset += s;
+        inp->coda_symlink.tname = offset;
+        s = (len & ~0x3) + 4;
+        memcpy((char *)(inp) + offset, name, len);
+        *((char *)inp + offset + len) = '\0';
+
+	error = coda_upcall(coda_sbp(sb), insize, &outsize, inp);
+
+	CODA_FREE(inp, insize);
+        return error;
+}
+
+int venus_fsync(struct super_block *sb, struct CodaFid *fid)
+{
+        union inputArgs *inp;
+        union outputArgs *outp; 
+	int insize, outsize, error;
+	
+	insize=SIZE(fsync);
+	UPARG(CODA_FSYNC);
+
+        inp->coda_fsync.VFid = *fid;
+        error = coda_upcall(coda_sbp(sb), sizeof(union inputArgs), 
+                            &outsize, inp);
+
+	CODA_FREE(inp, insize);
+	return error;
+}
+
+int venus_access(struct super_block *sb, struct CodaFid *fid, int mask)
+{
+        union inputArgs *inp;
+        union outputArgs *outp; 
+	int insize, outsize, error;
+
+	insize = SIZE(access);
+	UPARG(CODA_ACCESS);
+
+        inp->coda_access.VFid = *fid;
+        inp->coda_access.flags = mask;
+
+	error = coda_upcall(coda_sbp(sb), insize, &outsize, inp);
+
+	CODA_FREE(inp, insize);
+	return error;
+}
+
+
+int venus_pioctl(struct super_block *sb, struct CodaFid *fid,
+		 unsigned int cmd, struct PioctlData *data)
+{
+        union inputArgs *inp;
+        union outputArgs *outp;  
+	int insize, outsize, error;
+	int iocsize;
+
+	insize = VC_MAXMSGSIZE;
+	UPARG(CODA_IOCTL);
+
+        /* build packet for Venus */
+        if (data->vi.in_size > VC_MAXDATASIZE) {
+		error = -EINVAL;
+		goto exit;
+        }
+
+        if (data->vi.out_size > VC_MAXDATASIZE) {
+		error = -EINVAL;
+		goto exit;
+	}
+
+        inp->coda_ioctl.VFid = *fid;
+    
+        /* the cmd field was mutated by increasing its size field to
+         * reflect the path and follow args. We need to subtract that
+         * out before sending the command to Venus.  */
+        inp->coda_ioctl.cmd = (cmd & ~(PIOCPARM_MASK << 16));	
+        iocsize = ((cmd >> 16) & PIOCPARM_MASK) - sizeof(char *) - sizeof(int);
+        inp->coda_ioctl.cmd |= (iocsize & PIOCPARM_MASK) <<	16;	
+    
+        /* in->coda_ioctl.rwflag = flag; */
+        inp->coda_ioctl.len = data->vi.in_size;
+        inp->coda_ioctl.data = (char *)(INSIZE(ioctl));
+     
+        /* get the data out of user space */
+        if ( copy_from_user((char*)inp + (long)inp->coda_ioctl.data,
+			    data->vi.in, data->vi.in_size) ) {
+		error = -EINVAL;
+	        goto exit;
+	}
+
+        error = coda_upcall(coda_sbp(sb), SIZE(ioctl) + data->vi.in_size,
+                            &outsize, inp);
+        
+        if (error) {
+	        printk("coda_pioctl: Venus returns: %d for %s\n", 
+		       error, coda_f2s(fid));
+		goto exit; 
+	}
+
+	if (outsize < (long)outp->coda_ioctl.data + outp->coda_ioctl.len) {
+		error = -EINVAL;
+		goto exit;
+	}
+        
+	/* Copy out the OUT buffer. */
+        if (outp->coda_ioctl.len > data->vi.out_size) {
+		error = -EINVAL;
+		goto exit;
+        }
+
+	/* Copy out the OUT buffer. */
+	if (copy_to_user(data->vi.out,
+			 (char *)outp + (long)outp->coda_ioctl.data,
+			 outp->coda_ioctl.len)) {
+		error = -EFAULT;
+		goto exit;
+	}
+
+ exit:
+	CODA_FREE(inp, insize);
+	return error;
+}
+
+int venus_statfs(struct super_block *sb, struct kstatfs *sfs) 
+{ 
+        union inputArgs *inp;
+        union outputArgs *outp;
+        int insize, outsize, error;
+        
+	insize = max_t(unsigned int, INSIZE(statfs), OUTSIZE(statfs));
+	UPARG(CODA_STATFS);
+
+        error = coda_upcall(coda_sbp(sb), insize, &outsize, inp);
+	
+        if (!error) {
+		sfs->f_blocks = outp->coda_statfs.stat.f_blocks;
+		sfs->f_bfree  = outp->coda_statfs.stat.f_bfree;
+		sfs->f_bavail = outp->coda_statfs.stat.f_bavail;
+		sfs->f_files  = outp->coda_statfs.stat.f_files;
+		sfs->f_ffree  = outp->coda_statfs.stat.f_ffree;
+	} else {
+		printk("coda_statfs: Venus returns: %d\n", error);
+	}
+
+        CODA_FREE(inp, insize);
+        return error;
+}
+
+/*
+ * coda_upcall and coda_downcall routines.
+ * 
+ */
+
+static inline void coda_waitfor_upcall(struct upc_req *vmp,
+				       struct venus_comm *vcommp)
+{
+	DECLARE_WAITQUEUE(wait, current);
+
+	vmp->uc_posttime = jiffies;
+
+	add_wait_queue(&vmp->uc_sleep, &wait);
+	for (;;) {
+		if ( !coda_hard && vmp->uc_opcode != CODA_CLOSE ) 
+			set_current_state(TASK_INTERRUPTIBLE);
+		else
+			set_current_state(TASK_UNINTERRUPTIBLE);
+
+                /* venus died */
+                if ( !vcommp->vc_inuse )
+                        break;
+
+		/* got a reply */
+		if ( vmp->uc_flags & ( REQ_WRITE | REQ_ABORT ) )
+			break;
+
+		if ( !coda_hard && vmp->uc_opcode != CODA_CLOSE && signal_pending(current) ) {
+			/* if this process really wants to die, let it go */
+			if ( sigismember(&(current->pending.signal), SIGKILL) ||
+			     sigismember(&(current->pending.signal), SIGINT) )
+				break;
+			/* signal is present: after timeout always return 
+			   really smart idea, probably useless ... */
+			if ( jiffies - vmp->uc_posttime > coda_timeout * HZ )
+				break; 
+		}
+		schedule();
+	}
+	remove_wait_queue(&vmp->uc_sleep, &wait);
+	set_current_state(TASK_RUNNING);
+
+	return;
+}
+
+
+/* 
+ * coda_upcall will return an error in the case of 
+ * failed communication with Venus _or_ will peek at Venus
+ * reply and return Venus' error.
+ *
+ * As venus has 2 types of errors, normal errors (positive) and internal
+ * errors (negative), normal errors are negated, while internal errors
+ * are all mapped to -EINTR, while showing a nice warning message. (jh)
+ * 
+ */
+static int coda_upcall(struct coda_sb_info *sbi, 
+		int inSize, int *outSize, 
+		union inputArgs *buffer) 
+{
+	struct venus_comm *vcommp;
+	union outputArgs *out;
+	struct upc_req *req;
+	int error = 0;
+
+	vcommp = sbi->sbi_vcomm;
+	if ( !vcommp->vc_inuse ) {
+		printk("No pseudo device in upcall comms at %p\n", vcommp);
+                return -ENXIO;
+	}
+
+	/* Format the request message. */
+	req = upc_alloc();
+	if (!req) {
+		printk("Failed to allocate upc_req structure\n");
+		return -ENOMEM;
+	}
+	req->uc_data = (void *)buffer;
+	req->uc_flags = 0;
+	req->uc_inSize = inSize;
+	req->uc_outSize = *outSize ? *outSize : inSize;
+	req->uc_opcode = ((union inputArgs *)buffer)->ih.opcode;
+	req->uc_unique = ++vcommp->vc_seq;
+	init_waitqueue_head(&req->uc_sleep);
+	
+	/* Fill in the common input args. */
+	((union inputArgs *)buffer)->ih.unique = req->uc_unique;
+
+	/* Append msg to pending queue and poke Venus. */
+	list_add(&(req->uc_chain), vcommp->vc_pending.prev);
+        
+	wake_up_interruptible(&vcommp->vc_waitq);
+	/* We can be interrupted while we wait for Venus to process
+	 * our request.  If the interrupt occurs before Venus has read
+	 * the request, we dequeue and return. If it occurs after the
+	 * read but before the reply, we dequeue, send a signal
+	 * message, and return. If it occurs after the reply we ignore
+	 * it. In no case do we want to restart the syscall.  If it
+	 * was interrupted by a venus shutdown (psdev_close), return
+	 * ENODEV.  */
+
+	/* Go to sleep.  Wake up on signals only after the timeout. */
+	coda_waitfor_upcall(req, vcommp);
+
+	if (vcommp->vc_inuse) {      /* i.e. Venus is still alive */
+	    /* Op went through, interrupt or not... */
+	    if (req->uc_flags & REQ_WRITE) {
+		out = (union outputArgs *)req->uc_data;
+		/* here we map positive Venus errors to kernel errors */
+		error = -out->oh.result;
+		*outSize = req->uc_outSize;
+		goto exit;
+	    }
+	    if ( !(req->uc_flags & REQ_READ) && signal_pending(current)) { 
+		/* Interrupted before venus read it. */
+		list_del(&(req->uc_chain));
+		/* perhaps the best way to convince the app to
+		   give up? */
+		error = -EINTR;
+		goto exit;
+	    } 
+	    if ( (req->uc_flags & REQ_READ) && signal_pending(current) ) {
+		    /* interrupted after Venus did its read, send signal */
+		    union inputArgs *sig_inputArgs;
+		    struct upc_req *sig_req;
+		    
+		    list_del(&(req->uc_chain));
+		    error = -ENOMEM;
+		    sig_req = upc_alloc();
+		    if (!sig_req) goto exit;
+
+		    CODA_ALLOC((sig_req->uc_data), char *, sizeof(struct coda_in_hdr));
+		    if (!sig_req->uc_data) {
+			upc_free(sig_req);
+			goto exit;
+		    }
+		    
+		    error = -EINTR;
+		    sig_inputArgs = (union inputArgs *)sig_req->uc_data;
+		    sig_inputArgs->ih.opcode = CODA_SIGNAL;
+		    sig_inputArgs->ih.unique = req->uc_unique;
+		    
+		    sig_req->uc_flags = REQ_ASYNC;
+		    sig_req->uc_opcode = sig_inputArgs->ih.opcode;
+		    sig_req->uc_unique = sig_inputArgs->ih.unique;
+		    sig_req->uc_inSize = sizeof(struct coda_in_hdr);
+		    sig_req->uc_outSize = sizeof(struct coda_in_hdr);
+		    
+		    /* insert at head of queue! */
+		    list_add(&(sig_req->uc_chain), &vcommp->vc_pending);
+		    wake_up_interruptible(&vcommp->vc_waitq);
+	    } else {
+		    printk("Coda: Strange interruption..\n");
+		    error = -EINTR;
+	    }
+	} else {	/* If venus died i.e. !VC_OPEN(vcommp) */
+	        printk("coda_upcall: Venus dead on (op,un) (%d.%d) flags %d\n",
+		       req->uc_opcode, req->uc_unique, req->uc_flags);
+		error = -ENODEV;
+	}
+
+ exit:
+	upc_free(req);
+	return error;
+}
+
+/*  
+    The statements below are part of the Coda opportunistic
+    programming -- taken from the Mach/BSD kernel code for Coda. 
+    You don't get correct semantics by stating what needs to be
+    done without guaranteeing the invariants needed for it to happen.
+    When will be have time to find out what exactly is going on?  (pjb)
+*/
+
+
+/* 
+ * There are 7 cases where cache invalidations occur.  The semantics
+ *  of each is listed here:
+ *
+ * CODA_FLUSH     -- flush all entries from the name cache and the cnode cache.
+ * CODA_PURGEUSER -- flush all entries from the name cache for a specific user
+ *                  This call is a result of token expiration.
+ *
+ * The next arise as the result of callbacks on a file or directory.
+ * CODA_ZAPFILE   -- flush the cached attributes for a file.
+
+ * CODA_ZAPDIR    -- flush the attributes for the dir and
+ *                  force a new lookup for all the children
+                    of this dir.
+
+ *
+ * The next is a result of Venus detecting an inconsistent file.
+ * CODA_PURGEFID  -- flush the attribute for the file
+ *                  purge it and its children from the dcache
+ *
+ * The last  allows Venus to replace local fids with global ones
+ * during reintegration.
+ *
+ * CODA_REPLACE -- replace one CodaFid with another throughout the name cache */
+
+int coda_downcall(int opcode, union outputArgs * out, struct super_block *sb)
+{
+	/* Handle invalidation requests. */
+          if ( !sb || !sb->s_root || !sb->s_root->d_inode)
+		  return 0; 
+
+	  switch (opcode) {
+
+	  case CODA_FLUSH : {
+		   coda_cache_clear_all(sb);
+		   shrink_dcache_sb(sb);
+		   coda_flag_inode(sb->s_root->d_inode, C_FLUSH);
+		   return(0);
+	  }
+
+	  case CODA_PURGEUSER : {
+		   coda_cache_clear_all(sb);
+		   return(0);
+	  }
+
+	  case CODA_ZAPDIR : {
+	          struct inode *inode;
+		  struct CodaFid *fid = &out->coda_zapdir.CodaFid;
+
+		  inode = coda_fid_to_inode(fid, sb);
+		  if (inode) {
+			  coda_flag_inode_children(inode, C_PURGE);
+	                  coda_flag_inode(inode, C_VATTR);
+			  iput(inode);
+		  }
+		  
+		  return(0);
+	  }
+
+	  case CODA_ZAPFILE : {
+	          struct inode *inode;
+		  struct CodaFid *fid = &out->coda_zapfile.CodaFid;
+		  inode = coda_fid_to_inode(fid, sb);
+		  if ( inode ) {
+	                  coda_flag_inode(inode, C_VATTR);
+			  iput(inode);
+		  }
+		  return 0;
+	  }
+
+	  case CODA_PURGEFID : {
+	          struct inode *inode;
+		  struct CodaFid *fid = &out->coda_purgefid.CodaFid;
+		  inode = coda_fid_to_inode(fid, sb);
+		  if ( inode ) { 
+			coda_flag_inode_children(inode, C_PURGE);
+
+			/* catch the dentries later if some are still busy */
+			coda_flag_inode(inode, C_PURGE);
+			d_prune_aliases(inode);
+
+			iput(inode);
+		  }
+		  return 0;
+	  }
+
+	  case CODA_REPLACE : {
+	          struct inode *inode;
+		  struct CodaFid *oldfid = &out->coda_replace.OldFid;
+		  struct CodaFid *newfid = &out->coda_replace.NewFid;
+		  inode = coda_fid_to_inode(oldfid, sb);
+		  if ( inode ) { 
+			  coda_replace_fid(inode, oldfid, newfid);
+			  iput(inode);
+		  }
+		  return 0;
+	  }
+	  }
+	  return 0;
+}
+