mirror of
				https://git.kernel.org/pub/scm/linux/kernel/git/chenhuacai/linux-loongson
				synced 2025-10-31 14:30:50 +00:00 
			
		
		
		
	 8e13059a37
			
		
	
	
		8e13059a37
		
	
	
	
	
		
			
			This patch converts list_add(A, B.prev) to list_add_tail(A, &B) for readability. Acked-by: Karsten Keil <kkeil@suse.de> Cc: Jan Harkes <jaharkes@cs.cmu.edu> Acked-by: Jan Kara <jack@suse.cz> AOLed-by: David Woodhouse <dwmw2@infradead.org> Cc: Sridhar Samudrala <sri@us.ibm.com> Signed-off-by: Akinobu Mita <mita@miraclelinux.com> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
		
			
				
	
	
		
			915 lines
		
	
	
		
			24 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			915 lines
		
	
	
		
			24 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/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 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_sbp(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;
 | |
| 	} 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_tail(&(req->uc_chain), &vcommp->vc_pending);
 | |
|         
 | |
| 	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;
 | |
| }
 | |
| 
 |