xref: /openbmc/linux/fs/coda/upcall.c (revision 3725e9dd)
11da177e4SLinus Torvalds /*
21da177e4SLinus Torvalds  * Mostly platform independent upcall operations to Venus:
31da177e4SLinus Torvalds  *  -- upcalls
41da177e4SLinus Torvalds  *  -- upcall routines
51da177e4SLinus Torvalds  *
61da177e4SLinus Torvalds  * Linux 2.0 version
71da177e4SLinus Torvalds  * Copyright (C) 1996 Peter J. Braam <braam@maths.ox.ac.uk>,
81da177e4SLinus Torvalds  * Michael Callahan <callahan@maths.ox.ac.uk>
91da177e4SLinus Torvalds  *
101da177e4SLinus Torvalds  * Redone for Linux 2.1
111da177e4SLinus Torvalds  * Copyright (C) 1997 Carnegie Mellon University
121da177e4SLinus Torvalds  *
131da177e4SLinus Torvalds  * Carnegie Mellon University encourages users of this code to contribute
141da177e4SLinus Torvalds  * improvements to the Coda project. Contact Peter Braam <coda@cs.cmu.edu>.
151da177e4SLinus Torvalds  */
161da177e4SLinus Torvalds 
171da177e4SLinus Torvalds #include <linux/signal.h>
18e8edc6e0SAlexey Dobriyan #include <linux/sched.h>
191da177e4SLinus Torvalds #include <linux/types.h>
201da177e4SLinus Torvalds #include <linux/kernel.h>
211da177e4SLinus Torvalds #include <linux/mm.h>
221da177e4SLinus Torvalds #include <linux/time.h>
231da177e4SLinus Torvalds #include <linux/fs.h>
241da177e4SLinus Torvalds #include <linux/file.h>
251da177e4SLinus Torvalds #include <linux/stat.h>
261da177e4SLinus Torvalds #include <linux/errno.h>
271da177e4SLinus Torvalds #include <linux/string.h>
285a0e3ad6STejun Heo #include <linux/slab.h>
29da47c19eSYoshihisa Abe #include <linux/mutex.h>
30834b46c3SFabian Frederick #include <linux/uaccess.h>
311da177e4SLinus Torvalds #include <linux/vmalloc.h>
321da177e4SLinus Torvalds #include <linux/vfs.h>
331da177e4SLinus Torvalds 
341da177e4SLinus Torvalds #include <linux/coda.h>
351da177e4SLinus Torvalds #include <linux/coda_psdev.h>
3631a203dfSAl Viro #include "coda_linux.h"
3731a203dfSAl Viro #include "coda_cache.h"
383cf01f28SJan Harkes 
393cf01f28SJan Harkes #include "coda_int.h"
401da177e4SLinus Torvalds 
41a1b0aa87SJan Harkes static int coda_upcall(struct venus_comm *vc, int inSize, int *outSize,
421da177e4SLinus Torvalds 		       union inputArgs *buffer);
431da177e4SLinus Torvalds 
441da177e4SLinus Torvalds static void *alloc_upcall(int opcode, int size)
451da177e4SLinus Torvalds {
461da177e4SLinus Torvalds 	union inputArgs *inp;
471da177e4SLinus Torvalds 
481da177e4SLinus Torvalds 	CODA_ALLOC(inp, union inputArgs *, size);
491da177e4SLinus Torvalds         if (!inp)
501da177e4SLinus Torvalds 		return ERR_PTR(-ENOMEM);
511da177e4SLinus Torvalds 
521da177e4SLinus Torvalds         inp->ih.opcode = opcode;
539fd973e0SEric W. Biederman 	inp->ih.pid = task_pid_nr_ns(current, &init_pid_ns);
549fd973e0SEric W. Biederman 	inp->ih.pgid = task_pgrp_nr_ns(current, &init_pid_ns);
55d83f5901SEric W. Biederman 	inp->ih.uid = from_kuid(&init_user_ns, current_fsuid());
56de0ca06aSAdrian Bunk 
571da177e4SLinus Torvalds 	return (void*)inp;
581da177e4SLinus Torvalds }
591da177e4SLinus Torvalds 
601da177e4SLinus Torvalds #define UPARG(op)\
611da177e4SLinus Torvalds do {\
621da177e4SLinus Torvalds 	inp = (union inputArgs *)alloc_upcall(op, insize); \
631da177e4SLinus Torvalds         if (IS_ERR(inp)) { return PTR_ERR(inp); }\
641da177e4SLinus Torvalds         outp = (union outputArgs *)(inp); \
651da177e4SLinus Torvalds         outsize = insize; \
661da177e4SLinus Torvalds } while (0)
671da177e4SLinus Torvalds 
681da177e4SLinus Torvalds #define INSIZE(tag) sizeof(struct coda_ ## tag ## _in)
691da177e4SLinus Torvalds #define OUTSIZE(tag) sizeof(struct coda_ ## tag ## _out)
701da177e4SLinus Torvalds #define SIZE(tag)  max_t(unsigned int, INSIZE(tag), OUTSIZE(tag))
711da177e4SLinus Torvalds 
721da177e4SLinus Torvalds 
731da177e4SLinus Torvalds /* the upcalls */
741da177e4SLinus Torvalds int venus_rootfid(struct super_block *sb, struct CodaFid *fidp)
751da177e4SLinus Torvalds {
761da177e4SLinus Torvalds         union inputArgs *inp;
771da177e4SLinus Torvalds         union outputArgs *outp;
781da177e4SLinus Torvalds         int insize, outsize, error;
791da177e4SLinus Torvalds 
801da177e4SLinus Torvalds         insize = SIZE(root);
811da177e4SLinus Torvalds         UPARG(CODA_ROOT);
821da177e4SLinus Torvalds 
83a1b0aa87SJan Harkes 	error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);
84970648ebSJan Harkes 	if (!error)
851da177e4SLinus Torvalds 		*fidp = outp->coda_root.VFid;
861da177e4SLinus Torvalds 
871da177e4SLinus Torvalds 	CODA_FREE(inp, insize);
881da177e4SLinus Torvalds 	return error;
891da177e4SLinus Torvalds }
901da177e4SLinus Torvalds 
911da177e4SLinus Torvalds int venus_getattr(struct super_block *sb, struct CodaFid *fid,
921da177e4SLinus Torvalds 		     struct coda_vattr *attr)
931da177e4SLinus Torvalds {
941da177e4SLinus Torvalds         union inputArgs *inp;
951da177e4SLinus Torvalds         union outputArgs *outp;
961da177e4SLinus Torvalds         int insize, outsize, error;
971da177e4SLinus Torvalds 
981da177e4SLinus Torvalds         insize = SIZE(getattr);
991da177e4SLinus Torvalds 	UPARG(CODA_GETATTR);
1001da177e4SLinus Torvalds         inp->coda_getattr.VFid = *fid;
1011da177e4SLinus Torvalds 
102a1b0aa87SJan Harkes 	error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);
103970648ebSJan Harkes 	if (!error)
1041da177e4SLinus Torvalds 		*attr = outp->coda_getattr.attr;
1051da177e4SLinus Torvalds 
1061da177e4SLinus Torvalds 	CODA_FREE(inp, insize);
1071da177e4SLinus Torvalds         return error;
1081da177e4SLinus Torvalds }
1091da177e4SLinus Torvalds 
1101da177e4SLinus Torvalds int venus_setattr(struct super_block *sb, struct CodaFid *fid,
1111da177e4SLinus Torvalds 		  struct coda_vattr *vattr)
1121da177e4SLinus Torvalds {
1131da177e4SLinus Torvalds         union inputArgs *inp;
1141da177e4SLinus Torvalds         union outputArgs *outp;
1151da177e4SLinus Torvalds         int insize, outsize, error;
1161da177e4SLinus Torvalds 
1171da177e4SLinus Torvalds 	insize = SIZE(setattr);
1181da177e4SLinus Torvalds 	UPARG(CODA_SETATTR);
1191da177e4SLinus Torvalds 
1201da177e4SLinus Torvalds         inp->coda_setattr.VFid = *fid;
1211da177e4SLinus Torvalds 	inp->coda_setattr.attr = *vattr;
1221da177e4SLinus Torvalds 
123a1b0aa87SJan Harkes 	error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);
1241da177e4SLinus Torvalds 
1251da177e4SLinus Torvalds         CODA_FREE(inp, insize);
1261da177e4SLinus Torvalds         return error;
1271da177e4SLinus Torvalds }
1281da177e4SLinus Torvalds 
1291da177e4SLinus Torvalds int venus_lookup(struct super_block *sb, struct CodaFid *fid,
1301da177e4SLinus Torvalds 		    const char *name, int length, int * type,
1311da177e4SLinus Torvalds 		    struct CodaFid *resfid)
1321da177e4SLinus Torvalds {
1331da177e4SLinus Torvalds         union inputArgs *inp;
1341da177e4SLinus Torvalds         union outputArgs *outp;
1351da177e4SLinus Torvalds         int insize, outsize, error;
1361da177e4SLinus Torvalds 	int offset;
1371da177e4SLinus Torvalds 
1381da177e4SLinus Torvalds 	offset = INSIZE(lookup);
1391da177e4SLinus Torvalds         insize = max_t(unsigned int, offset + length +1, OUTSIZE(lookup));
1401da177e4SLinus Torvalds 	UPARG(CODA_LOOKUP);
1411da177e4SLinus Torvalds 
1421da177e4SLinus Torvalds         inp->coda_lookup.VFid = *fid;
1431da177e4SLinus Torvalds 	inp->coda_lookup.name = offset;
1441da177e4SLinus Torvalds 	inp->coda_lookup.flags = CLU_CASE_SENSITIVE;
1451da177e4SLinus Torvalds         /* send Venus a null terminated string */
1461da177e4SLinus Torvalds         memcpy((char *)(inp) + offset, name, length);
1471da177e4SLinus Torvalds         *((char *)inp + offset + length) = '\0';
1481da177e4SLinus Torvalds 
149a1b0aa87SJan Harkes 	error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);
150970648ebSJan Harkes 	if (!error) {
1511da177e4SLinus Torvalds 		*resfid = outp->coda_lookup.VFid;
1521da177e4SLinus Torvalds 		*type = outp->coda_lookup.vtype;
153970648ebSJan Harkes 	}
1541da177e4SLinus Torvalds 
1551da177e4SLinus Torvalds 	CODA_FREE(inp, insize);
1561da177e4SLinus Torvalds 	return error;
1571da177e4SLinus Torvalds }
1581da177e4SLinus Torvalds 
1591da177e4SLinus Torvalds int venus_close(struct super_block *sb, struct CodaFid *fid, int flags,
160d83f5901SEric W. Biederman 		kuid_t uid)
1611da177e4SLinus Torvalds {
1621da177e4SLinus Torvalds 	union inputArgs *inp;
1631da177e4SLinus Torvalds 	union outputArgs *outp;
1641da177e4SLinus Torvalds 	int insize, outsize, error;
1651da177e4SLinus Torvalds 
1661da177e4SLinus Torvalds 	insize = SIZE(release);
1671da177e4SLinus Torvalds 	UPARG(CODA_CLOSE);
1681da177e4SLinus Torvalds 
169d83f5901SEric W. Biederman 	inp->ih.uid = from_kuid(&init_user_ns, uid);
1701da177e4SLinus Torvalds         inp->coda_close.VFid = *fid;
1711da177e4SLinus Torvalds         inp->coda_close.flags = flags;
1721da177e4SLinus Torvalds 
173a1b0aa87SJan Harkes 	error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);
1741da177e4SLinus Torvalds 
1751da177e4SLinus Torvalds 	CODA_FREE(inp, insize);
1761da177e4SLinus Torvalds         return error;
1771da177e4SLinus Torvalds }
1781da177e4SLinus Torvalds 
1791da177e4SLinus Torvalds int venus_open(struct super_block *sb, struct CodaFid *fid,
1801da177e4SLinus Torvalds 		  int flags, struct file **fh)
1811da177e4SLinus Torvalds {
1821da177e4SLinus Torvalds         union inputArgs *inp;
1831da177e4SLinus Torvalds         union outputArgs *outp;
1841da177e4SLinus Torvalds         int insize, outsize, error;
1851da177e4SLinus Torvalds 
1861da177e4SLinus Torvalds 	insize = SIZE(open_by_fd);
1871da177e4SLinus Torvalds 	UPARG(CODA_OPEN_BY_FD);
1881da177e4SLinus Torvalds 
18938c2e437SJan Harkes 	inp->coda_open_by_fd.VFid = *fid;
19038c2e437SJan Harkes 	inp->coda_open_by_fd.flags = flags;
1911da177e4SLinus Torvalds 
192a1b0aa87SJan Harkes 	error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);
19338c2e437SJan Harkes 	if (!error)
1941da177e4SLinus Torvalds 		*fh = outp->coda_open_by_fd.fh;
1951da177e4SLinus Torvalds 
1961da177e4SLinus Torvalds 	CODA_FREE(inp, insize);
1971da177e4SLinus Torvalds 	return error;
1981da177e4SLinus Torvalds }
1991da177e4SLinus Torvalds 
2001da177e4SLinus Torvalds int venus_mkdir(struct super_block *sb, struct CodaFid *dirfid,
2011da177e4SLinus Torvalds 		   const char *name, int length,
2021da177e4SLinus Torvalds 		   struct CodaFid *newfid, struct coda_vattr *attrs)
2031da177e4SLinus Torvalds {
2041da177e4SLinus Torvalds         union inputArgs *inp;
2051da177e4SLinus Torvalds         union outputArgs *outp;
2061da177e4SLinus Torvalds         int insize, outsize, error;
2071da177e4SLinus Torvalds         int offset;
2081da177e4SLinus Torvalds 
2091da177e4SLinus Torvalds 	offset = INSIZE(mkdir);
2101da177e4SLinus Torvalds 	insize = max_t(unsigned int, offset + length + 1, OUTSIZE(mkdir));
2111da177e4SLinus Torvalds 	UPARG(CODA_MKDIR);
2121da177e4SLinus Torvalds 
2131da177e4SLinus Torvalds         inp->coda_mkdir.VFid = *dirfid;
2141da177e4SLinus Torvalds         inp->coda_mkdir.attr = *attrs;
2151da177e4SLinus Torvalds 	inp->coda_mkdir.name = offset;
2161da177e4SLinus Torvalds         /* Venus must get null terminated string */
2171da177e4SLinus Torvalds         memcpy((char *)(inp) + offset, name, length);
2181da177e4SLinus Torvalds         *((char *)inp + offset + length) = '\0';
2191da177e4SLinus Torvalds 
220a1b0aa87SJan Harkes 	error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);
221970648ebSJan Harkes 	if (!error) {
2221da177e4SLinus Torvalds 		*attrs = outp->coda_mkdir.attr;
2231da177e4SLinus Torvalds 		*newfid = outp->coda_mkdir.VFid;
224970648ebSJan Harkes 	}
2251da177e4SLinus Torvalds 
2261da177e4SLinus Torvalds 	CODA_FREE(inp, insize);
2271da177e4SLinus Torvalds 	return error;
2281da177e4SLinus Torvalds }
2291da177e4SLinus Torvalds 
2301da177e4SLinus Torvalds 
2311da177e4SLinus Torvalds int venus_rename(struct super_block *sb, struct CodaFid *old_fid,
2321da177e4SLinus Torvalds 		 struct CodaFid *new_fid, size_t old_length,
2331da177e4SLinus Torvalds 		 size_t new_length, const char *old_name,
2341da177e4SLinus Torvalds 		 const char *new_name)
2351da177e4SLinus Torvalds {
2361da177e4SLinus Torvalds 	union inputArgs *inp;
2371da177e4SLinus Torvalds         union outputArgs *outp;
2381da177e4SLinus Torvalds         int insize, outsize, error;
2391da177e4SLinus Torvalds 	int offset, s;
2401da177e4SLinus Torvalds 
2411da177e4SLinus Torvalds 	offset = INSIZE(rename);
2421da177e4SLinus Torvalds 	insize = max_t(unsigned int, offset + new_length + old_length + 8,
2431da177e4SLinus Torvalds 		     OUTSIZE(rename));
2441da177e4SLinus Torvalds  	UPARG(CODA_RENAME);
2451da177e4SLinus Torvalds 
2461da177e4SLinus Torvalds         inp->coda_rename.sourceFid = *old_fid;
2471da177e4SLinus Torvalds         inp->coda_rename.destFid =  *new_fid;
2481da177e4SLinus Torvalds         inp->coda_rename.srcname = offset;
2491da177e4SLinus Torvalds 
2501da177e4SLinus Torvalds         /* Venus must receive an null terminated string */
2511da177e4SLinus Torvalds         s = ( old_length & ~0x3) +4; /* round up to word boundary */
2521da177e4SLinus Torvalds         memcpy((char *)(inp) + offset, old_name, old_length);
2531da177e4SLinus Torvalds         *((char *)inp + offset + old_length) = '\0';
2541da177e4SLinus Torvalds 
2551da177e4SLinus Torvalds         /* another null terminated string for Venus */
2561da177e4SLinus Torvalds         offset += s;
2571da177e4SLinus Torvalds         inp->coda_rename.destname = offset;
2581da177e4SLinus Torvalds         s = ( new_length & ~0x3) +4; /* round up to word boundary */
2591da177e4SLinus Torvalds         memcpy((char *)(inp) + offset, new_name, new_length);
2601da177e4SLinus Torvalds         *((char *)inp + offset + new_length) = '\0';
2611da177e4SLinus Torvalds 
262a1b0aa87SJan Harkes 	error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);
2631da177e4SLinus Torvalds 
2641da177e4SLinus Torvalds 	CODA_FREE(inp, insize);
2651da177e4SLinus Torvalds 	return error;
2661da177e4SLinus Torvalds }
2671da177e4SLinus Torvalds 
2681da177e4SLinus Torvalds int venus_create(struct super_block *sb, struct CodaFid *dirfid,
2691da177e4SLinus Torvalds 		 const char *name, int length, int excl, int mode,
2701da177e4SLinus Torvalds 		 struct CodaFid *newfid, struct coda_vattr *attrs)
2711da177e4SLinus Torvalds {
2721da177e4SLinus Torvalds         union inputArgs *inp;
2731da177e4SLinus Torvalds         union outputArgs *outp;
2741da177e4SLinus Torvalds         int insize, outsize, error;
2751da177e4SLinus Torvalds         int offset;
2761da177e4SLinus Torvalds 
2771da177e4SLinus Torvalds         offset = INSIZE(create);
2781da177e4SLinus Torvalds 	insize = max_t(unsigned int, offset + length + 1, OUTSIZE(create));
2791da177e4SLinus Torvalds 	UPARG(CODA_CREATE);
2801da177e4SLinus Torvalds 
2811da177e4SLinus Torvalds         inp->coda_create.VFid = *dirfid;
2821da177e4SLinus Torvalds         inp->coda_create.attr.va_mode = mode;
2831da177e4SLinus Torvalds 	inp->coda_create.excl = excl;
2841da177e4SLinus Torvalds         inp->coda_create.mode = mode;
2851da177e4SLinus Torvalds         inp->coda_create.name = offset;
2861da177e4SLinus Torvalds 
2871da177e4SLinus Torvalds         /* Venus must get null terminated string */
2881da177e4SLinus Torvalds         memcpy((char *)(inp) + offset, name, length);
2891da177e4SLinus Torvalds         *((char *)inp + offset + length) = '\0';
2901da177e4SLinus Torvalds 
291a1b0aa87SJan Harkes 	error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);
292970648ebSJan Harkes 	if (!error) {
2931da177e4SLinus Torvalds 		*attrs = outp->coda_create.attr;
2941da177e4SLinus Torvalds 		*newfid = outp->coda_create.VFid;
295970648ebSJan Harkes 	}
2961da177e4SLinus Torvalds 
2971da177e4SLinus Torvalds 	CODA_FREE(inp, insize);
2981da177e4SLinus Torvalds 	return error;
2991da177e4SLinus Torvalds }
3001da177e4SLinus Torvalds 
3011da177e4SLinus Torvalds int venus_rmdir(struct super_block *sb, struct CodaFid *dirfid,
3021da177e4SLinus Torvalds 		    const char *name, int length)
3031da177e4SLinus Torvalds {
3041da177e4SLinus Torvalds         union inputArgs *inp;
3051da177e4SLinus Torvalds         union outputArgs *outp;
3061da177e4SLinus Torvalds         int insize, outsize, error;
3071da177e4SLinus Torvalds         int offset;
3081da177e4SLinus Torvalds 
3091da177e4SLinus Torvalds         offset = INSIZE(rmdir);
3101da177e4SLinus Torvalds 	insize = max_t(unsigned int, offset + length + 1, OUTSIZE(rmdir));
3111da177e4SLinus Torvalds 	UPARG(CODA_RMDIR);
3121da177e4SLinus Torvalds 
3131da177e4SLinus Torvalds         inp->coda_rmdir.VFid = *dirfid;
3141da177e4SLinus Torvalds         inp->coda_rmdir.name = offset;
3151da177e4SLinus Torvalds         memcpy((char *)(inp) + offset, name, length);
3161da177e4SLinus Torvalds 	*((char *)inp + offset + length) = '\0';
3171da177e4SLinus Torvalds 
318a1b0aa87SJan Harkes 	error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);
3191da177e4SLinus Torvalds 
3201da177e4SLinus Torvalds 	CODA_FREE(inp, insize);
3211da177e4SLinus Torvalds 	return error;
3221da177e4SLinus Torvalds }
3231da177e4SLinus Torvalds 
3241da177e4SLinus Torvalds int venus_remove(struct super_block *sb, struct CodaFid *dirfid,
3251da177e4SLinus Torvalds 		    const char *name, int length)
3261da177e4SLinus Torvalds {
3271da177e4SLinus Torvalds         union inputArgs *inp;
3281da177e4SLinus Torvalds         union outputArgs *outp;
3291da177e4SLinus Torvalds         int error=0, insize, outsize, offset;
3301da177e4SLinus Torvalds 
3311da177e4SLinus Torvalds         offset = INSIZE(remove);
3321da177e4SLinus Torvalds 	insize = max_t(unsigned int, offset + length + 1, OUTSIZE(remove));
3331da177e4SLinus Torvalds 	UPARG(CODA_REMOVE);
3341da177e4SLinus Torvalds 
3351da177e4SLinus Torvalds         inp->coda_remove.VFid = *dirfid;
3361da177e4SLinus Torvalds         inp->coda_remove.name = offset;
3371da177e4SLinus Torvalds         memcpy((char *)(inp) + offset, name, length);
3381da177e4SLinus Torvalds 	*((char *)inp + offset + length) = '\0';
3391da177e4SLinus Torvalds 
340a1b0aa87SJan Harkes 	error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);
3411da177e4SLinus Torvalds 
3421da177e4SLinus Torvalds 	CODA_FREE(inp, insize);
3431da177e4SLinus Torvalds 	return error;
3441da177e4SLinus Torvalds }
3451da177e4SLinus Torvalds 
3461da177e4SLinus Torvalds int venus_readlink(struct super_block *sb, struct CodaFid *fid,
3471da177e4SLinus Torvalds 		      char *buffer, int *length)
3481da177e4SLinus Torvalds {
3491da177e4SLinus Torvalds         union inputArgs *inp;
3501da177e4SLinus Torvalds         union outputArgs *outp;
3511da177e4SLinus Torvalds         int insize, outsize, error;
3521da177e4SLinus Torvalds         int retlen;
3531da177e4SLinus Torvalds         char *result;
3541da177e4SLinus Torvalds 
3551da177e4SLinus Torvalds 	insize = max_t(unsigned int,
3563725e9ddSJan Harkes 		     INSIZE(readlink), OUTSIZE(readlink)+ *length);
3571da177e4SLinus Torvalds 	UPARG(CODA_READLINK);
3581da177e4SLinus Torvalds 
3591da177e4SLinus Torvalds         inp->coda_readlink.VFid = *fid;
3601da177e4SLinus Torvalds 
361a1b0aa87SJan Harkes 	error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);
3621da177e4SLinus Torvalds 	if (!error) {
3631da177e4SLinus Torvalds 		retlen = outp->coda_readlink.count;
3643725e9ddSJan Harkes 		if (retlen >= *length)
3653725e9ddSJan Harkes 			retlen = *length - 1;
3661da177e4SLinus Torvalds 		*length = retlen;
3671da177e4SLinus Torvalds 		result =  (char *)outp + (long)outp->coda_readlink.data;
3681da177e4SLinus Torvalds 		memcpy(buffer, result, retlen);
3691da177e4SLinus Torvalds 		*(buffer + retlen) = '\0';
3701da177e4SLinus Torvalds 	}
3711da177e4SLinus Torvalds 
3721da177e4SLinus Torvalds         CODA_FREE(inp, insize);
3731da177e4SLinus Torvalds         return error;
3741da177e4SLinus Torvalds }
3751da177e4SLinus Torvalds 
3761da177e4SLinus Torvalds 
3771da177e4SLinus Torvalds 
3781da177e4SLinus Torvalds int venus_link(struct super_block *sb, struct CodaFid *fid,
3791da177e4SLinus Torvalds 		  struct CodaFid *dirfid, const char *name, int len )
3801da177e4SLinus Torvalds {
3811da177e4SLinus Torvalds         union inputArgs *inp;
3821da177e4SLinus Torvalds         union outputArgs *outp;
3831da177e4SLinus Torvalds         int insize, outsize, error;
3841da177e4SLinus Torvalds         int offset;
3851da177e4SLinus Torvalds 
3861da177e4SLinus Torvalds 	offset = INSIZE(link);
3871da177e4SLinus Torvalds 	insize = max_t(unsigned int, offset  + len + 1, OUTSIZE(link));
3881da177e4SLinus Torvalds         UPARG(CODA_LINK);
3891da177e4SLinus Torvalds 
3901da177e4SLinus Torvalds         inp->coda_link.sourceFid = *fid;
3911da177e4SLinus Torvalds         inp->coda_link.destFid = *dirfid;
3921da177e4SLinus Torvalds         inp->coda_link.tname = offset;
3931da177e4SLinus Torvalds 
3941da177e4SLinus Torvalds         /* make sure strings are null terminated */
3951da177e4SLinus Torvalds         memcpy((char *)(inp) + offset, name, len);
3961da177e4SLinus Torvalds         *((char *)inp + offset + len) = '\0';
3971da177e4SLinus Torvalds 
398a1b0aa87SJan Harkes 	error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);
3991da177e4SLinus Torvalds 
4001da177e4SLinus Torvalds 	CODA_FREE(inp, insize);
4011da177e4SLinus Torvalds         return error;
4021da177e4SLinus Torvalds }
4031da177e4SLinus Torvalds 
4041da177e4SLinus Torvalds int venus_symlink(struct super_block *sb, struct CodaFid *fid,
4051da177e4SLinus Torvalds 		     const char *name, int len,
4061da177e4SLinus Torvalds 		     const char *symname, int symlen)
4071da177e4SLinus Torvalds {
4081da177e4SLinus Torvalds         union inputArgs *inp;
4091da177e4SLinus Torvalds         union outputArgs *outp;
4101da177e4SLinus Torvalds         int insize, outsize, error;
4111da177e4SLinus Torvalds         int offset, s;
4121da177e4SLinus Torvalds 
4131da177e4SLinus Torvalds         offset = INSIZE(symlink);
4141da177e4SLinus Torvalds 	insize = max_t(unsigned int, offset + len + symlen + 8, OUTSIZE(symlink));
4151da177e4SLinus Torvalds 	UPARG(CODA_SYMLINK);
4161da177e4SLinus Torvalds 
4171da177e4SLinus Torvalds         /*        inp->coda_symlink.attr = *tva; XXXXXX */
4181da177e4SLinus Torvalds         inp->coda_symlink.VFid = *fid;
4191da177e4SLinus Torvalds 
4201da177e4SLinus Torvalds 	/* Round up to word boundary and null terminate */
4211da177e4SLinus Torvalds         inp->coda_symlink.srcname = offset;
4221da177e4SLinus Torvalds         s = ( symlen  & ~0x3 ) + 4;
4231da177e4SLinus Torvalds         memcpy((char *)(inp) + offset, symname, symlen);
4241da177e4SLinus Torvalds         *((char *)inp + offset + symlen) = '\0';
4251da177e4SLinus Torvalds 
4261da177e4SLinus Torvalds 	/* Round up to word boundary and null terminate */
4271da177e4SLinus Torvalds         offset += s;
4281da177e4SLinus Torvalds         inp->coda_symlink.tname = offset;
4291da177e4SLinus Torvalds         s = (len & ~0x3) + 4;
4301da177e4SLinus Torvalds         memcpy((char *)(inp) + offset, name, len);
4311da177e4SLinus Torvalds         *((char *)inp + offset + len) = '\0';
4321da177e4SLinus Torvalds 
433a1b0aa87SJan Harkes 	error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);
4341da177e4SLinus Torvalds 
4351da177e4SLinus Torvalds 	CODA_FREE(inp, insize);
4361da177e4SLinus Torvalds         return error;
4371da177e4SLinus Torvalds }
4381da177e4SLinus Torvalds 
4391da177e4SLinus Torvalds int venus_fsync(struct super_block *sb, struct CodaFid *fid)
4401da177e4SLinus Torvalds {
4411da177e4SLinus Torvalds         union inputArgs *inp;
4421da177e4SLinus Torvalds         union outputArgs *outp;
4431da177e4SLinus Torvalds 	int insize, outsize, error;
4441da177e4SLinus Torvalds 
4451da177e4SLinus Torvalds 	insize=SIZE(fsync);
4461da177e4SLinus Torvalds 	UPARG(CODA_FSYNC);
4471da177e4SLinus Torvalds 
4481da177e4SLinus Torvalds 	inp->coda_fsync.VFid = *fid;
449a1b0aa87SJan Harkes 	error = coda_upcall(coda_vcp(sb), sizeof(union inputArgs),
4501da177e4SLinus Torvalds 			    &outsize, inp);
4511da177e4SLinus Torvalds 
4521da177e4SLinus Torvalds 	CODA_FREE(inp, insize);
4531da177e4SLinus Torvalds 	return error;
4541da177e4SLinus Torvalds }
4551da177e4SLinus Torvalds 
4561da177e4SLinus Torvalds int venus_access(struct super_block *sb, struct CodaFid *fid, int mask)
4571da177e4SLinus Torvalds {
4581da177e4SLinus Torvalds         union inputArgs *inp;
4591da177e4SLinus Torvalds         union outputArgs *outp;
4601da177e4SLinus Torvalds 	int insize, outsize, error;
4611da177e4SLinus Torvalds 
4621da177e4SLinus Torvalds 	insize = SIZE(access);
4631da177e4SLinus Torvalds 	UPARG(CODA_ACCESS);
4641da177e4SLinus Torvalds 
4651da177e4SLinus Torvalds         inp->coda_access.VFid = *fid;
4661da177e4SLinus Torvalds         inp->coda_access.flags = mask;
4671da177e4SLinus Torvalds 
468a1b0aa87SJan Harkes 	error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);
4691da177e4SLinus Torvalds 
4701da177e4SLinus Torvalds 	CODA_FREE(inp, insize);
4711da177e4SLinus Torvalds 	return error;
4721da177e4SLinus Torvalds }
4731da177e4SLinus Torvalds 
4741da177e4SLinus Torvalds 
4751da177e4SLinus Torvalds int venus_pioctl(struct super_block *sb, struct CodaFid *fid,
4761da177e4SLinus Torvalds 		 unsigned int cmd, struct PioctlData *data)
4771da177e4SLinus Torvalds {
4781da177e4SLinus Torvalds         union inputArgs *inp;
4791da177e4SLinus Torvalds         union outputArgs *outp;
4801da177e4SLinus Torvalds 	int insize, outsize, error;
4811da177e4SLinus Torvalds 	int iocsize;
4821da177e4SLinus Torvalds 
4831da177e4SLinus Torvalds 	insize = VC_MAXMSGSIZE;
4841da177e4SLinus Torvalds 	UPARG(CODA_IOCTL);
4851da177e4SLinus Torvalds 
4861da177e4SLinus Torvalds         /* build packet for Venus */
4871da177e4SLinus Torvalds         if (data->vi.in_size > VC_MAXDATASIZE) {
4881da177e4SLinus Torvalds 		error = -EINVAL;
4891da177e4SLinus Torvalds 		goto exit;
4901da177e4SLinus Torvalds         }
4911da177e4SLinus Torvalds 
4921da177e4SLinus Torvalds         if (data->vi.out_size > VC_MAXDATASIZE) {
4931da177e4SLinus Torvalds 		error = -EINVAL;
4941da177e4SLinus Torvalds 		goto exit;
4951da177e4SLinus Torvalds 	}
4961da177e4SLinus Torvalds 
4971da177e4SLinus Torvalds         inp->coda_ioctl.VFid = *fid;
4981da177e4SLinus Torvalds 
4991da177e4SLinus Torvalds         /* the cmd field was mutated by increasing its size field to
5001da177e4SLinus Torvalds          * reflect the path and follow args. We need to subtract that
5011da177e4SLinus Torvalds          * out before sending the command to Venus.  */
5021da177e4SLinus Torvalds         inp->coda_ioctl.cmd = (cmd & ~(PIOCPARM_MASK << 16));
5031da177e4SLinus Torvalds         iocsize = ((cmd >> 16) & PIOCPARM_MASK) - sizeof(char *) - sizeof(int);
5041da177e4SLinus Torvalds         inp->coda_ioctl.cmd |= (iocsize & PIOCPARM_MASK) <<	16;
5051da177e4SLinus Torvalds 
5061da177e4SLinus Torvalds         /* in->coda_ioctl.rwflag = flag; */
5071da177e4SLinus Torvalds         inp->coda_ioctl.len = data->vi.in_size;
5081da177e4SLinus Torvalds         inp->coda_ioctl.data = (char *)(INSIZE(ioctl));
5091da177e4SLinus Torvalds 
5101da177e4SLinus Torvalds         /* get the data out of user space */
5111da177e4SLinus Torvalds 	if (copy_from_user((char *)inp + (long)inp->coda_ioctl.data,
5121da177e4SLinus Torvalds 			   data->vi.in, data->vi.in_size)) {
5131da177e4SLinus Torvalds 		error = -EINVAL;
5141da177e4SLinus Torvalds 	        goto exit;
5151da177e4SLinus Torvalds 	}
5161da177e4SLinus Torvalds 
517a1b0aa87SJan Harkes 	error = coda_upcall(coda_vcp(sb), SIZE(ioctl) + data->vi.in_size,
5181da177e4SLinus Torvalds 			    &outsize, inp);
5191da177e4SLinus Torvalds 
5201da177e4SLinus Torvalds         if (error) {
5216d6bd94fSFabian Frederick 		pr_warn("%s: Venus returns: %d for %s\n",
5226d6bd94fSFabian Frederick 			__func__, error, coda_f2s(fid));
5231da177e4SLinus Torvalds 		goto exit;
5241da177e4SLinus Torvalds 	}
5251da177e4SLinus Torvalds 
5261da177e4SLinus Torvalds 	if (outsize < (long)outp->coda_ioctl.data + outp->coda_ioctl.len) {
5271da177e4SLinus Torvalds 		error = -EINVAL;
5281da177e4SLinus Torvalds 		goto exit;
5291da177e4SLinus Torvalds 	}
5301da177e4SLinus Torvalds 
5311da177e4SLinus Torvalds 	/* Copy out the OUT buffer. */
5321da177e4SLinus Torvalds         if (outp->coda_ioctl.len > data->vi.out_size) {
5331da177e4SLinus Torvalds 		error = -EINVAL;
5341da177e4SLinus Torvalds 		goto exit;
5351da177e4SLinus Torvalds         }
5361da177e4SLinus Torvalds 
5371da177e4SLinus Torvalds 	/* Copy out the OUT buffer. */
5381da177e4SLinus Torvalds 	if (copy_to_user(data->vi.out,
5391da177e4SLinus Torvalds 			 (char *)outp + (long)outp->coda_ioctl.data,
5401da177e4SLinus Torvalds 			 outp->coda_ioctl.len)) {
5411da177e4SLinus Torvalds 		error = -EFAULT;
5421da177e4SLinus Torvalds 		goto exit;
5431da177e4SLinus Torvalds 	}
5441da177e4SLinus Torvalds 
5451da177e4SLinus Torvalds  exit:
5461da177e4SLinus Torvalds 	CODA_FREE(inp, insize);
5471da177e4SLinus Torvalds 	return error;
5481da177e4SLinus Torvalds }
5491da177e4SLinus Torvalds 
550726c3342SDavid Howells int venus_statfs(struct dentry *dentry, struct kstatfs *sfs)
5511da177e4SLinus Torvalds {
5521da177e4SLinus Torvalds         union inputArgs *inp;
5531da177e4SLinus Torvalds         union outputArgs *outp;
5541da177e4SLinus Torvalds         int insize, outsize, error;
5551da177e4SLinus Torvalds 
5561da177e4SLinus Torvalds 	insize = max_t(unsigned int, INSIZE(statfs), OUTSIZE(statfs));
5571da177e4SLinus Torvalds 	UPARG(CODA_STATFS);
5581da177e4SLinus Torvalds 
559a1b0aa87SJan Harkes 	error = coda_upcall(coda_vcp(dentry->d_sb), insize, &outsize, inp);
5601da177e4SLinus Torvalds 	if (!error) {
5611da177e4SLinus Torvalds 		sfs->f_blocks = outp->coda_statfs.stat.f_blocks;
5621da177e4SLinus Torvalds 		sfs->f_bfree  = outp->coda_statfs.stat.f_bfree;
5631da177e4SLinus Torvalds 		sfs->f_bavail = outp->coda_statfs.stat.f_bavail;
5641da177e4SLinus Torvalds 		sfs->f_files  = outp->coda_statfs.stat.f_files;
5651da177e4SLinus Torvalds 		sfs->f_ffree  = outp->coda_statfs.stat.f_ffree;
5661da177e4SLinus Torvalds 	}
5671da177e4SLinus Torvalds 
5681da177e4SLinus Torvalds         CODA_FREE(inp, insize);
5691da177e4SLinus Torvalds         return error;
5701da177e4SLinus Torvalds }
5711da177e4SLinus Torvalds 
5721da177e4SLinus Torvalds /*
5731da177e4SLinus Torvalds  * coda_upcall and coda_downcall routines.
5741da177e4SLinus Torvalds  */
5755f47c7eaSAl Viro static void coda_block_signals(sigset_t *old)
576d9664c95SJan Harkes {
577d9664c95SJan Harkes 	spin_lock_irq(&current->sighand->siglock);
578d9664c95SJan Harkes 	*old = current->blocked;
5791da177e4SLinus Torvalds 
580d9664c95SJan Harkes 	sigfillset(&current->blocked);
581d9664c95SJan Harkes 	sigdelset(&current->blocked, SIGKILL);
582d9664c95SJan Harkes 	sigdelset(&current->blocked, SIGSTOP);
583d9664c95SJan Harkes 	sigdelset(&current->blocked, SIGINT);
584d9664c95SJan Harkes 
585d9664c95SJan Harkes 	recalc_sigpending();
586d9664c95SJan Harkes 	spin_unlock_irq(&current->sighand->siglock);
587d9664c95SJan Harkes }
588d9664c95SJan Harkes 
5895f47c7eaSAl Viro static void coda_unblock_signals(sigset_t *old)
590d9664c95SJan Harkes {
591d9664c95SJan Harkes 	spin_lock_irq(&current->sighand->siglock);
592d9664c95SJan Harkes 	current->blocked = *old;
593d9664c95SJan Harkes 	recalc_sigpending();
594d9664c95SJan Harkes 	spin_unlock_irq(&current->sighand->siglock);
595d9664c95SJan Harkes }
596d9664c95SJan Harkes 
597d9664c95SJan Harkes /* Don't allow signals to interrupt the following upcalls before venus
598d9664c95SJan Harkes  * has seen them,
599d9664c95SJan Harkes  * - CODA_CLOSE or CODA_RELEASE upcall  (to avoid reference count problems)
600d9664c95SJan Harkes  * - CODA_STORE				(to avoid data loss)
601d9664c95SJan Harkes  */
602d9664c95SJan Harkes #define CODA_INTERRUPTIBLE(r) (!coda_hard && \
603d9664c95SJan Harkes 			       (((r)->uc_opcode != CODA_CLOSE && \
604d9664c95SJan Harkes 				 (r)->uc_opcode != CODA_STORE && \
605d9664c95SJan Harkes 				 (r)->uc_opcode != CODA_RELEASE) || \
6064aeefdc6SJens Axboe 				(r)->uc_flags & CODA_REQ_READ))
607d9664c95SJan Harkes 
608da47c19eSYoshihisa Abe static inline void coda_waitfor_upcall(struct venus_comm *vcp,
609da47c19eSYoshihisa Abe 				       struct upc_req *req)
6101da177e4SLinus Torvalds {
6111da177e4SLinus Torvalds 	DECLARE_WAITQUEUE(wait, current);
612d9664c95SJan Harkes 	unsigned long timeout = jiffies + coda_timeout * HZ;
613d9664c95SJan Harkes 	sigset_t old;
614d9664c95SJan Harkes 	int blocked;
6151da177e4SLinus Torvalds 
6165f47c7eaSAl Viro 	coda_block_signals(&old);
617d9664c95SJan Harkes 	blocked = 1;
6181da177e4SLinus Torvalds 
619d9664c95SJan Harkes 	add_wait_queue(&req->uc_sleep, &wait);
6201da177e4SLinus Torvalds 	for (;;) {
621d9664c95SJan Harkes 		if (CODA_INTERRUPTIBLE(req))
6221da177e4SLinus Torvalds 			set_current_state(TASK_INTERRUPTIBLE);
6231da177e4SLinus Torvalds 		else
6241da177e4SLinus Torvalds 			set_current_state(TASK_UNINTERRUPTIBLE);
6251da177e4SLinus Torvalds 
6261da177e4SLinus Torvalds 		/* got a reply */
6274aeefdc6SJens Axboe 		if (req->uc_flags & (CODA_REQ_WRITE | CODA_REQ_ABORT))
6281da177e4SLinus Torvalds 			break;
6291da177e4SLinus Torvalds 
630d9664c95SJan Harkes 		if (blocked && time_after(jiffies, timeout) &&
631d9664c95SJan Harkes 		    CODA_INTERRUPTIBLE(req))
632d9664c95SJan Harkes 		{
6335f47c7eaSAl Viro 			coda_unblock_signals(&old);
634d9664c95SJan Harkes 			blocked = 0;
635d9664c95SJan Harkes 		}
636d9664c95SJan Harkes 
637d9664c95SJan Harkes 		if (signal_pending(current)) {
638d9664c95SJan Harkes 			list_del(&req->uc_chain);
6391da177e4SLinus Torvalds 			break;
6401da177e4SLinus Torvalds 		}
641d9664c95SJan Harkes 
642da47c19eSYoshihisa Abe 		mutex_unlock(&vcp->vc_mutex);
643d9664c95SJan Harkes 		if (blocked)
644d9664c95SJan Harkes 			schedule_timeout(HZ);
645d9664c95SJan Harkes 		else
6461da177e4SLinus Torvalds 			schedule();
647da47c19eSYoshihisa Abe 		mutex_lock(&vcp->vc_mutex);
6481da177e4SLinus Torvalds 	}
649d9664c95SJan Harkes 	if (blocked)
6505f47c7eaSAl Viro 		coda_unblock_signals(&old);
6511da177e4SLinus Torvalds 
652d9664c95SJan Harkes 	remove_wait_queue(&req->uc_sleep, &wait);
653d9664c95SJan Harkes 	set_current_state(TASK_RUNNING);
6541da177e4SLinus Torvalds }
6551da177e4SLinus Torvalds 
6561da177e4SLinus Torvalds 
6571da177e4SLinus Torvalds /*
6581da177e4SLinus Torvalds  * coda_upcall will return an error in the case of
6591da177e4SLinus Torvalds  * failed communication with Venus _or_ will peek at Venus
6601da177e4SLinus Torvalds  * reply and return Venus' error.
6611da177e4SLinus Torvalds  *
6621da177e4SLinus Torvalds  * As venus has 2 types of errors, normal errors (positive) and internal
6631da177e4SLinus Torvalds  * errors (negative), normal errors are negated, while internal errors
6641da177e4SLinus Torvalds  * are all mapped to -EINTR, while showing a nice warning message. (jh)
6651da177e4SLinus Torvalds  */
666a1b0aa87SJan Harkes static int coda_upcall(struct venus_comm *vcp,
6671da177e4SLinus Torvalds 		       int inSize, int *outSize,
6681da177e4SLinus Torvalds 		       union inputArgs *buffer)
6691da177e4SLinus Torvalds {
6701da177e4SLinus Torvalds 	union outputArgs *out;
671fe71b5f3SJan Harkes 	union inputArgs *sig_inputArgs;
672f7cc02b8SYoshihisa Abe 	struct upc_req *req = NULL, *sig_req;
673f7cc02b8SYoshihisa Abe 	int error;
674f7cc02b8SYoshihisa Abe 
675da47c19eSYoshihisa Abe 	mutex_lock(&vcp->vc_mutex);
6761da177e4SLinus Torvalds 
677a1b0aa87SJan Harkes 	if (!vcp->vc_inuse) {
678f38cfb25SFabian Frederick 		pr_notice("Venus dead, not sending upcall\n");
679f7cc02b8SYoshihisa Abe 		error = -ENXIO;
680f7cc02b8SYoshihisa Abe 		goto exit;
6811da177e4SLinus Torvalds 	}
6821da177e4SLinus Torvalds 
6831da177e4SLinus Torvalds 	/* Format the request message. */
68437461e19SJan Harkes 	req = kmalloc(sizeof(struct upc_req), GFP_KERNEL);
685f7cc02b8SYoshihisa Abe 	if (!req) {
686f7cc02b8SYoshihisa Abe 		error = -ENOMEM;
687f7cc02b8SYoshihisa Abe 		goto exit;
688f7cc02b8SYoshihisa Abe 	}
689fe71b5f3SJan Harkes 
6901da177e4SLinus Torvalds 	req->uc_data = (void *)buffer;
6911da177e4SLinus Torvalds 	req->uc_flags = 0;
6921da177e4SLinus Torvalds 	req->uc_inSize = inSize;
6931da177e4SLinus Torvalds 	req->uc_outSize = *outSize ? *outSize : inSize;
6941da177e4SLinus Torvalds 	req->uc_opcode = ((union inputArgs *)buffer)->ih.opcode;
695a1b0aa87SJan Harkes 	req->uc_unique = ++vcp->vc_seq;
6961da177e4SLinus Torvalds 	init_waitqueue_head(&req->uc_sleep);
6971da177e4SLinus Torvalds 
6981da177e4SLinus Torvalds 	/* Fill in the common input args. */
6991da177e4SLinus Torvalds 	((union inputArgs *)buffer)->ih.unique = req->uc_unique;
7001da177e4SLinus Torvalds 
7011da177e4SLinus Torvalds 	/* Append msg to pending queue and poke Venus. */
702a1b0aa87SJan Harkes 	list_add_tail(&req->uc_chain, &vcp->vc_pending);
7031da177e4SLinus Torvalds 
704a1b0aa87SJan Harkes 	wake_up_interruptible(&vcp->vc_waitq);
7051da177e4SLinus Torvalds 	/* We can be interrupted while we wait for Venus to process
7061da177e4SLinus Torvalds 	 * our request.  If the interrupt occurs before Venus has read
7071da177e4SLinus Torvalds 	 * the request, we dequeue and return. If it occurs after the
7081da177e4SLinus Torvalds 	 * read but before the reply, we dequeue, send a signal
7091da177e4SLinus Torvalds 	 * message, and return. If it occurs after the reply we ignore
7101da177e4SLinus Torvalds 	 * it. In no case do we want to restart the syscall.  If it
7111da177e4SLinus Torvalds 	 * was interrupted by a venus shutdown (psdev_close), return
7121da177e4SLinus Torvalds 	 * ENODEV.  */
7131da177e4SLinus Torvalds 
7141da177e4SLinus Torvalds 	/* Go to sleep.  Wake up on signals only after the timeout. */
715da47c19eSYoshihisa Abe 	coda_waitfor_upcall(vcp, req);
7161da177e4SLinus Torvalds 
7171da177e4SLinus Torvalds 	/* Op went through, interrupt or not... */
7184aeefdc6SJens Axboe 	if (req->uc_flags & CODA_REQ_WRITE) {
7191da177e4SLinus Torvalds 		out = (union outputArgs *)req->uc_data;
7201da177e4SLinus Torvalds 		/* here we map positive Venus errors to kernel errors */
7211da177e4SLinus Torvalds 		error = -out->oh.result;
7221da177e4SLinus Torvalds 		*outSize = req->uc_outSize;
7231da177e4SLinus Torvalds 		goto exit;
7241da177e4SLinus Torvalds 	}
725fe71b5f3SJan Harkes 
7261da177e4SLinus Torvalds 	error = -EINTR;
7274aeefdc6SJens Axboe 	if ((req->uc_flags & CODA_REQ_ABORT) || !signal_pending(current)) {
728f38cfb25SFabian Frederick 		pr_warn("Unexpected interruption.\n");
7291da177e4SLinus Torvalds 		goto exit;
7301da177e4SLinus Torvalds 	}
7311da177e4SLinus Torvalds 
732fe71b5f3SJan Harkes 	/* Interrupted before venus read it. */
7334aeefdc6SJens Axboe 	if (!(req->uc_flags & CODA_REQ_READ))
734fe71b5f3SJan Harkes 		goto exit;
735fe71b5f3SJan Harkes 
736fe71b5f3SJan Harkes 	/* Venus saw the upcall, make sure we can send interrupt signal */
737a1b0aa87SJan Harkes 	if (!vcp->vc_inuse) {
738f38cfb25SFabian Frederick 		pr_info("Venus dead, not sending signal.\n");
739fe71b5f3SJan Harkes 		goto exit;
740fe71b5f3SJan Harkes 	}
741fe71b5f3SJan Harkes 
7421da177e4SLinus Torvalds 	error = -ENOMEM;
74337461e19SJan Harkes 	sig_req = kmalloc(sizeof(struct upc_req), GFP_KERNEL);
7441da177e4SLinus Torvalds 	if (!sig_req) goto exit;
7451da177e4SLinus Torvalds 
7461da177e4SLinus Torvalds 	CODA_ALLOC((sig_req->uc_data), char *, sizeof(struct coda_in_hdr));
7471da177e4SLinus Torvalds 	if (!sig_req->uc_data) {
74837461e19SJan Harkes 		kfree(sig_req);
7491da177e4SLinus Torvalds 		goto exit;
7501da177e4SLinus Torvalds 	}
7511da177e4SLinus Torvalds 
7521da177e4SLinus Torvalds 	error = -EINTR;
7531da177e4SLinus Torvalds 	sig_inputArgs = (union inputArgs *)sig_req->uc_data;
7541da177e4SLinus Torvalds 	sig_inputArgs->ih.opcode = CODA_SIGNAL;
7551da177e4SLinus Torvalds 	sig_inputArgs->ih.unique = req->uc_unique;
7561da177e4SLinus Torvalds 
7574aeefdc6SJens Axboe 	sig_req->uc_flags = CODA_REQ_ASYNC;
7581da177e4SLinus Torvalds 	sig_req->uc_opcode = sig_inputArgs->ih.opcode;
7591da177e4SLinus Torvalds 	sig_req->uc_unique = sig_inputArgs->ih.unique;
7601da177e4SLinus Torvalds 	sig_req->uc_inSize = sizeof(struct coda_in_hdr);
7611da177e4SLinus Torvalds 	sig_req->uc_outSize = sizeof(struct coda_in_hdr);
7621da177e4SLinus Torvalds 
7631da177e4SLinus Torvalds 	/* insert at head of queue! */
764a1b0aa87SJan Harkes 	list_add(&(sig_req->uc_chain), &vcp->vc_pending);
765a1b0aa87SJan Harkes 	wake_up_interruptible(&vcp->vc_waitq);
7661da177e4SLinus Torvalds 
7671da177e4SLinus Torvalds exit:
76837461e19SJan Harkes 	kfree(req);
769da47c19eSYoshihisa Abe 	mutex_unlock(&vcp->vc_mutex);
7701da177e4SLinus Torvalds 	return error;
7711da177e4SLinus Torvalds }
7721da177e4SLinus Torvalds 
7731da177e4SLinus Torvalds /*
7741da177e4SLinus Torvalds     The statements below are part of the Coda opportunistic
7751da177e4SLinus Torvalds     programming -- taken from the Mach/BSD kernel code for Coda.
7761da177e4SLinus Torvalds     You don't get correct semantics by stating what needs to be
7771da177e4SLinus Torvalds     done without guaranteeing the invariants needed for it to happen.
7781da177e4SLinus Torvalds     When will be have time to find out what exactly is going on?  (pjb)
7791da177e4SLinus Torvalds */
7801da177e4SLinus Torvalds 
7811da177e4SLinus Torvalds 
7821da177e4SLinus Torvalds /*
7831da177e4SLinus Torvalds  * There are 7 cases where cache invalidations occur.  The semantics
7841da177e4SLinus Torvalds  *  of each is listed here:
7851da177e4SLinus Torvalds  *
7861da177e4SLinus Torvalds  * CODA_FLUSH     -- flush all entries from the name cache and the cnode cache.
7871da177e4SLinus Torvalds  * CODA_PURGEUSER -- flush all entries from the name cache for a specific user
7881da177e4SLinus Torvalds  *                  This call is a result of token expiration.
7891da177e4SLinus Torvalds  *
7901da177e4SLinus Torvalds  * The next arise as the result of callbacks on a file or directory.
7911da177e4SLinus Torvalds  * CODA_ZAPFILE   -- flush the cached attributes for a file.
7921da177e4SLinus Torvalds 
7931da177e4SLinus Torvalds  * CODA_ZAPDIR    -- flush the attributes for the dir and
7941da177e4SLinus Torvalds  *                  force a new lookup for all the children
7951da177e4SLinus Torvalds                     of this dir.
7961da177e4SLinus Torvalds 
7971da177e4SLinus Torvalds  *
7981da177e4SLinus Torvalds  * The next is a result of Venus detecting an inconsistent file.
7991da177e4SLinus Torvalds  * CODA_PURGEFID  -- flush the attribute for the file
8001da177e4SLinus Torvalds  *                  purge it and its children from the dcache
8011da177e4SLinus Torvalds  *
8021da177e4SLinus Torvalds  * The last  allows Venus to replace local fids with global ones
8031da177e4SLinus Torvalds  * during reintegration.
8041da177e4SLinus Torvalds  *
8051da177e4SLinus Torvalds  * CODA_REPLACE -- replace one CodaFid with another throughout the name cache */
8061da177e4SLinus Torvalds 
807f7cc02b8SYoshihisa Abe int coda_downcall(struct venus_comm *vcp, int opcode, union outputArgs *out)
8081da177e4SLinus Torvalds {
8095fd31e9aSJan Harkes 	struct inode *inode = NULL;
810da47c19eSYoshihisa Abe 	struct CodaFid *fid = NULL, *newfid;
811f7cc02b8SYoshihisa Abe 	struct super_block *sb;
8125fd31e9aSJan Harkes 
8131da177e4SLinus Torvalds 	/* Handle invalidation requests. */
814da47c19eSYoshihisa Abe 	mutex_lock(&vcp->vc_mutex);
815f7cc02b8SYoshihisa Abe 	sb = vcp->vc_sb;
8165fd31e9aSJan Harkes 	if (!sb || !sb->s_root)
817f7cc02b8SYoshihisa Abe 		goto unlock_out;
8181da177e4SLinus Torvalds 
8191da177e4SLinus Torvalds 	switch (opcode) {
8205fd31e9aSJan Harkes 	case CODA_FLUSH:
8211da177e4SLinus Torvalds 		coda_cache_clear_all(sb);
8221da177e4SLinus Torvalds 		shrink_dcache_sb(sb);
8232b0143b5SDavid Howells 		if (d_really_is_positive(sb->s_root))
8242b0143b5SDavid Howells 			coda_flag_inode(d_inode(sb->s_root), C_FLUSH);
8255fd31e9aSJan Harkes 		break;
8261da177e4SLinus Torvalds 
8275fd31e9aSJan Harkes 	case CODA_PURGEUSER:
8281da177e4SLinus Torvalds 		coda_cache_clear_all(sb);
8295fd31e9aSJan Harkes 		break;
8301da177e4SLinus Torvalds 
8315fd31e9aSJan Harkes 	case CODA_ZAPDIR:
8325fd31e9aSJan Harkes 		fid = &out->coda_zapdir.CodaFid;
8335fd31e9aSJan Harkes 		break;
8341da177e4SLinus Torvalds 
8355fd31e9aSJan Harkes 	case CODA_ZAPFILE:
8365fd31e9aSJan Harkes 		fid = &out->coda_zapfile.CodaFid;
8375fd31e9aSJan Harkes 		break;
8381da177e4SLinus Torvalds 
8395fd31e9aSJan Harkes 	case CODA_PURGEFID:
8405fd31e9aSJan Harkes 		fid = &out->coda_purgefid.CodaFid;
841da47c19eSYoshihisa Abe 		break;
842da47c19eSYoshihisa Abe 
843da47c19eSYoshihisa Abe 	case CODA_REPLACE:
844da47c19eSYoshihisa Abe 		fid = &out->coda_replace.OldFid;
845da47c19eSYoshihisa Abe 		break;
846da47c19eSYoshihisa Abe 	}
847da47c19eSYoshihisa Abe 	if (fid)
8481da177e4SLinus Torvalds 		inode = coda_fid_to_inode(fid, sb);
849da47c19eSYoshihisa Abe 
850da47c19eSYoshihisa Abe unlock_out:
851da47c19eSYoshihisa Abe 	mutex_unlock(&vcp->vc_mutex);
852da47c19eSYoshihisa Abe 
853da47c19eSYoshihisa Abe 	if (!inode)
854da47c19eSYoshihisa Abe 		return 0;
855da47c19eSYoshihisa Abe 
856da47c19eSYoshihisa Abe 	switch (opcode) {
857da47c19eSYoshihisa Abe 	case CODA_ZAPDIR:
858da47c19eSYoshihisa Abe 		coda_flag_inode_children(inode, C_PURGE);
859da47c19eSYoshihisa Abe 		coda_flag_inode(inode, C_VATTR);
860da47c19eSYoshihisa Abe 		break;
861da47c19eSYoshihisa Abe 
862da47c19eSYoshihisa Abe 	case CODA_ZAPFILE:
863da47c19eSYoshihisa Abe 		coda_flag_inode(inode, C_VATTR);
864da47c19eSYoshihisa Abe 		break;
865da47c19eSYoshihisa Abe 
866da47c19eSYoshihisa Abe 	case CODA_PURGEFID:
8671da177e4SLinus Torvalds 		coda_flag_inode_children(inode, C_PURGE);
8681da177e4SLinus Torvalds 
8691da177e4SLinus Torvalds 		/* catch the dentries later if some are still busy */
8701da177e4SLinus Torvalds 		coda_flag_inode(inode, C_PURGE);
8711da177e4SLinus Torvalds 		d_prune_aliases(inode);
8725fd31e9aSJan Harkes 		break;
8735fd31e9aSJan Harkes 
8745fd31e9aSJan Harkes 	case CODA_REPLACE:
8755fd31e9aSJan Harkes 		newfid = &out->coda_replace.NewFid;
8765fd31e9aSJan Harkes 		coda_replace_fid(inode, fid, newfid);
8775fd31e9aSJan Harkes 		break;
8781da177e4SLinus Torvalds 	}
8791da177e4SLinus Torvalds 	iput(inode);
8801da177e4SLinus Torvalds 	return 0;
8811da177e4SLinus Torvalds }
8821da177e4SLinus Torvalds 
883