xref: /openbmc/linux/fs/coda/upcall.c (revision 48df1335)
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>
368fc8b9dfSDavid Howells #include "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 
alloc_upcall(int opcode,int size)451da177e4SLinus Torvalds static void *alloc_upcall(int opcode, int size)
461da177e4SLinus Torvalds {
471da177e4SLinus Torvalds 	union inputArgs *inp;
481da177e4SLinus Torvalds 
494dc48193SDan Carpenter 	inp = kvzalloc(size, GFP_KERNEL);
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 */
venus_rootfid(struct super_block * sb,struct CodaFid * fidp)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 
88936dae45SDan Carpenter 	kvfree(inp);
891da177e4SLinus Torvalds 	return error;
901da177e4SLinus Torvalds }
911da177e4SLinus Torvalds 
venus_getattr(struct super_block * sb,struct CodaFid * fid,struct coda_vattr * attr)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 
107936dae45SDan Carpenter 	kvfree(inp);
1081da177e4SLinus Torvalds         return error;
1091da177e4SLinus Torvalds }
1101da177e4SLinus Torvalds 
venus_setattr(struct super_block * sb,struct CodaFid * fid,struct coda_vattr * vattr)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 
126936dae45SDan Carpenter 	kvfree(inp);
1271da177e4SLinus Torvalds         return error;
1281da177e4SLinus Torvalds }
1291da177e4SLinus Torvalds 
venus_lookup(struct super_block * sb,struct CodaFid * fid,const char * name,int length,int * type,struct CodaFid * resfid)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 
156936dae45SDan Carpenter 	kvfree(inp);
1571da177e4SLinus Torvalds 	return error;
1581da177e4SLinus Torvalds }
1591da177e4SLinus Torvalds 
venus_close(struct super_block * sb,struct CodaFid * fid,int flags,kuid_t uid)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 
176936dae45SDan Carpenter 	kvfree(inp);
1771da177e4SLinus Torvalds         return error;
1781da177e4SLinus Torvalds }
1791da177e4SLinus Torvalds 
venus_open(struct super_block * sb,struct CodaFid * fid,int flags,struct file ** fh)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 
197936dae45SDan Carpenter 	kvfree(inp);
1981da177e4SLinus Torvalds 	return error;
1991da177e4SLinus Torvalds }
2001da177e4SLinus Torvalds 
venus_mkdir(struct super_block * sb,struct CodaFid * dirfid,const char * name,int length,struct CodaFid * newfid,struct coda_vattr * attrs)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 
227936dae45SDan Carpenter 	kvfree(inp);
2281da177e4SLinus Torvalds 	return error;
2291da177e4SLinus Torvalds }
2301da177e4SLinus Torvalds 
2311da177e4SLinus Torvalds 
venus_rename(struct super_block * sb,struct CodaFid * old_fid,struct CodaFid * new_fid,size_t old_length,size_t new_length,const char * old_name,const char * new_name)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 
265936dae45SDan Carpenter 	kvfree(inp);
2661da177e4SLinus Torvalds 	return error;
2671da177e4SLinus Torvalds }
2681da177e4SLinus Torvalds 
venus_create(struct super_block * sb,struct CodaFid * dirfid,const char * name,int length,int excl,int mode,struct CodaFid * newfid,struct coda_vattr * attrs)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 
298936dae45SDan Carpenter 	kvfree(inp);
2991da177e4SLinus Torvalds 	return error;
3001da177e4SLinus Torvalds }
3011da177e4SLinus Torvalds 
venus_rmdir(struct super_block * sb,struct CodaFid * dirfid,const char * name,int length)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 
321936dae45SDan Carpenter 	kvfree(inp);
3221da177e4SLinus Torvalds 	return error;
3231da177e4SLinus Torvalds }
3241da177e4SLinus Torvalds 
venus_remove(struct super_block * sb,struct CodaFid * dirfid,const char * name,int length)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 
343936dae45SDan Carpenter 	kvfree(inp);
3441da177e4SLinus Torvalds 	return error;
3451da177e4SLinus Torvalds }
3461da177e4SLinus Torvalds 
venus_readlink(struct super_block * sb,struct CodaFid * fid,char * buffer,int * length)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 
373936dae45SDan Carpenter 	kvfree(inp);
3741da177e4SLinus Torvalds         return error;
3751da177e4SLinus Torvalds }
3761da177e4SLinus Torvalds 
3771da177e4SLinus Torvalds 
3781da177e4SLinus Torvalds 
venus_link(struct super_block * sb,struct CodaFid * fid,struct CodaFid * dirfid,const char * name,int len)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 
401936dae45SDan Carpenter 	kvfree(inp);
4021da177e4SLinus Torvalds         return error;
4031da177e4SLinus Torvalds }
4041da177e4SLinus Torvalds 
venus_symlink(struct super_block * sb,struct CodaFid * fid,const char * name,int len,const char * symname,int symlen)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 
436936dae45SDan Carpenter 	kvfree(inp);
4371da177e4SLinus Torvalds         return error;
4381da177e4SLinus Torvalds }
4391da177e4SLinus Torvalds 
venus_fsync(struct super_block * sb,struct CodaFid * fid)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 
452936dae45SDan Carpenter 	kvfree(inp);
4531da177e4SLinus Torvalds 	return error;
4541da177e4SLinus Torvalds }
4551da177e4SLinus Torvalds 
venus_access(struct super_block * sb,struct CodaFid * fid,int mask)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 
470936dae45SDan Carpenter 	kvfree(inp);
4711da177e4SLinus Torvalds 	return error;
4721da177e4SLinus Torvalds }
4731da177e4SLinus Torvalds 
4741da177e4SLinus Torvalds 
venus_pioctl(struct super_block * sb,struct CodaFid * fid,unsigned int cmd,struct PioctlData * data)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:
546936dae45SDan Carpenter 	kvfree(inp);
5471da177e4SLinus Torvalds 	return error;
5481da177e4SLinus Torvalds }
5491da177e4SLinus Torvalds 
venus_statfs(struct dentry * dentry,struct kstatfs * sfs)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 
55650e9a6efSFabian Frederick 	insize = SIZE(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 
568936dae45SDan Carpenter 	kvfree(inp);
5691da177e4SLinus Torvalds         return error;
5701da177e4SLinus Torvalds }
5711da177e4SLinus Torvalds 
venus_access_intent(struct super_block * sb,struct CodaFid * fid,bool * access_intent_supported,size_t count,loff_t ppos,int type)572a9fba24cSPedro Cuadra int venus_access_intent(struct super_block *sb, struct CodaFid *fid,
573a9fba24cSPedro Cuadra 			bool *access_intent_supported,
574a9fba24cSPedro Cuadra 			size_t count, loff_t ppos, int type)
575a9fba24cSPedro Cuadra {
576a9fba24cSPedro Cuadra 	union inputArgs *inp;
577a9fba24cSPedro Cuadra 	union outputArgs *outp;
578a9fba24cSPedro Cuadra 	int insize, outsize, error;
579a9fba24cSPedro Cuadra 	bool finalizer =
580a9fba24cSPedro Cuadra 		type == CODA_ACCESS_TYPE_READ_FINISH ||
581a9fba24cSPedro Cuadra 		type == CODA_ACCESS_TYPE_WRITE_FINISH;
582a9fba24cSPedro Cuadra 
583a9fba24cSPedro Cuadra 	if (!*access_intent_supported && !finalizer)
584a9fba24cSPedro Cuadra 		return 0;
585a9fba24cSPedro Cuadra 
586a9fba24cSPedro Cuadra 	insize = SIZE(access_intent);
587a9fba24cSPedro Cuadra 	UPARG(CODA_ACCESS_INTENT);
588a9fba24cSPedro Cuadra 
589a9fba24cSPedro Cuadra 	inp->coda_access_intent.VFid = *fid;
590a9fba24cSPedro Cuadra 	inp->coda_access_intent.count = count;
591a9fba24cSPedro Cuadra 	inp->coda_access_intent.pos = ppos;
592a9fba24cSPedro Cuadra 	inp->coda_access_intent.type = type;
593a9fba24cSPedro Cuadra 
594a9fba24cSPedro Cuadra 	error = coda_upcall(coda_vcp(sb), insize,
595a9fba24cSPedro Cuadra 			    finalizer ? NULL : &outsize, inp);
596a9fba24cSPedro Cuadra 
597a9fba24cSPedro Cuadra 	/*
598a9fba24cSPedro Cuadra 	 * we have to free the request buffer for synchronous upcalls
599a9fba24cSPedro Cuadra 	 * or when asynchronous upcalls fail, but not when asynchronous
600a9fba24cSPedro Cuadra 	 * upcalls succeed
601a9fba24cSPedro Cuadra 	 */
602a9fba24cSPedro Cuadra 	if (!finalizer || error)
603a9fba24cSPedro Cuadra 		kvfree(inp);
604a9fba24cSPedro Cuadra 
605a9fba24cSPedro Cuadra 	/* Chunked access is not supported or an old Coda client */
606a9fba24cSPedro Cuadra 	if (error == -EOPNOTSUPP) {
607a9fba24cSPedro Cuadra 		*access_intent_supported = false;
608a9fba24cSPedro Cuadra 		error = 0;
609a9fba24cSPedro Cuadra 	}
610a9fba24cSPedro Cuadra 	return error;
611a9fba24cSPedro Cuadra }
612a9fba24cSPedro Cuadra 
6131da177e4SLinus Torvalds /*
6141da177e4SLinus Torvalds  * coda_upcall and coda_downcall routines.
6151da177e4SLinus Torvalds  */
coda_block_signals(sigset_t * old)6165f47c7eaSAl Viro static void coda_block_signals(sigset_t *old)
617d9664c95SJan Harkes {
618d9664c95SJan Harkes 	spin_lock_irq(&current->sighand->siglock);
619d9664c95SJan Harkes 	*old = current->blocked;
6201da177e4SLinus Torvalds 
621d9664c95SJan Harkes 	sigfillset(&current->blocked);
622d9664c95SJan Harkes 	sigdelset(&current->blocked, SIGKILL);
623d9664c95SJan Harkes 	sigdelset(&current->blocked, SIGSTOP);
624d9664c95SJan Harkes 	sigdelset(&current->blocked, SIGINT);
625d9664c95SJan Harkes 
626d9664c95SJan Harkes 	recalc_sigpending();
627d9664c95SJan Harkes 	spin_unlock_irq(&current->sighand->siglock);
628d9664c95SJan Harkes }
629d9664c95SJan Harkes 
coda_unblock_signals(sigset_t * old)6305f47c7eaSAl Viro static void coda_unblock_signals(sigset_t *old)
631d9664c95SJan Harkes {
632d9664c95SJan Harkes 	spin_lock_irq(&current->sighand->siglock);
633d9664c95SJan Harkes 	current->blocked = *old;
634d9664c95SJan Harkes 	recalc_sigpending();
635d9664c95SJan Harkes 	spin_unlock_irq(&current->sighand->siglock);
636d9664c95SJan Harkes }
637d9664c95SJan Harkes 
638d9664c95SJan Harkes /* Don't allow signals to interrupt the following upcalls before venus
639d9664c95SJan Harkes  * has seen them,
640d9664c95SJan Harkes  * - CODA_CLOSE or CODA_RELEASE upcall  (to avoid reference count problems)
641d9664c95SJan Harkes  * - CODA_STORE				(to avoid data loss)
642a9fba24cSPedro Cuadra  * - CODA_ACCESS_INTENT                 (to avoid reference count problems)
643d9664c95SJan Harkes  */
644d9664c95SJan Harkes #define CODA_INTERRUPTIBLE(r) (!coda_hard && \
645d9664c95SJan Harkes 			       (((r)->uc_opcode != CODA_CLOSE && \
646d9664c95SJan Harkes 				 (r)->uc_opcode != CODA_STORE && \
647a9fba24cSPedro Cuadra 				 (r)->uc_opcode != CODA_ACCESS_INTENT && \
648d9664c95SJan Harkes 				 (r)->uc_opcode != CODA_RELEASE) || \
6494aeefdc6SJens Axboe 				(r)->uc_flags & CODA_REQ_READ))
650d9664c95SJan Harkes 
coda_waitfor_upcall(struct venus_comm * vcp,struct upc_req * req)651da47c19eSYoshihisa Abe static inline void coda_waitfor_upcall(struct venus_comm *vcp,
652da47c19eSYoshihisa Abe 				       struct upc_req *req)
6531da177e4SLinus Torvalds {
6541da177e4SLinus Torvalds 	DECLARE_WAITQUEUE(wait, current);
655d9664c95SJan Harkes 	unsigned long timeout = jiffies + coda_timeout * HZ;
656d9664c95SJan Harkes 	sigset_t old;
657d9664c95SJan Harkes 	int blocked;
6581da177e4SLinus Torvalds 
6595f47c7eaSAl Viro 	coda_block_signals(&old);
660d9664c95SJan Harkes 	blocked = 1;
6611da177e4SLinus Torvalds 
662d9664c95SJan Harkes 	add_wait_queue(&req->uc_sleep, &wait);
6631da177e4SLinus Torvalds 	for (;;) {
664d9664c95SJan Harkes 		if (CODA_INTERRUPTIBLE(req))
6651da177e4SLinus Torvalds 			set_current_state(TASK_INTERRUPTIBLE);
6661da177e4SLinus Torvalds 		else
6671da177e4SLinus Torvalds 			set_current_state(TASK_UNINTERRUPTIBLE);
6681da177e4SLinus Torvalds 
6691da177e4SLinus Torvalds 		/* got a reply */
6704aeefdc6SJens Axboe 		if (req->uc_flags & (CODA_REQ_WRITE | CODA_REQ_ABORT))
6711da177e4SLinus Torvalds 			break;
6721da177e4SLinus Torvalds 
673d9664c95SJan Harkes 		if (blocked && time_after(jiffies, timeout) &&
674d9664c95SJan Harkes 		    CODA_INTERRUPTIBLE(req))
675d9664c95SJan Harkes 		{
6765f47c7eaSAl Viro 			coda_unblock_signals(&old);
677d9664c95SJan Harkes 			blocked = 0;
678d9664c95SJan Harkes 		}
679d9664c95SJan Harkes 
680d9664c95SJan Harkes 		if (signal_pending(current)) {
681d9664c95SJan Harkes 			list_del(&req->uc_chain);
6821da177e4SLinus Torvalds 			break;
6831da177e4SLinus Torvalds 		}
684d9664c95SJan Harkes 
685da47c19eSYoshihisa Abe 		mutex_unlock(&vcp->vc_mutex);
686d9664c95SJan Harkes 		if (blocked)
687d9664c95SJan Harkes 			schedule_timeout(HZ);
688d9664c95SJan Harkes 		else
6891da177e4SLinus Torvalds 			schedule();
690da47c19eSYoshihisa Abe 		mutex_lock(&vcp->vc_mutex);
6911da177e4SLinus Torvalds 	}
692d9664c95SJan Harkes 	if (blocked)
6935f47c7eaSAl Viro 		coda_unblock_signals(&old);
6941da177e4SLinus Torvalds 
695d9664c95SJan Harkes 	remove_wait_queue(&req->uc_sleep, &wait);
696d9664c95SJan Harkes 	set_current_state(TASK_RUNNING);
6971da177e4SLinus Torvalds }
6981da177e4SLinus Torvalds 
6991da177e4SLinus Torvalds 
7001da177e4SLinus Torvalds /*
7011da177e4SLinus Torvalds  * coda_upcall will return an error in the case of
7021da177e4SLinus Torvalds  * failed communication with Venus _or_ will peek at Venus
7031da177e4SLinus Torvalds  * reply and return Venus' error.
7041da177e4SLinus Torvalds  *
7051da177e4SLinus Torvalds  * As venus has 2 types of errors, normal errors (positive) and internal
7061da177e4SLinus Torvalds  * errors (negative), normal errors are negated, while internal errors
7071da177e4SLinus Torvalds  * are all mapped to -EINTR, while showing a nice warning message. (jh)
7081da177e4SLinus Torvalds  */
coda_upcall(struct venus_comm * vcp,int inSize,int * outSize,union inputArgs * buffer)709a1b0aa87SJan Harkes static int coda_upcall(struct venus_comm *vcp,
7101da177e4SLinus Torvalds 		       int inSize, int *outSize,
7111da177e4SLinus Torvalds 		       union inputArgs *buffer)
7121da177e4SLinus Torvalds {
7131da177e4SLinus Torvalds 	union outputArgs *out;
714fe71b5f3SJan Harkes 	union inputArgs *sig_inputArgs;
715f7cc02b8SYoshihisa Abe 	struct upc_req *req = NULL, *sig_req;
716f7cc02b8SYoshihisa Abe 	int error;
717f7cc02b8SYoshihisa Abe 
718da47c19eSYoshihisa Abe 	mutex_lock(&vcp->vc_mutex);
7191da177e4SLinus Torvalds 
720a1b0aa87SJan Harkes 	if (!vcp->vc_inuse) {
721f38cfb25SFabian Frederick 		pr_notice("Venus dead, not sending upcall\n");
722f7cc02b8SYoshihisa Abe 		error = -ENXIO;
723f7cc02b8SYoshihisa Abe 		goto exit;
7241da177e4SLinus Torvalds 	}
7251da177e4SLinus Torvalds 
7261da177e4SLinus Torvalds 	/* Format the request message. */
72737461e19SJan Harkes 	req = kmalloc(sizeof(struct upc_req), GFP_KERNEL);
728f7cc02b8SYoshihisa Abe 	if (!req) {
729f7cc02b8SYoshihisa Abe 		error = -ENOMEM;
730f7cc02b8SYoshihisa Abe 		goto exit;
731f7cc02b8SYoshihisa Abe 	}
732fe71b5f3SJan Harkes 
733a9fba24cSPedro Cuadra 	buffer->ih.unique = ++vcp->vc_seq;
7341da177e4SLinus Torvalds 
735a9fba24cSPedro Cuadra 	req->uc_data = (void *)buffer;
736a9fba24cSPedro Cuadra 	req->uc_flags = outSize ? 0 : CODA_REQ_ASYNC;
737a9fba24cSPedro Cuadra 	req->uc_inSize = inSize;
738a9fba24cSPedro Cuadra 	req->uc_outSize = (outSize && *outSize) ? *outSize : inSize;
739a9fba24cSPedro Cuadra 	req->uc_opcode = buffer->ih.opcode;
740a9fba24cSPedro Cuadra 	req->uc_unique = buffer->ih.unique;
741a9fba24cSPedro Cuadra 	init_waitqueue_head(&req->uc_sleep);
7421da177e4SLinus Torvalds 
7431da177e4SLinus Torvalds 	/* Append msg to pending queue and poke Venus. */
744a1b0aa87SJan Harkes 	list_add_tail(&req->uc_chain, &vcp->vc_pending);
745a1b0aa87SJan Harkes 	wake_up_interruptible(&vcp->vc_waitq);
746a9fba24cSPedro Cuadra 
7473d8e72d9SJan Harkes 	/* We can return early on asynchronous requests */
7483d8e72d9SJan Harkes 	if (outSize == NULL) {
749a9fba24cSPedro Cuadra 		mutex_unlock(&vcp->vc_mutex);
750a9fba24cSPedro Cuadra 		return 0;
751a9fba24cSPedro Cuadra 	}
752a9fba24cSPedro Cuadra 
7531da177e4SLinus Torvalds 	/* We can be interrupted while we wait for Venus to process
7541da177e4SLinus Torvalds 	 * our request.  If the interrupt occurs before Venus has read
7551da177e4SLinus Torvalds 	 * the request, we dequeue and return. If it occurs after the
7561da177e4SLinus Torvalds 	 * read but before the reply, we dequeue, send a signal
7571da177e4SLinus Torvalds 	 * message, and return. If it occurs after the reply we ignore
7581da177e4SLinus Torvalds 	 * it. In no case do we want to restart the syscall.  If it
7591da177e4SLinus Torvalds 	 * was interrupted by a venus shutdown (psdev_close), return
7601da177e4SLinus Torvalds 	 * ENODEV.  */
7611da177e4SLinus Torvalds 
7621da177e4SLinus Torvalds 	/* Go to sleep.  Wake up on signals only after the timeout. */
763da47c19eSYoshihisa Abe 	coda_waitfor_upcall(vcp, req);
7641da177e4SLinus Torvalds 
7651da177e4SLinus Torvalds 	/* Op went through, interrupt or not... */
7664aeefdc6SJens Axboe 	if (req->uc_flags & CODA_REQ_WRITE) {
7671da177e4SLinus Torvalds 		out = (union outputArgs *)req->uc_data;
7681da177e4SLinus Torvalds 		/* here we map positive Venus errors to kernel errors */
7691da177e4SLinus Torvalds 		error = -out->oh.result;
7701da177e4SLinus Torvalds 		*outSize = req->uc_outSize;
7711da177e4SLinus Torvalds 		goto exit;
7721da177e4SLinus Torvalds 	}
773fe71b5f3SJan Harkes 
7741da177e4SLinus Torvalds 	error = -EINTR;
7754aeefdc6SJens Axboe 	if ((req->uc_flags & CODA_REQ_ABORT) || !signal_pending(current)) {
776f38cfb25SFabian Frederick 		pr_warn("Unexpected interruption.\n");
7771da177e4SLinus Torvalds 		goto exit;
7781da177e4SLinus Torvalds 	}
7791da177e4SLinus Torvalds 
780fe71b5f3SJan Harkes 	/* Interrupted before venus read it. */
7814aeefdc6SJens Axboe 	if (!(req->uc_flags & CODA_REQ_READ))
782fe71b5f3SJan Harkes 		goto exit;
783fe71b5f3SJan Harkes 
784fe71b5f3SJan Harkes 	/* Venus saw the upcall, make sure we can send interrupt signal */
785a1b0aa87SJan Harkes 	if (!vcp->vc_inuse) {
786f38cfb25SFabian Frederick 		pr_info("Venus dead, not sending signal.\n");
787fe71b5f3SJan Harkes 		goto exit;
788fe71b5f3SJan Harkes 	}
789fe71b5f3SJan Harkes 
7901da177e4SLinus Torvalds 	error = -ENOMEM;
79137461e19SJan Harkes 	sig_req = kmalloc(sizeof(struct upc_req), GFP_KERNEL);
7921da177e4SLinus Torvalds 	if (!sig_req) goto exit;
7931da177e4SLinus Torvalds 
794*48df1335SKees Cook 	sig_inputArgs = kvzalloc(sizeof(*sig_inputArgs), GFP_KERNEL);
795a9fba24cSPedro Cuadra 	if (!sig_inputArgs) {
79637461e19SJan Harkes 		kfree(sig_req);
7971da177e4SLinus Torvalds 		goto exit;
7981da177e4SLinus Torvalds 	}
7991da177e4SLinus Torvalds 
8001da177e4SLinus Torvalds 	error = -EINTR;
8011da177e4SLinus Torvalds 	sig_inputArgs->ih.opcode = CODA_SIGNAL;
8021da177e4SLinus Torvalds 	sig_inputArgs->ih.unique = req->uc_unique;
8031da177e4SLinus Torvalds 
8044aeefdc6SJens Axboe 	sig_req->uc_flags = CODA_REQ_ASYNC;
8051da177e4SLinus Torvalds 	sig_req->uc_opcode = sig_inputArgs->ih.opcode;
8061da177e4SLinus Torvalds 	sig_req->uc_unique = sig_inputArgs->ih.unique;
807a9fba24cSPedro Cuadra 	sig_req->uc_data = (void *)sig_inputArgs;
8081da177e4SLinus Torvalds 	sig_req->uc_inSize = sizeof(struct coda_in_hdr);
8091da177e4SLinus Torvalds 	sig_req->uc_outSize = sizeof(struct coda_in_hdr);
8101da177e4SLinus Torvalds 
8111da177e4SLinus Torvalds 	/* insert at head of queue! */
812a1b0aa87SJan Harkes 	list_add(&(sig_req->uc_chain), &vcp->vc_pending);
813a1b0aa87SJan Harkes 	wake_up_interruptible(&vcp->vc_waitq);
8141da177e4SLinus Torvalds 
8151da177e4SLinus Torvalds exit:
81637461e19SJan Harkes 	kfree(req);
817da47c19eSYoshihisa Abe 	mutex_unlock(&vcp->vc_mutex);
8181da177e4SLinus Torvalds 	return error;
8191da177e4SLinus Torvalds }
8201da177e4SLinus Torvalds 
8211da177e4SLinus Torvalds /*
8221da177e4SLinus Torvalds     The statements below are part of the Coda opportunistic
8231da177e4SLinus Torvalds     programming -- taken from the Mach/BSD kernel code for Coda.
8241da177e4SLinus Torvalds     You don't get correct semantics by stating what needs to be
8251da177e4SLinus Torvalds     done without guaranteeing the invariants needed for it to happen.
8261da177e4SLinus Torvalds     When will be have time to find out what exactly is going on?  (pjb)
8271da177e4SLinus Torvalds */
8281da177e4SLinus Torvalds 
8291da177e4SLinus Torvalds 
8301da177e4SLinus Torvalds /*
8311da177e4SLinus Torvalds  * There are 7 cases where cache invalidations occur.  The semantics
8321da177e4SLinus Torvalds  *  of each is listed here:
8331da177e4SLinus Torvalds  *
8341da177e4SLinus Torvalds  * CODA_FLUSH     -- flush all entries from the name cache and the cnode cache.
8351da177e4SLinus Torvalds  * CODA_PURGEUSER -- flush all entries from the name cache for a specific user
8361da177e4SLinus Torvalds  *                  This call is a result of token expiration.
8371da177e4SLinus Torvalds  *
8381da177e4SLinus Torvalds  * The next arise as the result of callbacks on a file or directory.
8391da177e4SLinus Torvalds  * CODA_ZAPFILE   -- flush the cached attributes for a file.
8401da177e4SLinus Torvalds 
8411da177e4SLinus Torvalds  * CODA_ZAPDIR    -- flush the attributes for the dir and
8421da177e4SLinus Torvalds  *                  force a new lookup for all the children
8431da177e4SLinus Torvalds                     of this dir.
8441da177e4SLinus Torvalds 
8451da177e4SLinus Torvalds  *
8461da177e4SLinus Torvalds  * The next is a result of Venus detecting an inconsistent file.
8471da177e4SLinus Torvalds  * CODA_PURGEFID  -- flush the attribute for the file
8481da177e4SLinus Torvalds  *                  purge it and its children from the dcache
8491da177e4SLinus Torvalds  *
8501da177e4SLinus Torvalds  * The last  allows Venus to replace local fids with global ones
8511da177e4SLinus Torvalds  * during reintegration.
8521da177e4SLinus Torvalds  *
8531da177e4SLinus Torvalds  * CODA_REPLACE -- replace one CodaFid with another throughout the name cache */
8541da177e4SLinus Torvalds 
coda_downcall(struct venus_comm * vcp,int opcode,union outputArgs * out,size_t nbytes)8556e51f8aaSJan Harkes int coda_downcall(struct venus_comm *vcp, int opcode, union outputArgs *out,
8566e51f8aaSJan Harkes 		  size_t nbytes)
8571da177e4SLinus Torvalds {
8585fd31e9aSJan Harkes 	struct inode *inode = NULL;
859da47c19eSYoshihisa Abe 	struct CodaFid *fid = NULL, *newfid;
860f7cc02b8SYoshihisa Abe 	struct super_block *sb;
8615fd31e9aSJan Harkes 
8626e51f8aaSJan Harkes 	/*
8636e51f8aaSJan Harkes 	 * Make sure we have received enough data from the cache
8646e51f8aaSJan Harkes 	 * manager to populate the necessary fields in the buffer
8656e51f8aaSJan Harkes 	 */
8666e51f8aaSJan Harkes 	switch (opcode) {
8676e51f8aaSJan Harkes 	case CODA_PURGEUSER:
8686e51f8aaSJan Harkes 		if (nbytes < sizeof(struct coda_purgeuser_out))
8696e51f8aaSJan Harkes 			return -EINVAL;
8706e51f8aaSJan Harkes 		break;
8716e51f8aaSJan Harkes 
8726e51f8aaSJan Harkes 	case CODA_ZAPDIR:
8736e51f8aaSJan Harkes 		if (nbytes < sizeof(struct coda_zapdir_out))
8746e51f8aaSJan Harkes 			return -EINVAL;
8756e51f8aaSJan Harkes 		break;
8766e51f8aaSJan Harkes 
8776e51f8aaSJan Harkes 	case CODA_ZAPFILE:
8786e51f8aaSJan Harkes 		if (nbytes < sizeof(struct coda_zapfile_out))
8796e51f8aaSJan Harkes 			return -EINVAL;
8806e51f8aaSJan Harkes 		break;
8816e51f8aaSJan Harkes 
8826e51f8aaSJan Harkes 	case CODA_PURGEFID:
8836e51f8aaSJan Harkes 		if (nbytes < sizeof(struct coda_purgefid_out))
8846e51f8aaSJan Harkes 			return -EINVAL;
8856e51f8aaSJan Harkes 		break;
8866e51f8aaSJan Harkes 
8876e51f8aaSJan Harkes 	case CODA_REPLACE:
8886e51f8aaSJan Harkes 		if (nbytes < sizeof(struct coda_replace_out))
8896e51f8aaSJan Harkes 			return -EINVAL;
8906e51f8aaSJan Harkes 		break;
8916e51f8aaSJan Harkes 	}
8926e51f8aaSJan Harkes 
8931da177e4SLinus Torvalds 	/* Handle invalidation requests. */
894da47c19eSYoshihisa Abe 	mutex_lock(&vcp->vc_mutex);
895f7cc02b8SYoshihisa Abe 	sb = vcp->vc_sb;
8965fd31e9aSJan Harkes 	if (!sb || !sb->s_root)
897f7cc02b8SYoshihisa Abe 		goto unlock_out;
8981da177e4SLinus Torvalds 
8991da177e4SLinus Torvalds 	switch (opcode) {
9005fd31e9aSJan Harkes 	case CODA_FLUSH:
9011da177e4SLinus Torvalds 		coda_cache_clear_all(sb);
9021da177e4SLinus Torvalds 		shrink_dcache_sb(sb);
9032b0143b5SDavid Howells 		if (d_really_is_positive(sb->s_root))
9042b0143b5SDavid Howells 			coda_flag_inode(d_inode(sb->s_root), C_FLUSH);
9055fd31e9aSJan Harkes 		break;
9061da177e4SLinus Torvalds 
9075fd31e9aSJan Harkes 	case CODA_PURGEUSER:
9081da177e4SLinus Torvalds 		coda_cache_clear_all(sb);
9095fd31e9aSJan Harkes 		break;
9101da177e4SLinus Torvalds 
9115fd31e9aSJan Harkes 	case CODA_ZAPDIR:
9125fd31e9aSJan Harkes 		fid = &out->coda_zapdir.CodaFid;
9135fd31e9aSJan Harkes 		break;
9141da177e4SLinus Torvalds 
9155fd31e9aSJan Harkes 	case CODA_ZAPFILE:
9165fd31e9aSJan Harkes 		fid = &out->coda_zapfile.CodaFid;
9175fd31e9aSJan Harkes 		break;
9181da177e4SLinus Torvalds 
9195fd31e9aSJan Harkes 	case CODA_PURGEFID:
9205fd31e9aSJan Harkes 		fid = &out->coda_purgefid.CodaFid;
921da47c19eSYoshihisa Abe 		break;
922da47c19eSYoshihisa Abe 
923da47c19eSYoshihisa Abe 	case CODA_REPLACE:
924da47c19eSYoshihisa Abe 		fid = &out->coda_replace.OldFid;
925da47c19eSYoshihisa Abe 		break;
926da47c19eSYoshihisa Abe 	}
927da47c19eSYoshihisa Abe 	if (fid)
9281da177e4SLinus Torvalds 		inode = coda_fid_to_inode(fid, sb);
929da47c19eSYoshihisa Abe 
930da47c19eSYoshihisa Abe unlock_out:
931da47c19eSYoshihisa Abe 	mutex_unlock(&vcp->vc_mutex);
932da47c19eSYoshihisa Abe 
933da47c19eSYoshihisa Abe 	if (!inode)
934da47c19eSYoshihisa Abe 		return 0;
935da47c19eSYoshihisa Abe 
936da47c19eSYoshihisa Abe 	switch (opcode) {
937da47c19eSYoshihisa Abe 	case CODA_ZAPDIR:
938da47c19eSYoshihisa Abe 		coda_flag_inode_children(inode, C_PURGE);
939da47c19eSYoshihisa Abe 		coda_flag_inode(inode, C_VATTR);
940da47c19eSYoshihisa Abe 		break;
941da47c19eSYoshihisa Abe 
942da47c19eSYoshihisa Abe 	case CODA_ZAPFILE:
943da47c19eSYoshihisa Abe 		coda_flag_inode(inode, C_VATTR);
944da47c19eSYoshihisa Abe 		break;
945da47c19eSYoshihisa Abe 
946da47c19eSYoshihisa Abe 	case CODA_PURGEFID:
9471da177e4SLinus Torvalds 		coda_flag_inode_children(inode, C_PURGE);
9481da177e4SLinus Torvalds 
9491da177e4SLinus Torvalds 		/* catch the dentries later if some are still busy */
9501da177e4SLinus Torvalds 		coda_flag_inode(inode, C_PURGE);
9511da177e4SLinus Torvalds 		d_prune_aliases(inode);
9525fd31e9aSJan Harkes 		break;
9535fd31e9aSJan Harkes 
9545fd31e9aSJan Harkes 	case CODA_REPLACE:
9555fd31e9aSJan Harkes 		newfid = &out->coda_replace.NewFid;
9565fd31e9aSJan Harkes 		coda_replace_fid(inode, fid, newfid);
9575fd31e9aSJan Harkes 		break;
9581da177e4SLinus Torvalds 	}
9591da177e4SLinus Torvalds 	iput(inode);
9601da177e4SLinus Torvalds 	return 0;
9611da177e4SLinus Torvalds }
962