summaryrefslogtreecommitdiff
path: root/fs/coda/upcall.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/coda/upcall.c')
-rw-r--r--fs/coda/upcall.c914
1 files changed, 914 insertions, 0 deletions
diff --git a/fs/coda/upcall.c b/fs/coda/upcall.c
new file mode 100644
index 000000000000..1bae99650a91
--- /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;
+}
+