xref: /openbmc/linux/fs/coda/upcall.c (revision 6e51f8aa)
1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds  * Mostly platform independent upcall operations to Venus:
41da177e4SLinus Torvalds  *  -- upcalls
51da177e4SLinus Torvalds  *  -- upcall routines
61da177e4SLinus Torvalds  *
71da177e4SLinus Torvalds  * Linux 2.0 version
81da177e4SLinus Torvalds  * Copyright (C) 1996 Peter J. Braam <braam@maths.ox.ac.uk>,
91da177e4SLinus Torvalds  * Michael Callahan <callahan@maths.ox.ac.uk>
101da177e4SLinus Torvalds  *
111da177e4SLinus Torvalds  * Redone for Linux 2.1
121da177e4SLinus Torvalds  * Copyright (C) 1997 Carnegie Mellon University
131da177e4SLinus Torvalds  *
141da177e4SLinus Torvalds  * Carnegie Mellon University encourages users of this code to contribute
151da177e4SLinus Torvalds  * improvements to the Coda project. Contact Peter Braam <coda@cs.cmu.edu>.
161da177e4SLinus Torvalds  */
171da177e4SLinus Torvalds 
181da177e4SLinus Torvalds #include <linux/signal.h>
193f07c014SIngo Molnar #include <linux/sched/signal.h>
201da177e4SLinus Torvalds #include <linux/types.h>
211da177e4SLinus Torvalds #include <linux/kernel.h>
221da177e4SLinus Torvalds #include <linux/mm.h>
231da177e4SLinus Torvalds #include <linux/time.h>
241da177e4SLinus Torvalds #include <linux/fs.h>
251da177e4SLinus Torvalds #include <linux/file.h>
261da177e4SLinus Torvalds #include <linux/stat.h>
271da177e4SLinus Torvalds #include <linux/errno.h>
281da177e4SLinus Torvalds #include <linux/string.h>
295a0e3ad6STejun Heo #include <linux/slab.h>
30da47c19eSYoshihisa Abe #include <linux/mutex.h>
31834b46c3SFabian Frederick #include <linux/uaccess.h>
321da177e4SLinus Torvalds #include <linux/vmalloc.h>
331da177e4SLinus Torvalds #include <linux/vfs.h>
341da177e4SLinus Torvalds 
351da177e4SLinus Torvalds #include <linux/coda.h>
361da177e4SLinus Torvalds #include <linux/coda_psdev.h>
3731a203dfSAl Viro #include "coda_linux.h"
3831a203dfSAl Viro #include "coda_cache.h"
393cf01f28SJan Harkes 
403cf01f28SJan Harkes #include "coda_int.h"
411da177e4SLinus Torvalds 
42a1b0aa87SJan Harkes static int coda_upcall(struct venus_comm *vc, int inSize, int *outSize,
431da177e4SLinus Torvalds 		       union inputArgs *buffer);
441da177e4SLinus Torvalds 
451da177e4SLinus Torvalds static void *alloc_upcall(int opcode, int size)
461da177e4SLinus Torvalds {
471da177e4SLinus Torvalds 	union inputArgs *inp;
481da177e4SLinus Torvalds 
491da177e4SLinus Torvalds 	CODA_ALLOC(inp, union inputArgs *, size);
501da177e4SLinus Torvalds         if (!inp)
511da177e4SLinus Torvalds 		return ERR_PTR(-ENOMEM);
521da177e4SLinus Torvalds 
531da177e4SLinus Torvalds         inp->ih.opcode = opcode;
549fd973e0SEric W. Biederman 	inp->ih.pid = task_pid_nr_ns(current, &init_pid_ns);
559fd973e0SEric W. Biederman 	inp->ih.pgid = task_pgrp_nr_ns(current, &init_pid_ns);
56d83f5901SEric W. Biederman 	inp->ih.uid = from_kuid(&init_user_ns, current_fsuid());
57de0ca06aSAdrian Bunk 
581da177e4SLinus Torvalds 	return (void*)inp;
591da177e4SLinus Torvalds }
601da177e4SLinus Torvalds 
611da177e4SLinus Torvalds #define UPARG(op)\
621da177e4SLinus Torvalds do {\
631da177e4SLinus Torvalds 	inp = (union inputArgs *)alloc_upcall(op, insize); \
641da177e4SLinus Torvalds         if (IS_ERR(inp)) { return PTR_ERR(inp); }\
651da177e4SLinus Torvalds         outp = (union outputArgs *)(inp); \
661da177e4SLinus Torvalds         outsize = insize; \
671da177e4SLinus Torvalds } while (0)
681da177e4SLinus Torvalds 
691da177e4SLinus Torvalds #define INSIZE(tag) sizeof(struct coda_ ## tag ## _in)
701da177e4SLinus Torvalds #define OUTSIZE(tag) sizeof(struct coda_ ## tag ## _out)
711da177e4SLinus Torvalds #define SIZE(tag)  max_t(unsigned int, INSIZE(tag), OUTSIZE(tag))
721da177e4SLinus Torvalds 
731da177e4SLinus Torvalds 
741da177e4SLinus Torvalds /* the upcalls */
751da177e4SLinus Torvalds int venus_rootfid(struct super_block *sb, struct CodaFid *fidp)
761da177e4SLinus Torvalds {
771da177e4SLinus Torvalds         union inputArgs *inp;
781da177e4SLinus Torvalds         union outputArgs *outp;
791da177e4SLinus Torvalds         int insize, outsize, error;
801da177e4SLinus Torvalds 
811da177e4SLinus Torvalds         insize = SIZE(root);
821da177e4SLinus Torvalds         UPARG(CODA_ROOT);
831da177e4SLinus Torvalds 
84a1b0aa87SJan Harkes 	error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);
85970648ebSJan Harkes 	if (!error)
861da177e4SLinus Torvalds 		*fidp = outp->coda_root.VFid;
871da177e4SLinus Torvalds 
881da177e4SLinus Torvalds 	CODA_FREE(inp, insize);
891da177e4SLinus Torvalds 	return error;
901da177e4SLinus Torvalds }
911da177e4SLinus Torvalds 
921da177e4SLinus Torvalds int venus_getattr(struct super_block *sb, struct CodaFid *fid,
931da177e4SLinus Torvalds 		     struct coda_vattr *attr)
941da177e4SLinus Torvalds {
951da177e4SLinus Torvalds         union inputArgs *inp;
961da177e4SLinus Torvalds         union outputArgs *outp;
971da177e4SLinus Torvalds         int insize, outsize, error;
981da177e4SLinus Torvalds 
991da177e4SLinus Torvalds         insize = SIZE(getattr);
1001da177e4SLinus Torvalds 	UPARG(CODA_GETATTR);
1011da177e4SLinus Torvalds         inp->coda_getattr.VFid = *fid;
1021da177e4SLinus Torvalds 
103a1b0aa87SJan Harkes 	error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);
104970648ebSJan Harkes 	if (!error)
1051da177e4SLinus Torvalds 		*attr = outp->coda_getattr.attr;
1061da177e4SLinus Torvalds 
1071da177e4SLinus Torvalds 	CODA_FREE(inp, insize);
1081da177e4SLinus Torvalds         return error;
1091da177e4SLinus Torvalds }
1101da177e4SLinus Torvalds 
1111da177e4SLinus Torvalds int venus_setattr(struct super_block *sb, struct CodaFid *fid,
1121da177e4SLinus Torvalds 		  struct coda_vattr *vattr)
1131da177e4SLinus Torvalds {
1141da177e4SLinus Torvalds         union inputArgs *inp;
1151da177e4SLinus Torvalds         union outputArgs *outp;
1161da177e4SLinus Torvalds         int insize, outsize, error;
1171da177e4SLinus Torvalds 
1181da177e4SLinus Torvalds 	insize = SIZE(setattr);
1191da177e4SLinus Torvalds 	UPARG(CODA_SETATTR);
1201da177e4SLinus Torvalds 
1211da177e4SLinus Torvalds         inp->coda_setattr.VFid = *fid;
1221da177e4SLinus Torvalds 	inp->coda_setattr.attr = *vattr;
1231da177e4SLinus Torvalds 
124a1b0aa87SJan Harkes 	error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);
1251da177e4SLinus Torvalds 
1261da177e4SLinus Torvalds         CODA_FREE(inp, insize);
1271da177e4SLinus Torvalds         return error;
1281da177e4SLinus Torvalds }
1291da177e4SLinus Torvalds 
1301da177e4SLinus Torvalds int venus_lookup(struct super_block *sb, struct CodaFid *fid,
1311da177e4SLinus Torvalds 		    const char *name, int length, int * type,
1321da177e4SLinus Torvalds 		    struct CodaFid *resfid)
1331da177e4SLinus Torvalds {
1341da177e4SLinus Torvalds         union inputArgs *inp;
1351da177e4SLinus Torvalds         union outputArgs *outp;
1361da177e4SLinus Torvalds         int insize, outsize, error;
1371da177e4SLinus Torvalds 	int offset;
1381da177e4SLinus Torvalds 
1391da177e4SLinus Torvalds 	offset = INSIZE(lookup);
1401da177e4SLinus Torvalds         insize = max_t(unsigned int, offset + length +1, OUTSIZE(lookup));
1411da177e4SLinus Torvalds 	UPARG(CODA_LOOKUP);
1421da177e4SLinus Torvalds 
1431da177e4SLinus Torvalds         inp->coda_lookup.VFid = *fid;
1441da177e4SLinus Torvalds 	inp->coda_lookup.name = offset;
1451da177e4SLinus Torvalds 	inp->coda_lookup.flags = CLU_CASE_SENSITIVE;
1461da177e4SLinus Torvalds         /* send Venus a null terminated string */
1471da177e4SLinus Torvalds         memcpy((char *)(inp) + offset, name, length);
1481da177e4SLinus Torvalds         *((char *)inp + offset + length) = '\0';
1491da177e4SLinus Torvalds 
150a1b0aa87SJan Harkes 	error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);
151970648ebSJan Harkes 	if (!error) {
1521da177e4SLinus Torvalds 		*resfid = outp->coda_lookup.VFid;
1531da177e4SLinus Torvalds 		*type = outp->coda_lookup.vtype;
154970648ebSJan Harkes 	}
1551da177e4SLinus Torvalds 
1561da177e4SLinus Torvalds 	CODA_FREE(inp, insize);
1571da177e4SLinus Torvalds 	return error;
1581da177e4SLinus Torvalds }
1591da177e4SLinus Torvalds 
1601da177e4SLinus Torvalds int venus_close(struct super_block *sb, struct CodaFid *fid, int flags,
161d83f5901SEric W. Biederman 		kuid_t uid)
1621da177e4SLinus Torvalds {
1631da177e4SLinus Torvalds 	union inputArgs *inp;
1641da177e4SLinus Torvalds 	union outputArgs *outp;
1651da177e4SLinus Torvalds 	int insize, outsize, error;
1661da177e4SLinus Torvalds 
1671da177e4SLinus Torvalds 	insize = SIZE(release);
1681da177e4SLinus Torvalds 	UPARG(CODA_CLOSE);
1691da177e4SLinus Torvalds 
170d83f5901SEric W. Biederman 	inp->ih.uid = from_kuid(&init_user_ns, uid);
1711da177e4SLinus Torvalds         inp->coda_close.VFid = *fid;
1721da177e4SLinus Torvalds         inp->coda_close.flags = flags;
1731da177e4SLinus Torvalds 
174a1b0aa87SJan Harkes 	error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);
1751da177e4SLinus Torvalds 
1761da177e4SLinus Torvalds 	CODA_FREE(inp, insize);
1771da177e4SLinus Torvalds         return error;
1781da177e4SLinus Torvalds }
1791da177e4SLinus Torvalds 
1801da177e4SLinus Torvalds int venus_open(struct super_block *sb, struct CodaFid *fid,
1811da177e4SLinus Torvalds 		  int flags, struct file **fh)
1821da177e4SLinus Torvalds {
1831da177e4SLinus Torvalds         union inputArgs *inp;
1841da177e4SLinus Torvalds         union outputArgs *outp;
1851da177e4SLinus Torvalds         int insize, outsize, error;
1861da177e4SLinus Torvalds 
1871da177e4SLinus Torvalds 	insize = SIZE(open_by_fd);
1881da177e4SLinus Torvalds 	UPARG(CODA_OPEN_BY_FD);
1891da177e4SLinus Torvalds 
19038c2e437SJan Harkes 	inp->coda_open_by_fd.VFid = *fid;
19138c2e437SJan Harkes 	inp->coda_open_by_fd.flags = flags;
1921da177e4SLinus Torvalds 
193a1b0aa87SJan Harkes 	error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);
19438c2e437SJan Harkes 	if (!error)
1951da177e4SLinus Torvalds 		*fh = outp->coda_open_by_fd.fh;
1961da177e4SLinus Torvalds 
1971da177e4SLinus Torvalds 	CODA_FREE(inp, insize);
1981da177e4SLinus Torvalds 	return error;
1991da177e4SLinus Torvalds }
2001da177e4SLinus Torvalds 
2011da177e4SLinus Torvalds int venus_mkdir(struct super_block *sb, struct CodaFid *dirfid,
2021da177e4SLinus Torvalds 		   const char *name, int length,
2031da177e4SLinus Torvalds 		   struct CodaFid *newfid, struct coda_vattr *attrs)
2041da177e4SLinus Torvalds {
2051da177e4SLinus Torvalds         union inputArgs *inp;
2061da177e4SLinus Torvalds         union outputArgs *outp;
2071da177e4SLinus Torvalds         int insize, outsize, error;
2081da177e4SLinus Torvalds         int offset;
2091da177e4SLinus Torvalds 
2101da177e4SLinus Torvalds 	offset = INSIZE(mkdir);
2111da177e4SLinus Torvalds 	insize = max_t(unsigned int, offset + length + 1, OUTSIZE(mkdir));
2121da177e4SLinus Torvalds 	UPARG(CODA_MKDIR);
2131da177e4SLinus Torvalds 
2141da177e4SLinus Torvalds         inp->coda_mkdir.VFid = *dirfid;
2151da177e4SLinus Torvalds         inp->coda_mkdir.attr = *attrs;
2161da177e4SLinus Torvalds 	inp->coda_mkdir.name = offset;
2171da177e4SLinus Torvalds         /* Venus must get null terminated string */
2181da177e4SLinus Torvalds         memcpy((char *)(inp) + offset, name, length);
2191da177e4SLinus Torvalds         *((char *)inp + offset + length) = '\0';
2201da177e4SLinus Torvalds 
221a1b0aa87SJan Harkes 	error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);
222970648ebSJan Harkes 	if (!error) {
2231da177e4SLinus Torvalds 		*attrs = outp->coda_mkdir.attr;
2241da177e4SLinus Torvalds 		*newfid = outp->coda_mkdir.VFid;
225970648ebSJan Harkes 	}
2261da177e4SLinus Torvalds 
2271da177e4SLinus Torvalds 	CODA_FREE(inp, insize);
2281da177e4SLinus Torvalds 	return error;
2291da177e4SLinus Torvalds }
2301da177e4SLinus Torvalds 
2311da177e4SLinus Torvalds 
2321da177e4SLinus Torvalds int venus_rename(struct super_block *sb, struct CodaFid *old_fid,
2331da177e4SLinus Torvalds 		 struct CodaFid *new_fid, size_t old_length,
2341da177e4SLinus Torvalds 		 size_t new_length, const char *old_name,
2351da177e4SLinus Torvalds 		 const char *new_name)
2361da177e4SLinus Torvalds {
2371da177e4SLinus Torvalds 	union inputArgs *inp;
2381da177e4SLinus Torvalds         union outputArgs *outp;
2391da177e4SLinus Torvalds         int insize, outsize, error;
2401da177e4SLinus Torvalds 	int offset, s;
2411da177e4SLinus Torvalds 
2421da177e4SLinus Torvalds 	offset = INSIZE(rename);
2431da177e4SLinus Torvalds 	insize = max_t(unsigned int, offset + new_length + old_length + 8,
2441da177e4SLinus Torvalds 		     OUTSIZE(rename));
2451da177e4SLinus Torvalds  	UPARG(CODA_RENAME);
2461da177e4SLinus Torvalds 
2471da177e4SLinus Torvalds         inp->coda_rename.sourceFid = *old_fid;
2481da177e4SLinus Torvalds         inp->coda_rename.destFid =  *new_fid;
2491da177e4SLinus Torvalds         inp->coda_rename.srcname = offset;
2501da177e4SLinus Torvalds 
2511da177e4SLinus Torvalds         /* Venus must receive an null terminated string */
2521da177e4SLinus Torvalds         s = ( old_length & ~0x3) +4; /* round up to word boundary */
2531da177e4SLinus Torvalds         memcpy((char *)(inp) + offset, old_name, old_length);
2541da177e4SLinus Torvalds         *((char *)inp + offset + old_length) = '\0';
2551da177e4SLinus Torvalds 
2561da177e4SLinus Torvalds         /* another null terminated string for Venus */
2571da177e4SLinus Torvalds         offset += s;
2581da177e4SLinus Torvalds         inp->coda_rename.destname = offset;
2591da177e4SLinus Torvalds         s = ( new_length & ~0x3) +4; /* round up to word boundary */
2601da177e4SLinus Torvalds         memcpy((char *)(inp) + offset, new_name, new_length);
2611da177e4SLinus Torvalds         *((char *)inp + offset + new_length) = '\0';
2621da177e4SLinus Torvalds 
263a1b0aa87SJan Harkes 	error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);
2641da177e4SLinus Torvalds 
2651da177e4SLinus Torvalds 	CODA_FREE(inp, insize);
2661da177e4SLinus Torvalds 	return error;
2671da177e4SLinus Torvalds }
2681da177e4SLinus Torvalds 
2691da177e4SLinus Torvalds int venus_create(struct super_block *sb, struct CodaFid *dirfid,
2701da177e4SLinus Torvalds 		 const char *name, int length, int excl, int mode,
2711da177e4SLinus Torvalds 		 struct CodaFid *newfid, struct coda_vattr *attrs)
2721da177e4SLinus Torvalds {
2731da177e4SLinus Torvalds         union inputArgs *inp;
2741da177e4SLinus Torvalds         union outputArgs *outp;
2751da177e4SLinus Torvalds         int insize, outsize, error;
2761da177e4SLinus Torvalds         int offset;
2771da177e4SLinus Torvalds 
2781da177e4SLinus Torvalds         offset = INSIZE(create);
2791da177e4SLinus Torvalds 	insize = max_t(unsigned int, offset + length + 1, OUTSIZE(create));
2801da177e4SLinus Torvalds 	UPARG(CODA_CREATE);
2811da177e4SLinus Torvalds 
2821da177e4SLinus Torvalds         inp->coda_create.VFid = *dirfid;
2831da177e4SLinus Torvalds         inp->coda_create.attr.va_mode = mode;
2841da177e4SLinus Torvalds 	inp->coda_create.excl = excl;
2851da177e4SLinus Torvalds         inp->coda_create.mode = mode;
2861da177e4SLinus Torvalds         inp->coda_create.name = offset;
2871da177e4SLinus Torvalds 
2881da177e4SLinus Torvalds         /* Venus must get null terminated string */
2891da177e4SLinus Torvalds         memcpy((char *)(inp) + offset, name, length);
2901da177e4SLinus Torvalds         *((char *)inp + offset + length) = '\0';
2911da177e4SLinus Torvalds 
292a1b0aa87SJan Harkes 	error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);
293970648ebSJan Harkes 	if (!error) {
2941da177e4SLinus Torvalds 		*attrs = outp->coda_create.attr;
2951da177e4SLinus Torvalds 		*newfid = outp->coda_create.VFid;
296970648ebSJan Harkes 	}
2971da177e4SLinus Torvalds 
2981da177e4SLinus Torvalds 	CODA_FREE(inp, insize);
2991da177e4SLinus Torvalds 	return error;
3001da177e4SLinus Torvalds }
3011da177e4SLinus Torvalds 
3021da177e4SLinus Torvalds int venus_rmdir(struct super_block *sb, struct CodaFid *dirfid,
3031da177e4SLinus Torvalds 		    const char *name, int length)
3041da177e4SLinus Torvalds {
3051da177e4SLinus Torvalds         union inputArgs *inp;
3061da177e4SLinus Torvalds         union outputArgs *outp;
3071da177e4SLinus Torvalds         int insize, outsize, error;
3081da177e4SLinus Torvalds         int offset;
3091da177e4SLinus Torvalds 
3101da177e4SLinus Torvalds         offset = INSIZE(rmdir);
3111da177e4SLinus Torvalds 	insize = max_t(unsigned int, offset + length + 1, OUTSIZE(rmdir));
3121da177e4SLinus Torvalds 	UPARG(CODA_RMDIR);
3131da177e4SLinus Torvalds 
3141da177e4SLinus Torvalds         inp->coda_rmdir.VFid = *dirfid;
3151da177e4SLinus Torvalds         inp->coda_rmdir.name = offset;
3161da177e4SLinus Torvalds         memcpy((char *)(inp) + offset, name, length);
3171da177e4SLinus Torvalds 	*((char *)inp + offset + length) = '\0';
3181da177e4SLinus Torvalds 
319a1b0aa87SJan Harkes 	error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);
3201da177e4SLinus Torvalds 
3211da177e4SLinus Torvalds 	CODA_FREE(inp, insize);
3221da177e4SLinus Torvalds 	return error;
3231da177e4SLinus Torvalds }
3241da177e4SLinus Torvalds 
3251da177e4SLinus Torvalds int venus_remove(struct super_block *sb, struct CodaFid *dirfid,
3261da177e4SLinus Torvalds 		    const char *name, int length)
3271da177e4SLinus Torvalds {
3281da177e4SLinus Torvalds         union inputArgs *inp;
3291da177e4SLinus Torvalds         union outputArgs *outp;
3301da177e4SLinus Torvalds         int error=0, insize, outsize, offset;
3311da177e4SLinus Torvalds 
3321da177e4SLinus Torvalds         offset = INSIZE(remove);
3331da177e4SLinus Torvalds 	insize = max_t(unsigned int, offset + length + 1, OUTSIZE(remove));
3341da177e4SLinus Torvalds 	UPARG(CODA_REMOVE);
3351da177e4SLinus Torvalds 
3361da177e4SLinus Torvalds         inp->coda_remove.VFid = *dirfid;
3371da177e4SLinus Torvalds         inp->coda_remove.name = offset;
3381da177e4SLinus Torvalds         memcpy((char *)(inp) + offset, name, length);
3391da177e4SLinus Torvalds 	*((char *)inp + offset + length) = '\0';
3401da177e4SLinus Torvalds 
341a1b0aa87SJan Harkes 	error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);
3421da177e4SLinus Torvalds 
3431da177e4SLinus Torvalds 	CODA_FREE(inp, insize);
3441da177e4SLinus Torvalds 	return error;
3451da177e4SLinus Torvalds }
3461da177e4SLinus Torvalds 
3471da177e4SLinus Torvalds int venus_readlink(struct super_block *sb, struct CodaFid *fid,
3481da177e4SLinus Torvalds 		      char *buffer, int *length)
3491da177e4SLinus Torvalds {
3501da177e4SLinus Torvalds         union inputArgs *inp;
3511da177e4SLinus Torvalds         union outputArgs *outp;
3521da177e4SLinus Torvalds         int insize, outsize, error;
3531da177e4SLinus Torvalds         int retlen;
3541da177e4SLinus Torvalds         char *result;
3551da177e4SLinus Torvalds 
3561da177e4SLinus Torvalds 	insize = max_t(unsigned int,
3573725e9ddSJan Harkes 		     INSIZE(readlink), OUTSIZE(readlink)+ *length);
3581da177e4SLinus Torvalds 	UPARG(CODA_READLINK);
3591da177e4SLinus Torvalds 
3601da177e4SLinus Torvalds         inp->coda_readlink.VFid = *fid;
3611da177e4SLinus Torvalds 
362a1b0aa87SJan Harkes 	error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);
3631da177e4SLinus Torvalds 	if (!error) {
3641da177e4SLinus Torvalds 		retlen = outp->coda_readlink.count;
3653725e9ddSJan Harkes 		if (retlen >= *length)
3663725e9ddSJan Harkes 			retlen = *length - 1;
3671da177e4SLinus Torvalds 		*length = retlen;
3681da177e4SLinus Torvalds 		result =  (char *)outp + (long)outp->coda_readlink.data;
3691da177e4SLinus Torvalds 		memcpy(buffer, result, retlen);
3701da177e4SLinus Torvalds 		*(buffer + retlen) = '\0';
3711da177e4SLinus Torvalds 	}
3721da177e4SLinus Torvalds 
3731da177e4SLinus Torvalds         CODA_FREE(inp, insize);
3741da177e4SLinus Torvalds         return error;
3751da177e4SLinus Torvalds }
3761da177e4SLinus Torvalds 
3771da177e4SLinus Torvalds 
3781da177e4SLinus Torvalds 
3791da177e4SLinus Torvalds int venus_link(struct super_block *sb, struct CodaFid *fid,
3801da177e4SLinus Torvalds 		  struct CodaFid *dirfid, const char *name, int len )
3811da177e4SLinus Torvalds {
3821da177e4SLinus Torvalds         union inputArgs *inp;
3831da177e4SLinus Torvalds         union outputArgs *outp;
3841da177e4SLinus Torvalds         int insize, outsize, error;
3851da177e4SLinus Torvalds         int offset;
3861da177e4SLinus Torvalds 
3871da177e4SLinus Torvalds 	offset = INSIZE(link);
3881da177e4SLinus Torvalds 	insize = max_t(unsigned int, offset  + len + 1, OUTSIZE(link));
3891da177e4SLinus Torvalds         UPARG(CODA_LINK);
3901da177e4SLinus Torvalds 
3911da177e4SLinus Torvalds         inp->coda_link.sourceFid = *fid;
3921da177e4SLinus Torvalds         inp->coda_link.destFid = *dirfid;
3931da177e4SLinus Torvalds         inp->coda_link.tname = offset;
3941da177e4SLinus Torvalds 
3951da177e4SLinus Torvalds         /* make sure strings are null terminated */
3961da177e4SLinus Torvalds         memcpy((char *)(inp) + offset, name, len);
3971da177e4SLinus Torvalds         *((char *)inp + offset + len) = '\0';
3981da177e4SLinus Torvalds 
399a1b0aa87SJan Harkes 	error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);
4001da177e4SLinus Torvalds 
4011da177e4SLinus Torvalds 	CODA_FREE(inp, insize);
4021da177e4SLinus Torvalds         return error;
4031da177e4SLinus Torvalds }
4041da177e4SLinus Torvalds 
4051da177e4SLinus Torvalds int venus_symlink(struct super_block *sb, struct CodaFid *fid,
4061da177e4SLinus Torvalds 		     const char *name, int len,
4071da177e4SLinus Torvalds 		     const char *symname, int symlen)
4081da177e4SLinus Torvalds {
4091da177e4SLinus Torvalds         union inputArgs *inp;
4101da177e4SLinus Torvalds         union outputArgs *outp;
4111da177e4SLinus Torvalds         int insize, outsize, error;
4121da177e4SLinus Torvalds         int offset, s;
4131da177e4SLinus Torvalds 
4141da177e4SLinus Torvalds         offset = INSIZE(symlink);
4151da177e4SLinus Torvalds 	insize = max_t(unsigned int, offset + len + symlen + 8, OUTSIZE(symlink));
4161da177e4SLinus Torvalds 	UPARG(CODA_SYMLINK);
4171da177e4SLinus Torvalds 
4181da177e4SLinus Torvalds         /*        inp->coda_symlink.attr = *tva; XXXXXX */
4191da177e4SLinus Torvalds         inp->coda_symlink.VFid = *fid;
4201da177e4SLinus Torvalds 
4211da177e4SLinus Torvalds 	/* Round up to word boundary and null terminate */
4221da177e4SLinus Torvalds         inp->coda_symlink.srcname = offset;
4231da177e4SLinus Torvalds         s = ( symlen  & ~0x3 ) + 4;
4241da177e4SLinus Torvalds         memcpy((char *)(inp) + offset, symname, symlen);
4251da177e4SLinus Torvalds         *((char *)inp + offset + symlen) = '\0';
4261da177e4SLinus Torvalds 
4271da177e4SLinus Torvalds 	/* Round up to word boundary and null terminate */
4281da177e4SLinus Torvalds         offset += s;
4291da177e4SLinus Torvalds         inp->coda_symlink.tname = offset;
4301da177e4SLinus Torvalds         s = (len & ~0x3) + 4;
4311da177e4SLinus Torvalds         memcpy((char *)(inp) + offset, name, len);
4321da177e4SLinus Torvalds         *((char *)inp + offset + len) = '\0';
4331da177e4SLinus Torvalds 
434a1b0aa87SJan Harkes 	error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);
4351da177e4SLinus Torvalds 
4361da177e4SLinus Torvalds 	CODA_FREE(inp, insize);
4371da177e4SLinus Torvalds         return error;
4381da177e4SLinus Torvalds }
4391da177e4SLinus Torvalds 
4401da177e4SLinus Torvalds int venus_fsync(struct super_block *sb, struct CodaFid *fid)
4411da177e4SLinus Torvalds {
4421da177e4SLinus Torvalds         union inputArgs *inp;
4431da177e4SLinus Torvalds         union outputArgs *outp;
4441da177e4SLinus Torvalds 	int insize, outsize, error;
4451da177e4SLinus Torvalds 
4461da177e4SLinus Torvalds 	insize=SIZE(fsync);
4471da177e4SLinus Torvalds 	UPARG(CODA_FSYNC);
4481da177e4SLinus Torvalds 
4491da177e4SLinus Torvalds 	inp->coda_fsync.VFid = *fid;
450d337b66aSJan Harkes 	error = coda_upcall(coda_vcp(sb), insize, &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 
8076e51f8aaSJan Harkes int coda_downcall(struct venus_comm *vcp, int opcode, union outputArgs *out,
8086e51f8aaSJan Harkes 		  size_t nbytes)
8091da177e4SLinus Torvalds {
8105fd31e9aSJan Harkes 	struct inode *inode = NULL;
811da47c19eSYoshihisa Abe 	struct CodaFid *fid = NULL, *newfid;
812f7cc02b8SYoshihisa Abe 	struct super_block *sb;
8135fd31e9aSJan Harkes 
8146e51f8aaSJan Harkes 	/*
8156e51f8aaSJan Harkes 	 * Make sure we have received enough data from the cache
8166e51f8aaSJan Harkes 	 * manager to populate the necessary fields in the buffer
8176e51f8aaSJan Harkes 	 */
8186e51f8aaSJan Harkes 	switch (opcode) {
8196e51f8aaSJan Harkes 	case CODA_PURGEUSER:
8206e51f8aaSJan Harkes 		if (nbytes < sizeof(struct coda_purgeuser_out))
8216e51f8aaSJan Harkes 			return -EINVAL;
8226e51f8aaSJan Harkes 		break;
8236e51f8aaSJan Harkes 
8246e51f8aaSJan Harkes 	case CODA_ZAPDIR:
8256e51f8aaSJan Harkes 		if (nbytes < sizeof(struct coda_zapdir_out))
8266e51f8aaSJan Harkes 			return -EINVAL;
8276e51f8aaSJan Harkes 		break;
8286e51f8aaSJan Harkes 
8296e51f8aaSJan Harkes 	case CODA_ZAPFILE:
8306e51f8aaSJan Harkes 		if (nbytes < sizeof(struct coda_zapfile_out))
8316e51f8aaSJan Harkes 			return -EINVAL;
8326e51f8aaSJan Harkes 		break;
8336e51f8aaSJan Harkes 
8346e51f8aaSJan Harkes 	case CODA_PURGEFID:
8356e51f8aaSJan Harkes 		if (nbytes < sizeof(struct coda_purgefid_out))
8366e51f8aaSJan Harkes 			return -EINVAL;
8376e51f8aaSJan Harkes 		break;
8386e51f8aaSJan Harkes 
8396e51f8aaSJan Harkes 	case CODA_REPLACE:
8406e51f8aaSJan Harkes 		if (nbytes < sizeof(struct coda_replace_out))
8416e51f8aaSJan Harkes 			return -EINVAL;
8426e51f8aaSJan Harkes 		break;
8436e51f8aaSJan Harkes 	}
8446e51f8aaSJan Harkes 
8451da177e4SLinus Torvalds 	/* Handle invalidation requests. */
846da47c19eSYoshihisa Abe 	mutex_lock(&vcp->vc_mutex);
847f7cc02b8SYoshihisa Abe 	sb = vcp->vc_sb;
8485fd31e9aSJan Harkes 	if (!sb || !sb->s_root)
849f7cc02b8SYoshihisa Abe 		goto unlock_out;
8501da177e4SLinus Torvalds 
8511da177e4SLinus Torvalds 	switch (opcode) {
8525fd31e9aSJan Harkes 	case CODA_FLUSH:
8531da177e4SLinus Torvalds 		coda_cache_clear_all(sb);
8541da177e4SLinus Torvalds 		shrink_dcache_sb(sb);
8552b0143b5SDavid Howells 		if (d_really_is_positive(sb->s_root))
8562b0143b5SDavid Howells 			coda_flag_inode(d_inode(sb->s_root), C_FLUSH);
8575fd31e9aSJan Harkes 		break;
8581da177e4SLinus Torvalds 
8595fd31e9aSJan Harkes 	case CODA_PURGEUSER:
8601da177e4SLinus Torvalds 		coda_cache_clear_all(sb);
8615fd31e9aSJan Harkes 		break;
8621da177e4SLinus Torvalds 
8635fd31e9aSJan Harkes 	case CODA_ZAPDIR:
8645fd31e9aSJan Harkes 		fid = &out->coda_zapdir.CodaFid;
8655fd31e9aSJan Harkes 		break;
8661da177e4SLinus Torvalds 
8675fd31e9aSJan Harkes 	case CODA_ZAPFILE:
8685fd31e9aSJan Harkes 		fid = &out->coda_zapfile.CodaFid;
8695fd31e9aSJan Harkes 		break;
8701da177e4SLinus Torvalds 
8715fd31e9aSJan Harkes 	case CODA_PURGEFID:
8725fd31e9aSJan Harkes 		fid = &out->coda_purgefid.CodaFid;
873da47c19eSYoshihisa Abe 		break;
874da47c19eSYoshihisa Abe 
875da47c19eSYoshihisa Abe 	case CODA_REPLACE:
876da47c19eSYoshihisa Abe 		fid = &out->coda_replace.OldFid;
877da47c19eSYoshihisa Abe 		break;
878da47c19eSYoshihisa Abe 	}
879da47c19eSYoshihisa Abe 	if (fid)
8801da177e4SLinus Torvalds 		inode = coda_fid_to_inode(fid, sb);
881da47c19eSYoshihisa Abe 
882da47c19eSYoshihisa Abe unlock_out:
883da47c19eSYoshihisa Abe 	mutex_unlock(&vcp->vc_mutex);
884da47c19eSYoshihisa Abe 
885da47c19eSYoshihisa Abe 	if (!inode)
886da47c19eSYoshihisa Abe 		return 0;
887da47c19eSYoshihisa Abe 
888da47c19eSYoshihisa Abe 	switch (opcode) {
889da47c19eSYoshihisa Abe 	case CODA_ZAPDIR:
890da47c19eSYoshihisa Abe 		coda_flag_inode_children(inode, C_PURGE);
891da47c19eSYoshihisa Abe 		coda_flag_inode(inode, C_VATTR);
892da47c19eSYoshihisa Abe 		break;
893da47c19eSYoshihisa Abe 
894da47c19eSYoshihisa Abe 	case CODA_ZAPFILE:
895da47c19eSYoshihisa Abe 		coda_flag_inode(inode, C_VATTR);
896da47c19eSYoshihisa Abe 		break;
897da47c19eSYoshihisa Abe 
898da47c19eSYoshihisa Abe 	case CODA_PURGEFID:
8991da177e4SLinus Torvalds 		coda_flag_inode_children(inode, C_PURGE);
9001da177e4SLinus Torvalds 
9011da177e4SLinus Torvalds 		/* catch the dentries later if some are still busy */
9021da177e4SLinus Torvalds 		coda_flag_inode(inode, C_PURGE);
9031da177e4SLinus Torvalds 		d_prune_aliases(inode);
9045fd31e9aSJan Harkes 		break;
9055fd31e9aSJan Harkes 
9065fd31e9aSJan Harkes 	case CODA_REPLACE:
9075fd31e9aSJan Harkes 		newfid = &out->coda_replace.NewFid;
9085fd31e9aSJan Harkes 		coda_replace_fid(inode, fid, newfid);
9095fd31e9aSJan Harkes 		break;
9101da177e4SLinus Torvalds 	}
9111da177e4SLinus Torvalds 	iput(inode);
9121da177e4SLinus Torvalds 	return 0;
9131da177e4SLinus Torvalds }
9141da177e4SLinus Torvalds 
915