d3fec424b2
This is an variation on the patch sent by Christoph Hellwig which kills file_count abuse by the Coda kernel module by moving the coda_flush functionality into coda_release. However part of reason we were using the coda_flush callback was to allow Coda to pass errors that occur during writeback from the userspace cache manager back to close(). As Al Viro explained on linux-fsdevel, it is impossible to guarantee that such errors can in fact be returned back to the caller. There are many cases where the last reference to a file is not released by the close system call and it is also impossible to pick some close as a 'last-close' and delay it until all other references have been destroyed. The CODA_STORE/CODA_RELEASE upcall combination is clearly a broken design, and it is better to remove it completely. Signed-off-by: Jan Harkes <jaharkes@cs.cmu.edu> Cc: Christoph Hellwig <hch@lst.de> Cc: Al Viro <viro@ftp.linux.org.uk> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
876 lines
23 KiB
C
876 lines
23 KiB
C
/*
|
|
* 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/sched.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 "coda_int.h"
|
|
|
|
static int coda_upcall(struct venus_comm *vc, 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_vcp(sb), insize, &outsize, inp);
|
|
if (!error)
|
|
*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_vcp(sb), insize, &outsize, inp);
|
|
if (!error)
|
|
*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_vcp(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_vcp(sb), insize, &outsize, inp);
|
|
if (!error) {
|
|
*resfid = outp->coda_lookup.VFid;
|
|
*type = outp->coda_lookup.vtype;
|
|
}
|
|
|
|
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_vcp(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_by_fd.VFid = *fid;
|
|
inp->coda_open_by_fd.flags = flags;
|
|
|
|
error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);
|
|
if (!error)
|
|
*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_vcp(sb), insize, &outsize, inp);
|
|
if (!error) {
|
|
*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_vcp(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_vcp(sb), insize, &outsize, inp);
|
|
if (!error) {
|
|
*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_vcp(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_vcp(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_vcp(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_vcp(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_vcp(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_vcp(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_vcp(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_vcp(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 dentry *dentry, 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_vcp(dentry->d_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;
|
|
}
|
|
|
|
CODA_FREE(inp, insize);
|
|
return error;
|
|
}
|
|
|
|
/*
|
|
* coda_upcall and coda_downcall routines.
|
|
*/
|
|
static void coda_block_signals(sigset_t *old)
|
|
{
|
|
spin_lock_irq(¤t->sighand->siglock);
|
|
*old = current->blocked;
|
|
|
|
sigfillset(¤t->blocked);
|
|
sigdelset(¤t->blocked, SIGKILL);
|
|
sigdelset(¤t->blocked, SIGSTOP);
|
|
sigdelset(¤t->blocked, SIGINT);
|
|
|
|
recalc_sigpending();
|
|
spin_unlock_irq(¤t->sighand->siglock);
|
|
}
|
|
|
|
static void coda_unblock_signals(sigset_t *old)
|
|
{
|
|
spin_lock_irq(¤t->sighand->siglock);
|
|
current->blocked = *old;
|
|
recalc_sigpending();
|
|
spin_unlock_irq(¤t->sighand->siglock);
|
|
}
|
|
|
|
/* Don't allow signals to interrupt the following upcalls before venus
|
|
* has seen them,
|
|
* - CODA_CLOSE or CODA_RELEASE upcall (to avoid reference count problems)
|
|
* - CODA_STORE (to avoid data loss)
|
|
*/
|
|
#define CODA_INTERRUPTIBLE(r) (!coda_hard && \
|
|
(((r)->uc_opcode != CODA_CLOSE && \
|
|
(r)->uc_opcode != CODA_STORE && \
|
|
(r)->uc_opcode != CODA_RELEASE) || \
|
|
(r)->uc_flags & REQ_READ))
|
|
|
|
static inline void coda_waitfor_upcall(struct upc_req *req)
|
|
{
|
|
DECLARE_WAITQUEUE(wait, current);
|
|
unsigned long timeout = jiffies + coda_timeout * HZ;
|
|
sigset_t old;
|
|
int blocked;
|
|
|
|
coda_block_signals(&old);
|
|
blocked = 1;
|
|
|
|
add_wait_queue(&req->uc_sleep, &wait);
|
|
for (;;) {
|
|
if (CODA_INTERRUPTIBLE(req))
|
|
set_current_state(TASK_INTERRUPTIBLE);
|
|
else
|
|
set_current_state(TASK_UNINTERRUPTIBLE);
|
|
|
|
/* got a reply */
|
|
if (req->uc_flags & (REQ_WRITE | REQ_ABORT))
|
|
break;
|
|
|
|
if (blocked && time_after(jiffies, timeout) &&
|
|
CODA_INTERRUPTIBLE(req))
|
|
{
|
|
coda_unblock_signals(&old);
|
|
blocked = 0;
|
|
}
|
|
|
|
if (signal_pending(current)) {
|
|
list_del(&req->uc_chain);
|
|
break;
|
|
}
|
|
|
|
if (blocked)
|
|
schedule_timeout(HZ);
|
|
else
|
|
schedule();
|
|
}
|
|
if (blocked)
|
|
coda_unblock_signals(&old);
|
|
|
|
remove_wait_queue(&req->uc_sleep, &wait);
|
|
set_current_state(TASK_RUNNING);
|
|
}
|
|
|
|
|
|
/*
|
|
* 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 venus_comm *vcp,
|
|
int inSize, int *outSize,
|
|
union inputArgs *buffer)
|
|
{
|
|
union outputArgs *out;
|
|
union inputArgs *sig_inputArgs;
|
|
struct upc_req *req, *sig_req;
|
|
int error = 0;
|
|
|
|
if (!vcp->vc_inuse) {
|
|
printk(KERN_NOTICE "coda: Venus dead, not sending upcall\n");
|
|
return -ENXIO;
|
|
}
|
|
|
|
/* Format the request message. */
|
|
req = kmalloc(sizeof(struct upc_req), GFP_KERNEL);
|
|
if (!req)
|
|
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 = ++vcp->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_tail(&req->uc_chain, &vcp->vc_pending);
|
|
|
|
wake_up_interruptible(&vcp->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);
|
|
|
|
/* 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;
|
|
}
|
|
|
|
error = -EINTR;
|
|
if ((req->uc_flags & REQ_ABORT) || !signal_pending(current)) {
|
|
printk(KERN_WARNING "coda: Unexpected interruption.\n");
|
|
goto exit;
|
|
}
|
|
|
|
/* Interrupted before venus read it. */
|
|
if (!(req->uc_flags & REQ_READ))
|
|
goto exit;
|
|
|
|
/* Venus saw the upcall, make sure we can send interrupt signal */
|
|
if (!vcp->vc_inuse) {
|
|
printk(KERN_INFO "coda: Venus dead, not sending signal.\n");
|
|
goto exit;
|
|
}
|
|
|
|
error = -ENOMEM;
|
|
sig_req = kmalloc(sizeof(struct upc_req), GFP_KERNEL);
|
|
if (!sig_req) goto exit;
|
|
|
|
CODA_ALLOC((sig_req->uc_data), char *, sizeof(struct coda_in_hdr));
|
|
if (!sig_req->uc_data) {
|
|
kfree(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), &vcp->vc_pending);
|
|
wake_up_interruptible(&vcp->vc_waitq);
|
|
|
|
exit:
|
|
kfree(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)
|
|
{
|
|
struct inode *inode = NULL;
|
|
struct CodaFid *fid, *newfid;
|
|
|
|
/* Handle invalidation requests. */
|
|
if ( !sb || !sb->s_root)
|
|
return 0;
|
|
|
|
switch (opcode) {
|
|
case CODA_FLUSH:
|
|
coda_cache_clear_all(sb);
|
|
shrink_dcache_sb(sb);
|
|
if (sb->s_root->d_inode)
|
|
coda_flag_inode(sb->s_root->d_inode, C_FLUSH);
|
|
break;
|
|
|
|
case CODA_PURGEUSER:
|
|
coda_cache_clear_all(sb);
|
|
break;
|
|
|
|
case CODA_ZAPDIR:
|
|
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);
|
|
}
|
|
break;
|
|
|
|
case CODA_ZAPFILE:
|
|
fid = &out->coda_zapfile.CodaFid;
|
|
inode = coda_fid_to_inode(fid, sb);
|
|
if (inode)
|
|
coda_flag_inode(inode, C_VATTR);
|
|
break;
|
|
|
|
case CODA_PURGEFID:
|
|
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);
|
|
|
|
}
|
|
break;
|
|
|
|
case CODA_REPLACE:
|
|
fid = &out->coda_replace.OldFid;
|
|
newfid = &out->coda_replace.NewFid;
|
|
inode = coda_fid_to_inode(fid, sb);
|
|
if (inode)
|
|
coda_replace_fid(inode, fid, newfid);
|
|
break;
|
|
}
|
|
|
|
if (inode)
|
|
iput(inode);
|
|
|
|
return 0;
|
|
}
|
|
|