brintos

brintos / linux-shallow public Read only

0
0
Text · 24.4 KiB · cd6a372 Raw
962 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Mostly platform independent upcall operations to Venus:4 *  -- upcalls5 *  -- upcall routines6 *7 * Linux 2.0 version8 * Copyright (C) 1996 Peter J. Braam <braam@maths.ox.ac.uk>, 9 * Michael Callahan <callahan@maths.ox.ac.uk> 10 * 11 * Redone for Linux 2.112 * Copyright (C) 1997 Carnegie Mellon University13 *14 * Carnegie Mellon University encourages users of this code to contribute15 * improvements to the Coda project. Contact Peter Braam <coda@cs.cmu.edu>.16 */17 18#include <linux/signal.h>19#include <linux/sched/signal.h>20#include <linux/types.h>21#include <linux/kernel.h>22#include <linux/mm.h>23#include <linux/time.h>24#include <linux/fs.h>25#include <linux/file.h>26#include <linux/stat.h>27#include <linux/errno.h>28#include <linux/string.h>29#include <linux/slab.h>30#include <linux/mutex.h>31#include <linux/uaccess.h>32#include <linux/vmalloc.h>33#include <linux/vfs.h>34 35#include <linux/coda.h>36#include "coda_psdev.h"37#include "coda_linux.h"38#include "coda_cache.h"39 40#include "coda_int.h"41 42static int coda_upcall(struct venus_comm *vc, int inSize, int *outSize,43		       union inputArgs *buffer);44 45static void *alloc_upcall(int opcode, int size)46{47	union inputArgs *inp;48 49	inp = kvzalloc(size, GFP_KERNEL);50        if (!inp)51		return ERR_PTR(-ENOMEM);52 53        inp->ih.opcode = opcode;54	inp->ih.pid = task_pid_nr_ns(current, &init_pid_ns);55	inp->ih.pgid = task_pgrp_nr_ns(current, &init_pid_ns);56	inp->ih.uid = from_kuid(&init_user_ns, current_fsuid());57 58	return (void*)inp;59}60 61#define UPARG(op)\62do {\63	inp = (union inputArgs *)alloc_upcall(op, insize); \64        if (IS_ERR(inp)) { return PTR_ERR(inp); }\65        outp = (union outputArgs *)(inp); \66        outsize = insize; \67} while (0)68 69#define INSIZE(tag) sizeof(struct coda_ ## tag ## _in)70#define OUTSIZE(tag) sizeof(struct coda_ ## tag ## _out)71#define SIZE(tag)  max_t(unsigned int, INSIZE(tag), OUTSIZE(tag))72 73 74/* the upcalls */75int venus_rootfid(struct super_block *sb, struct CodaFid *fidp)76{77        union inputArgs *inp;78        union outputArgs *outp;79        int insize, outsize, error;80 81        insize = SIZE(root);82        UPARG(CODA_ROOT);83 84	error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);85	if (!error)86		*fidp = outp->coda_root.VFid;87 88	kvfree(inp);89	return error;90}91 92int venus_getattr(struct super_block *sb, struct CodaFid *fid, 93		     struct coda_vattr *attr) 94{95        union inputArgs *inp;96        union outputArgs *outp;97        int insize, outsize, error;98 99        insize = SIZE(getattr); 100	UPARG(CODA_GETATTR);101        inp->coda_getattr.VFid = *fid;102 103	error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);104	if (!error)105		*attr = outp->coda_getattr.attr;106 107	kvfree(inp);108        return error;109}110 111int venus_setattr(struct super_block *sb, struct CodaFid *fid, 112		  struct coda_vattr *vattr)113{114        union inputArgs *inp;115        union outputArgs *outp;116        int insize, outsize, error;117	118	insize = SIZE(setattr);119	UPARG(CODA_SETATTR);120 121        inp->coda_setattr.VFid = *fid;122	inp->coda_setattr.attr = *vattr;123 124	error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);125 126	kvfree(inp);127        return error;128}129 130int venus_lookup(struct super_block *sb, struct CodaFid *fid, 131		    const char *name, int length, int * type, 132		    struct CodaFid *resfid)133{134        union inputArgs *inp;135        union outputArgs *outp;136        int insize, outsize, error;137	int offset;138 139	offset = INSIZE(lookup);140        insize = max_t(unsigned int, offset + length +1, OUTSIZE(lookup));141	UPARG(CODA_LOOKUP);142 143        inp->coda_lookup.VFid = *fid;144	inp->coda_lookup.name = offset;145	inp->coda_lookup.flags = CLU_CASE_SENSITIVE;146        /* send Venus a null terminated string */147        memcpy((char *)(inp) + offset, name, length);148        *((char *)inp + offset + length) = '\0';149 150	error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);151	if (!error) {152		*resfid = outp->coda_lookup.VFid;153		*type = outp->coda_lookup.vtype;154	}155 156	kvfree(inp);157	return error;158}159 160int venus_close(struct super_block *sb, struct CodaFid *fid, int flags,161		kuid_t uid)162{163	union inputArgs *inp;164	union outputArgs *outp;165	int insize, outsize, error;166	167	insize = SIZE(release);168	UPARG(CODA_CLOSE);169	170	inp->ih.uid = from_kuid(&init_user_ns, uid);171        inp->coda_close.VFid = *fid;172        inp->coda_close.flags = flags;173 174	error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);175 176	kvfree(inp);177        return error;178}179 180int venus_open(struct super_block *sb, struct CodaFid *fid,181		  int flags, struct file **fh)182{183        union inputArgs *inp;184        union outputArgs *outp;185        int insize, outsize, error;186       187	insize = SIZE(open_by_fd);188	UPARG(CODA_OPEN_BY_FD);189 190	inp->coda_open_by_fd.VFid = *fid;191	inp->coda_open_by_fd.flags = flags;192 193	error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);194	if (!error)195		*fh = outp->coda_open_by_fd.fh;196 197	kvfree(inp);198	return error;199}	200 201int venus_mkdir(struct super_block *sb, struct CodaFid *dirfid, 202		   const char *name, int length, 203		   struct CodaFid *newfid, struct coda_vattr *attrs)204{205        union inputArgs *inp;206        union outputArgs *outp;207        int insize, outsize, error;208        int offset;209 210	offset = INSIZE(mkdir);211	insize = max_t(unsigned int, offset + length + 1, OUTSIZE(mkdir));212	UPARG(CODA_MKDIR);213 214        inp->coda_mkdir.VFid = *dirfid;215        inp->coda_mkdir.attr = *attrs;216	inp->coda_mkdir.name = offset;217        /* Venus must get null terminated string */218        memcpy((char *)(inp) + offset, name, length);219        *((char *)inp + offset + length) = '\0';220 221	error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);222	if (!error) {223		*attrs = outp->coda_mkdir.attr;224		*newfid = outp->coda_mkdir.VFid;225	}226 227	kvfree(inp);228	return error;        229}230 231 232int venus_rename(struct super_block *sb, struct CodaFid *old_fid, 233		 struct CodaFid *new_fid, size_t old_length, 234		 size_t new_length, const char *old_name, 235		 const char *new_name)236{237	union inputArgs *inp;238        union outputArgs *outp;239        int insize, outsize, error; 240	int offset, s;241	242	offset = INSIZE(rename);243	insize = max_t(unsigned int, offset + new_length + old_length + 8,244		     OUTSIZE(rename)); 245 	UPARG(CODA_RENAME);246 247        inp->coda_rename.sourceFid = *old_fid;248        inp->coda_rename.destFid =  *new_fid;249        inp->coda_rename.srcname = offset;250 251        /* Venus must receive an null terminated string */252        s = ( old_length & ~0x3) +4; /* round up to word boundary */253        memcpy((char *)(inp) + offset, old_name, old_length);254        *((char *)inp + offset + old_length) = '\0';255 256        /* another null terminated string for Venus */257        offset += s;258        inp->coda_rename.destname = offset;259        s = ( new_length & ~0x3) +4; /* round up to word boundary */260        memcpy((char *)(inp) + offset, new_name, new_length);261        *((char *)inp + offset + new_length) = '\0';262 263	error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);264 265	kvfree(inp);266	return error;267}268 269int venus_create(struct super_block *sb, struct CodaFid *dirfid, 270		 const char *name, int length, int excl, int mode,271		 struct CodaFid *newfid, struct coda_vattr *attrs) 272{273        union inputArgs *inp;274        union outputArgs *outp;275        int insize, outsize, error;276        int offset;277 278        offset = INSIZE(create);279	insize = max_t(unsigned int, offset + length + 1, OUTSIZE(create));280	UPARG(CODA_CREATE);281 282        inp->coda_create.VFid = *dirfid;283        inp->coda_create.attr.va_mode = mode;284	inp->coda_create.excl = excl;285        inp->coda_create.mode = mode;286        inp->coda_create.name = offset;287 288        /* Venus must get null terminated string */289        memcpy((char *)(inp) + offset, name, length);290        *((char *)inp + offset + length) = '\0';291 292	error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);293	if (!error) {294		*attrs = outp->coda_create.attr;295		*newfid = outp->coda_create.VFid;296	}297 298	kvfree(inp);299	return error;        300}301 302int venus_rmdir(struct super_block *sb, struct CodaFid *dirfid, 303		    const char *name, int length)304{305        union inputArgs *inp;306        union outputArgs *outp;307        int insize, outsize, error;308        int offset;309 310        offset = INSIZE(rmdir);311	insize = max_t(unsigned int, offset + length + 1, OUTSIZE(rmdir));312	UPARG(CODA_RMDIR);313 314        inp->coda_rmdir.VFid = *dirfid;315        inp->coda_rmdir.name = offset;316        memcpy((char *)(inp) + offset, name, length);317	*((char *)inp + offset + length) = '\0';318 319	error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);320 321	kvfree(inp);322	return error;323}324 325int venus_remove(struct super_block *sb, struct CodaFid *dirfid, 326		    const char *name, int length)327{328        union inputArgs *inp;329        union outputArgs *outp;330        int error=0, insize, outsize, offset;331 332        offset = INSIZE(remove);333	insize = max_t(unsigned int, offset + length + 1, OUTSIZE(remove));334	UPARG(CODA_REMOVE);335 336        inp->coda_remove.VFid = *dirfid;337        inp->coda_remove.name = offset;338        memcpy((char *)(inp) + offset, name, length);339	*((char *)inp + offset + length) = '\0';340 341	error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);342 343	kvfree(inp);344	return error;345}346 347int venus_readlink(struct super_block *sb, struct CodaFid *fid, 348		      char *buffer, int *length)349{ 350        union inputArgs *inp;351        union outputArgs *outp;352        int insize, outsize, error;353        int retlen;354        char *result;355        356	insize = max_t(unsigned int,357		     INSIZE(readlink), OUTSIZE(readlink)+ *length);358	UPARG(CODA_READLINK);359 360        inp->coda_readlink.VFid = *fid;361 362	error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);363	if (!error) {364		retlen = outp->coda_readlink.count;365		if (retlen >= *length)366			retlen = *length - 1;367		*length = retlen;368		result =  (char *)outp + (long)outp->coda_readlink.data;369		memcpy(buffer, result, retlen);370		*(buffer + retlen) = '\0';371	}372 373	kvfree(inp);374        return error;375}376 377 378 379int venus_link(struct super_block *sb, struct CodaFid *fid, 380		  struct CodaFid *dirfid, const char *name, int len )381{382        union inputArgs *inp;383        union outputArgs *outp;384        int insize, outsize, error;385        int offset;386 387	offset = INSIZE(link);388	insize = max_t(unsigned int, offset  + len + 1, OUTSIZE(link));389        UPARG(CODA_LINK);390 391        inp->coda_link.sourceFid = *fid;392        inp->coda_link.destFid = *dirfid;393        inp->coda_link.tname = offset;394 395        /* make sure strings are null terminated */396        memcpy((char *)(inp) + offset, name, len);397        *((char *)inp + offset + len) = '\0';398 399	error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);400 401	kvfree(inp);402        return error;403}404 405int venus_symlink(struct super_block *sb, struct CodaFid *fid,406		     const char *name, int len,407		     const char *symname, int symlen)408{409        union inputArgs *inp;410        union outputArgs *outp;411        int insize, outsize, error;412        int offset, s;413 414        offset = INSIZE(symlink);415	insize = max_t(unsigned int, offset + len + symlen + 8, OUTSIZE(symlink));416	UPARG(CODA_SYMLINK);417        418        /*        inp->coda_symlink.attr = *tva; XXXXXX */ 419        inp->coda_symlink.VFid = *fid;420 421	/* Round up to word boundary and null terminate */422        inp->coda_symlink.srcname = offset;423        s = ( symlen  & ~0x3 ) + 4; 424        memcpy((char *)(inp) + offset, symname, symlen);425        *((char *)inp + offset + symlen) = '\0';426        427	/* Round up to word boundary and null terminate */428        offset += s;429        inp->coda_symlink.tname = offset;430        s = (len & ~0x3) + 4;431        memcpy((char *)(inp) + offset, name, len);432        *((char *)inp + offset + len) = '\0';433 434	error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);435 436	kvfree(inp);437        return error;438}439 440int venus_fsync(struct super_block *sb, struct CodaFid *fid)441{442        union inputArgs *inp;443        union outputArgs *outp; 444	int insize, outsize, error;445	446	insize=SIZE(fsync);447	UPARG(CODA_FSYNC);448 449	inp->coda_fsync.VFid = *fid;450	error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);451 452	kvfree(inp);453	return error;454}455 456int venus_access(struct super_block *sb, struct CodaFid *fid, int mask)457{458        union inputArgs *inp;459        union outputArgs *outp; 460	int insize, outsize, error;461 462	insize = SIZE(access);463	UPARG(CODA_ACCESS);464 465        inp->coda_access.VFid = *fid;466        inp->coda_access.flags = mask;467 468	error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);469 470	kvfree(inp);471	return error;472}473 474 475int venus_pioctl(struct super_block *sb, struct CodaFid *fid,476		 unsigned int cmd, struct PioctlData *data)477{478        union inputArgs *inp;479        union outputArgs *outp;  480	int insize, outsize, error;481	int iocsize;482 483	insize = VC_MAXMSGSIZE;484	UPARG(CODA_IOCTL);485 486        /* build packet for Venus */487        if (data->vi.in_size > VC_MAXDATASIZE) {488		error = -EINVAL;489		goto exit;490        }491 492        if (data->vi.out_size > VC_MAXDATASIZE) {493		error = -EINVAL;494		goto exit;495	}496 497        inp->coda_ioctl.VFid = *fid;498    499        /* the cmd field was mutated by increasing its size field to500         * reflect the path and follow args. We need to subtract that501         * out before sending the command to Venus.  */502        inp->coda_ioctl.cmd = (cmd & ~(PIOCPARM_MASK << 16));	503        iocsize = ((cmd >> 16) & PIOCPARM_MASK) - sizeof(char *) - sizeof(int);504        inp->coda_ioctl.cmd |= (iocsize & PIOCPARM_MASK) <<	16;	505    506        /* in->coda_ioctl.rwflag = flag; */507        inp->coda_ioctl.len = data->vi.in_size;508        inp->coda_ioctl.data = (char *)(INSIZE(ioctl));509     510        /* get the data out of user space */511	if (copy_from_user((char *)inp + (long)inp->coda_ioctl.data,512			   data->vi.in, data->vi.in_size)) {513		error = -EINVAL;514	        goto exit;515	}516 517	error = coda_upcall(coda_vcp(sb), SIZE(ioctl) + data->vi.in_size,518			    &outsize, inp);519 520        if (error) {521		pr_warn("%s: Venus returns: %d for %s\n",522			__func__, error, coda_f2s(fid));523		goto exit; 524	}525 526	if (outsize < (long)outp->coda_ioctl.data + outp->coda_ioctl.len) {527		error = -EINVAL;528		goto exit;529	}530        531	/* Copy out the OUT buffer. */532        if (outp->coda_ioctl.len > data->vi.out_size) {533		error = -EINVAL;534		goto exit;535        }536 537	/* Copy out the OUT buffer. */538	if (copy_to_user(data->vi.out,539			 (char *)outp + (long)outp->coda_ioctl.data,540			 outp->coda_ioctl.len)) {541		error = -EFAULT;542		goto exit;543	}544 545 exit:546	kvfree(inp);547	return error;548}549 550int venus_statfs(struct dentry *dentry, struct kstatfs *sfs)551{ 552        union inputArgs *inp;553        union outputArgs *outp;554        int insize, outsize, error;555        556	insize = SIZE(statfs);557	UPARG(CODA_STATFS);558 559	error = coda_upcall(coda_vcp(dentry->d_sb), insize, &outsize, inp);560	if (!error) {561		sfs->f_blocks = outp->coda_statfs.stat.f_blocks;562		sfs->f_bfree  = outp->coda_statfs.stat.f_bfree;563		sfs->f_bavail = outp->coda_statfs.stat.f_bavail;564		sfs->f_files  = outp->coda_statfs.stat.f_files;565		sfs->f_ffree  = outp->coda_statfs.stat.f_ffree;566	}567 568	kvfree(inp);569        return error;570}571 572int venus_access_intent(struct super_block *sb, struct CodaFid *fid,573			bool *access_intent_supported,574			size_t count, loff_t ppos, int type)575{576	union inputArgs *inp;577	union outputArgs *outp;578	int insize, outsize, error;579	bool finalizer =580		type == CODA_ACCESS_TYPE_READ_FINISH ||581		type == CODA_ACCESS_TYPE_WRITE_FINISH;582 583	if (!*access_intent_supported && !finalizer)584		return 0;585 586	insize = SIZE(access_intent);587	UPARG(CODA_ACCESS_INTENT);588 589	inp->coda_access_intent.VFid = *fid;590	inp->coda_access_intent.count = count;591	inp->coda_access_intent.pos = ppos;592	inp->coda_access_intent.type = type;593 594	error = coda_upcall(coda_vcp(sb), insize,595			    finalizer ? NULL : &outsize, inp);596 597	/*598	 * we have to free the request buffer for synchronous upcalls599	 * or when asynchronous upcalls fail, but not when asynchronous600	 * upcalls succeed601	 */602	if (!finalizer || error)603		kvfree(inp);604 605	/* Chunked access is not supported or an old Coda client */606	if (error == -EOPNOTSUPP) {607		*access_intent_supported = false;608		error = 0;609	}610	return error;611}612 613/*614 * coda_upcall and coda_downcall routines.615 */616static void coda_block_signals(sigset_t *old)617{618	spin_lock_irq(&current->sighand->siglock);619	*old = current->blocked;620 621	sigfillset(&current->blocked);622	sigdelset(&current->blocked, SIGKILL);623	sigdelset(&current->blocked, SIGSTOP);624	sigdelset(&current->blocked, SIGINT);625 626	recalc_sigpending();627	spin_unlock_irq(&current->sighand->siglock);628}629 630static void coda_unblock_signals(sigset_t *old)631{632	spin_lock_irq(&current->sighand->siglock);633	current->blocked = *old;634	recalc_sigpending();635	spin_unlock_irq(&current->sighand->siglock);636}637 638/* Don't allow signals to interrupt the following upcalls before venus639 * has seen them,640 * - CODA_CLOSE or CODA_RELEASE upcall  (to avoid reference count problems)641 * - CODA_STORE				(to avoid data loss)642 * - CODA_ACCESS_INTENT                 (to avoid reference count problems)643 */644#define CODA_INTERRUPTIBLE(r) (!coda_hard && \645			       (((r)->uc_opcode != CODA_CLOSE && \646				 (r)->uc_opcode != CODA_STORE && \647				 (r)->uc_opcode != CODA_ACCESS_INTENT && \648				 (r)->uc_opcode != CODA_RELEASE) || \649				(r)->uc_flags & CODA_REQ_READ))650 651static inline void coda_waitfor_upcall(struct venus_comm *vcp,652				       struct upc_req *req)653{654	DECLARE_WAITQUEUE(wait, current);655	unsigned long timeout = jiffies + coda_timeout * HZ;656	sigset_t old;657	int blocked;658 659	coda_block_signals(&old);660	blocked = 1;661 662	add_wait_queue(&req->uc_sleep, &wait);663	for (;;) {664		if (CODA_INTERRUPTIBLE(req))665			set_current_state(TASK_INTERRUPTIBLE);666		else667			set_current_state(TASK_UNINTERRUPTIBLE);668 669		/* got a reply */670		if (req->uc_flags & (CODA_REQ_WRITE | CODA_REQ_ABORT))671			break;672 673		if (blocked && time_after(jiffies, timeout) &&674		    CODA_INTERRUPTIBLE(req))675		{676			coda_unblock_signals(&old);677			blocked = 0;678		}679 680		if (signal_pending(current)) {681			list_del(&req->uc_chain);682			break;683		}684 685		mutex_unlock(&vcp->vc_mutex);686		if (blocked)687			schedule_timeout(HZ);688		else689			schedule();690		mutex_lock(&vcp->vc_mutex);691	}692	if (blocked)693		coda_unblock_signals(&old);694 695	remove_wait_queue(&req->uc_sleep, &wait);696	set_current_state(TASK_RUNNING);697}698 699 700/*701 * coda_upcall will return an error in the case of702 * failed communication with Venus _or_ will peek at Venus703 * reply and return Venus' error.704 *705 * As venus has 2 types of errors, normal errors (positive) and internal706 * errors (negative), normal errors are negated, while internal errors707 * are all mapped to -EINTR, while showing a nice warning message. (jh)708 */709static int coda_upcall(struct venus_comm *vcp,710		       int inSize, int *outSize,711		       union inputArgs *buffer)712{713	union outputArgs *out;714	union inputArgs *sig_inputArgs;715	struct upc_req *req = NULL, *sig_req;716	int error;717 718	mutex_lock(&vcp->vc_mutex);719 720	if (!vcp->vc_inuse) {721		pr_notice("Venus dead, not sending upcall\n");722		error = -ENXIO;723		goto exit;724	}725 726	/* Format the request message. */727	req = kmalloc(sizeof(struct upc_req), GFP_KERNEL);728	if (!req) {729		error = -ENOMEM;730		goto exit;731	}732 733	buffer->ih.unique = ++vcp->vc_seq;734 735	req->uc_data = (void *)buffer;736	req->uc_flags = outSize ? 0 : CODA_REQ_ASYNC;737	req->uc_inSize = inSize;738	req->uc_outSize = (outSize && *outSize) ? *outSize : inSize;739	req->uc_opcode = buffer->ih.opcode;740	req->uc_unique = buffer->ih.unique;741	init_waitqueue_head(&req->uc_sleep);742 743	/* Append msg to pending queue and poke Venus. */744	list_add_tail(&req->uc_chain, &vcp->vc_pending);745	wake_up_interruptible(&vcp->vc_waitq);746 747	/* We can return early on asynchronous requests */748	if (outSize == NULL) {749		mutex_unlock(&vcp->vc_mutex);750		return 0;751	}752 753	/* We can be interrupted while we wait for Venus to process754	 * our request.  If the interrupt occurs before Venus has read755	 * the request, we dequeue and return. If it occurs after the756	 * read but before the reply, we dequeue, send a signal757	 * message, and return. If it occurs after the reply we ignore758	 * it. In no case do we want to restart the syscall.  If it759	 * was interrupted by a venus shutdown (psdev_close), return760	 * ENODEV.  */761 762	/* Go to sleep.  Wake up on signals only after the timeout. */763	coda_waitfor_upcall(vcp, req);764 765	/* Op went through, interrupt or not... */766	if (req->uc_flags & CODA_REQ_WRITE) {767		out = (union outputArgs *)req->uc_data;768		/* here we map positive Venus errors to kernel errors */769		error = -out->oh.result;770		*outSize = req->uc_outSize;771		goto exit;772	}773 774	error = -EINTR;775	if ((req->uc_flags & CODA_REQ_ABORT) || !signal_pending(current)) {776		pr_warn("Unexpected interruption.\n");777		goto exit;778	}779 780	/* Interrupted before venus read it. */781	if (!(req->uc_flags & CODA_REQ_READ))782		goto exit;783 784	/* Venus saw the upcall, make sure we can send interrupt signal */785	if (!vcp->vc_inuse) {786		pr_info("Venus dead, not sending signal.\n");787		goto exit;788	}789 790	error = -ENOMEM;791	sig_req = kmalloc(sizeof(struct upc_req), GFP_KERNEL);792	if (!sig_req) goto exit;793 794	sig_inputArgs = kvzalloc(sizeof(*sig_inputArgs), GFP_KERNEL);795	if (!sig_inputArgs) {796		kfree(sig_req);797		goto exit;798	}799 800	error = -EINTR;801	sig_inputArgs->ih.opcode = CODA_SIGNAL;802	sig_inputArgs->ih.unique = req->uc_unique;803 804	sig_req->uc_flags = CODA_REQ_ASYNC;805	sig_req->uc_opcode = sig_inputArgs->ih.opcode;806	sig_req->uc_unique = sig_inputArgs->ih.unique;807	sig_req->uc_data = (void *)sig_inputArgs;808	sig_req->uc_inSize = sizeof(struct coda_in_hdr);809	sig_req->uc_outSize = sizeof(struct coda_in_hdr);810 811	/* insert at head of queue! */812	list_add(&(sig_req->uc_chain), &vcp->vc_pending);813	wake_up_interruptible(&vcp->vc_waitq);814 815exit:816	kfree(req);817	mutex_unlock(&vcp->vc_mutex);818	return error;819}820 821/*  822    The statements below are part of the Coda opportunistic823    programming -- taken from the Mach/BSD kernel code for Coda. 824    You don't get correct semantics by stating what needs to be825    done without guaranteeing the invariants needed for it to happen.826    When will be have time to find out what exactly is going on?  (pjb)827*/828 829 830/* 831 * There are 7 cases where cache invalidations occur.  The semantics832 *  of each is listed here:833 *834 * CODA_FLUSH     -- flush all entries from the name cache and the cnode cache.835 * CODA_PURGEUSER -- flush all entries from the name cache for a specific user836 *                  This call is a result of token expiration.837 *838 * The next arise as the result of callbacks on a file or directory.839 * CODA_ZAPFILE   -- flush the cached attributes for a file.840 841 * CODA_ZAPDIR    -- flush the attributes for the dir and842 *                  force a new lookup for all the children843                    of this dir.844 845 *846 * The next is a result of Venus detecting an inconsistent file.847 * CODA_PURGEFID  -- flush the attribute for the file848 *                  purge it and its children from the dcache849 *850 * The last  allows Venus to replace local fids with global ones851 * during reintegration.852 *853 * CODA_REPLACE -- replace one CodaFid with another throughout the name cache */854 855int coda_downcall(struct venus_comm *vcp, int opcode, union outputArgs *out,856		  size_t nbytes)857{858	struct inode *inode = NULL;859	struct CodaFid *fid = NULL, *newfid;860	struct super_block *sb;861 862	/*863	 * Make sure we have received enough data from the cache864	 * manager to populate the necessary fields in the buffer865	 */866	switch (opcode) {867	case CODA_PURGEUSER:868		if (nbytes < sizeof(struct coda_purgeuser_out))869			return -EINVAL;870		break;871 872	case CODA_ZAPDIR:873		if (nbytes < sizeof(struct coda_zapdir_out))874			return -EINVAL;875		break;876 877	case CODA_ZAPFILE:878		if (nbytes < sizeof(struct coda_zapfile_out))879			return -EINVAL;880		break;881 882	case CODA_PURGEFID:883		if (nbytes < sizeof(struct coda_purgefid_out))884			return -EINVAL;885		break;886 887	case CODA_REPLACE:888		if (nbytes < sizeof(struct coda_replace_out))889			return -EINVAL;890		break;891	}892 893	/* Handle invalidation requests. */894	mutex_lock(&vcp->vc_mutex);895	sb = vcp->vc_sb;896	if (!sb || !sb->s_root)897		goto unlock_out;898 899	switch (opcode) {900	case CODA_FLUSH:901		coda_cache_clear_all(sb);902		shrink_dcache_sb(sb);903		if (d_really_is_positive(sb->s_root))904			coda_flag_inode(d_inode(sb->s_root), C_FLUSH);905		break;906 907	case CODA_PURGEUSER:908		coda_cache_clear_all(sb);909		break;910 911	case CODA_ZAPDIR:912		fid = &out->coda_zapdir.CodaFid;913		break;914 915	case CODA_ZAPFILE:916		fid = &out->coda_zapfile.CodaFid;917		break;918 919	case CODA_PURGEFID:920		fid = &out->coda_purgefid.CodaFid;921		break;922 923	case CODA_REPLACE:924		fid = &out->coda_replace.OldFid;925		break;926	}927	if (fid)928		inode = coda_fid_to_inode(fid, sb);929 930unlock_out:931	mutex_unlock(&vcp->vc_mutex);932 933	if (!inode)934		return 0;935 936	switch (opcode) {937	case CODA_ZAPDIR:938		coda_flag_inode_children(inode, C_PURGE);939		coda_flag_inode(inode, C_VATTR);940		break;941 942	case CODA_ZAPFILE:943		coda_flag_inode(inode, C_VATTR);944		break;945 946	case CODA_PURGEFID:947		coda_flag_inode_children(inode, C_PURGE);948 949		/* catch the dentries later if some are still busy */950		coda_flag_inode(inode, C_PURGE);951		d_prune_aliases(inode);952		break;953 954	case CODA_REPLACE:955		newfid = &out->coda_replace.NewFid;956		coda_replace_fid(inode, fid, newfid);957		break;958	}959	iput(inode);960	return 0;961}962