diff options
Diffstat (limited to 'fs/coda/upcall.c')
-rw-r--r-- | fs/coda/upcall.c | 914 |
1 files changed, 914 insertions, 0 deletions
diff --git a/fs/coda/upcall.c b/fs/coda/upcall.c new file mode 100644 index 00000000000..1bae99650a9 --- /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; +} + |