xref: /netbsd-src/sys/coda/coda_psdev.c (revision b1c86f5f087524e68db12794ee9c3e3da1ab17a0)
1 /*	$NetBSD: coda_psdev.c,v 1.47 2009/01/11 02:45:46 christos Exp $	*/
2 
3 /*
4  *
5  *             Coda: an Experimental Distributed File System
6  *                              Release 3.1
7  *
8  *           Copyright (c) 1987-1998 Carnegie Mellon University
9  *                          All Rights Reserved
10  *
11  * Permission  to  use, copy, modify and distribute this software and its
12  * documentation is hereby granted,  provided  that  both  the  copyright
13  * notice  and  this  permission  notice  appear  in  all  copies  of the
14  * software, derivative works or  modified  versions,  and  any  portions
15  * thereof, and that both notices appear in supporting documentation, and
16  * that credit is given to Carnegie Mellon University  in  all  documents
17  * and publicity pertaining to direct or indirect use of this code or its
18  * derivatives.
19  *
20  * CODA IS AN EXPERIMENTAL SOFTWARE SYSTEM AND IS  KNOWN  TO  HAVE  BUGS,
21  * SOME  OF  WHICH MAY HAVE SERIOUS CONSEQUENCES.  CARNEGIE MELLON ALLOWS
22  * FREE USE OF THIS SOFTWARE IN ITS "AS IS" CONDITION.   CARNEGIE  MELLON
23  * DISCLAIMS  ANY  LIABILITY  OF  ANY  KIND  FOR  ANY  DAMAGES WHATSOEVER
24  * RESULTING DIRECTLY OR INDIRECTLY FROM THE USE OF THIS SOFTWARE  OR  OF
25  * ANY DERIVATIVE WORK.
26  *
27  * Carnegie  Mellon  encourages  users  of  this  software  to return any
28  * improvements or extensions that  they  make,  and  to  grant  Carnegie
29  * Mellon the rights to redistribute these changes without encumbrance.
30  *
31  * 	@(#) coda/coda_psdev.c,v 1.1.1.1 1998/08/29 21:26:45 rvb Exp $
32  */
33 
34 /*
35  * Mach Operating System
36  * Copyright (c) 1989 Carnegie-Mellon University
37  * All rights reserved.  The CMU software License Agreement specifies
38  * the terms and conditions for use and redistribution.
39  */
40 
41 /*
42  * This code was written for the Coda file system at Carnegie Mellon
43  * University.  Contributers include David Steere, James Kistler, and
44  * M. Satyanarayanan.  */
45 
46 /* These routines define the pseudo device for communication between
47  * Coda's Venus and Minicache in Mach 2.6. They used to be in cfs_subr.c,
48  * but I moved them to make it easier to port the Minicache without
49  * porting coda. -- DCS 10/12/94
50  *
51  * Following code depends on file-system CODA.
52  */
53 
54 /* These routines are the device entry points for Venus. */
55 
56 #include <sys/cdefs.h>
57 __KERNEL_RCSID(0, "$NetBSD: coda_psdev.c,v 1.47 2009/01/11 02:45:46 christos Exp $");
58 
59 extern int coda_nc_initialized;    /* Set if cache has been initialized */
60 
61 #ifndef _KERNEL_OPT
62 #define	NVCODA 4
63 #else
64 #include <vcoda.h>
65 #endif
66 
67 #include <sys/param.h>
68 #include <sys/systm.h>
69 #include <sys/kernel.h>
70 #include <sys/malloc.h>
71 #include <sys/proc.h>
72 #include <sys/mount.h>
73 #include <sys/file.h>
74 #include <sys/ioctl.h>
75 #include <sys/poll.h>
76 #include <sys/select.h>
77 #include <sys/conf.h>
78 #include <sys/atomic.h>
79 
80 #include <miscfs/syncfs/syncfs.h>
81 
82 #include <coda/coda.h>
83 #include <coda/cnode.h>
84 #include <coda/coda_namecache.h>
85 #include <coda/coda_io.h>
86 
87 #define CTL_C
88 
89 int coda_psdev_print_entry = 0;
90 static
91 int outstanding_upcalls = 0;
92 int coda_call_sleep = PZERO - 1;
93 #ifdef	CTL_C
94 int coda_pcatch = PCATCH;
95 #else
96 #endif
97 
98 #define ENTRY if(coda_psdev_print_entry) myprintf(("Entered %s\n",__func__))
99 
100 void vcodaattach(int n);
101 
102 dev_type_open(vc_nb_open);
103 dev_type_close(vc_nb_close);
104 dev_type_read(vc_nb_read);
105 dev_type_write(vc_nb_write);
106 dev_type_ioctl(vc_nb_ioctl);
107 dev_type_poll(vc_nb_poll);
108 dev_type_kqfilter(vc_nb_kqfilter);
109 
110 const struct cdevsw vcoda_cdevsw = {
111 	vc_nb_open, vc_nb_close, vc_nb_read, vc_nb_write, vc_nb_ioctl,
112 	nostop, notty, vc_nb_poll, nommap, vc_nb_kqfilter, D_OTHER,
113 };
114 
115 struct vmsg {
116     TAILQ_ENTRY(vmsg) vm_chain;
117     void *	 vm_data;
118     u_short	 vm_flags;
119     u_short      vm_inSize;	/* Size is at most 5000 bytes */
120     u_short	 vm_outSize;
121     u_short	 vm_opcode; 	/* copied from data to save ptr lookup */
122     int		 vm_unique;
123     void *	 vm_sleep;	/* Not used by Mach. */
124 };
125 
126 #define	VM_READ	    1
127 #define	VM_WRITE    2
128 #define	VM_INTR	    4
129 
130 /* vcodaattach: do nothing */
131 void
132 vcodaattach(int n)
133 {
134 }
135 
136 /*
137  * These functions are written for NetBSD.
138  */
139 int
140 vc_nb_open(dev_t dev, int flag, int mode,
141     struct lwp *l)
142 {
143     struct vcomm *vcp;
144 
145     ENTRY;
146 
147     if (minor(dev) >= NVCODA)
148 	return(ENXIO);
149 
150     if (!coda_nc_initialized)
151 	coda_nc_init();
152 
153     vcp = &coda_mnttbl[minor(dev)].mi_vcomm;
154     if (VC_OPEN(vcp))
155 	return(EBUSY);
156 
157     selinit(&vcp->vc_selproc);
158     TAILQ_INIT(&vcp->vc_requests);
159     TAILQ_INIT(&vcp->vc_replies);
160     MARK_VC_OPEN(vcp);
161 
162     coda_mnttbl[minor(dev)].mi_vfsp = NULL;
163     coda_mnttbl[minor(dev)].mi_rootvp = NULL;
164 
165     return(0);
166 }
167 
168 int
169 vc_nb_close(dev_t dev, int flag, int mode, struct lwp *l)
170 {
171     struct vcomm *vcp;
172     struct vmsg *vmp;
173     struct coda_mntinfo *mi;
174     int                 err;
175 
176     ENTRY;
177 
178     if (minor(dev) >= NVCODA)
179 	return(ENXIO);
180 
181     mi = &coda_mnttbl[minor(dev)];
182     vcp = &(mi->mi_vcomm);
183 
184     if (!VC_OPEN(vcp))
185 	panic("vcclose: not open");
186 
187     /* prevent future operations on this vfs from succeeding by auto-
188      * unmounting any vfs mounted via this device. This frees user or
189      * sysadm from having to remember where all mount points are located.
190      * Put this before WAKEUPs to avoid queuing new messages between
191      * the WAKEUP and the unmount (which can happen if we're unlucky)
192      */
193     if (!mi->mi_rootvp) {
194 	/* just a simple open/close w no mount */
195 	MARK_VC_CLOSED(vcp);
196 	return 0;
197     }
198 
199     /* Let unmount know this is for real */
200     atomic_inc_uint(&mi->mi_vfsp->mnt_refcnt);
201     VTOC(mi->mi_rootvp)->c_flags |= C_UNMOUNTING;
202     coda_unmounting(mi->mi_vfsp);
203 
204     /* Wakeup clients so they can return. */
205     while ((vmp = TAILQ_FIRST(&vcp->vc_requests)) != NULL) {
206 	TAILQ_REMOVE(&vcp->vc_requests, vmp, vm_chain);
207 
208 	/* Free signal request messages and don't wakeup cause
209 	   no one is waiting. */
210 	if (vmp->vm_opcode == CODA_SIGNAL) {
211 	    CODA_FREE(vmp->vm_data, VC_IN_NO_DATA);
212 	    CODA_FREE(vmp, sizeof(struct vmsg));
213 	    continue;
214 	}
215 	outstanding_upcalls++;
216 	wakeup(&vmp->vm_sleep);
217     }
218 
219     while ((vmp = TAILQ_FIRST(&vcp->vc_replies)) != NULL) {
220 	TAILQ_REMOVE(&vcp->vc_replies, vmp, vm_chain);
221 
222 	outstanding_upcalls++;
223 	wakeup(&vmp->vm_sleep);
224     }
225 
226     MARK_VC_CLOSED(vcp);
227 
228     if (outstanding_upcalls) {
229 #ifdef	CODA_VERBOSE
230 	printf("presleep: outstanding_upcalls = %d\n", outstanding_upcalls);
231     	(void) tsleep(&outstanding_upcalls, coda_call_sleep, "coda_umount", 0);
232 	printf("postsleep: outstanding_upcalls = %d\n", outstanding_upcalls);
233 #else
234     	(void) tsleep(&outstanding_upcalls, coda_call_sleep, "coda_umount", 0);
235 #endif
236     }
237 
238     err = dounmount(mi->mi_vfsp, flag, l);
239     if (err)
240 	myprintf(("Error %d unmounting vfs in vcclose(%llu)\n",
241 	           err, (unsigned long long)minor(dev)));
242     seldestroy(&vcp->vc_selproc);
243     return 0;
244 }
245 
246 int
247 vc_nb_read(dev_t dev, struct uio *uiop, int flag)
248 {
249     struct vcomm *	vcp;
250     struct vmsg *vmp;
251     int error = 0;
252 
253     ENTRY;
254 
255     if (minor(dev) >= NVCODA)
256 	return(ENXIO);
257 
258     vcp = &coda_mnttbl[minor(dev)].mi_vcomm;
259 
260     /* Get message at head of request queue. */
261     vmp = TAILQ_FIRST(&vcp->vc_requests);
262     if (vmp == NULL)
263 	return(0);	/* Nothing to read */
264 
265     /* Move the input args into userspace */
266     uiop->uio_rw = UIO_READ;
267     error = uiomove(vmp->vm_data, vmp->vm_inSize, uiop);
268     if (error) {
269 	myprintf(("vcread: error (%d) on uiomove\n", error));
270 	error = EINVAL;
271     }
272 
273     TAILQ_REMOVE(&vcp->vc_requests, vmp, vm_chain);
274 
275     /* If request was a signal, free up the message and don't
276        enqueue it in the reply queue. */
277     if (vmp->vm_opcode == CODA_SIGNAL) {
278 	if (codadebug)
279 	    myprintf(("vcread: signal msg (%d, %d)\n",
280 		      vmp->vm_opcode, vmp->vm_unique));
281 	CODA_FREE(vmp->vm_data, VC_IN_NO_DATA);
282 	CODA_FREE(vmp, sizeof(struct vmsg));
283 	return(error);
284     }
285 
286     vmp->vm_flags |= VM_READ;
287     TAILQ_INSERT_TAIL(&vcp->vc_replies, vmp, vm_chain);
288 
289     return(error);
290 }
291 
292 int
293 vc_nb_write(dev_t dev, struct uio *uiop, int flag)
294 {
295     struct vcomm *	vcp;
296     struct vmsg *vmp;
297     struct coda_out_hdr *out;
298     u_long seq;
299     u_long opcode;
300     int tbuf[2];
301     int error = 0;
302 
303     ENTRY;
304 
305     if (minor(dev) >= NVCODA)
306 	return(ENXIO);
307 
308     vcp = &coda_mnttbl[minor(dev)].mi_vcomm;
309 
310     /* Peek at the opcode, unique without transfering the data. */
311     uiop->uio_rw = UIO_WRITE;
312     error = uiomove(tbuf, sizeof(int) * 2, uiop);
313     if (error) {
314 	myprintf(("vcwrite: error (%d) on uiomove\n", error));
315 	return(EINVAL);
316     }
317 
318     opcode = tbuf[0];
319     seq = tbuf[1];
320 
321     if (codadebug)
322 	myprintf(("vcwrite got a call for %ld.%ld\n", opcode, seq));
323 
324     if (DOWNCALL(opcode)) {
325 	union outputArgs pbuf;
326 
327 	/* get the rest of the data. */
328 	uiop->uio_rw = UIO_WRITE;
329 	error = uiomove(&pbuf.coda_purgeuser.oh.result, sizeof(pbuf) - (sizeof(int)*2), uiop);
330 	if (error) {
331 	    myprintf(("vcwrite: error (%d) on uiomove (Op %ld seq %ld)\n",
332 		      error, opcode, seq));
333 	    return(EINVAL);
334 	    }
335 
336 	return handleDownCall(opcode, &pbuf);
337     }
338 
339     /* Look for the message on the (waiting for) reply queue. */
340     TAILQ_FOREACH(vmp, &vcp->vc_replies, vm_chain) {
341 	if (vmp->vm_unique == seq) break;
342     }
343 
344     if (vmp == NULL) {
345 	if (codadebug)
346 	    myprintf(("vcwrite: msg (%ld, %ld) not found\n", opcode, seq));
347 
348 	return(ESRCH);
349     }
350 
351     /* Remove the message from the reply queue */
352     TAILQ_REMOVE(&vcp->vc_replies, vmp, vm_chain);
353 
354     /* move data into response buffer. */
355     out = (struct coda_out_hdr *)vmp->vm_data;
356     /* Don't need to copy opcode and uniquifier. */
357 
358     /* get the rest of the data. */
359     if (vmp->vm_outSize < uiop->uio_resid) {
360 	myprintf(("vcwrite: more data than asked for (%d < %lu)\n",
361 		  vmp->vm_outSize, (unsigned long) uiop->uio_resid));
362 	wakeup(&vmp->vm_sleep); 	/* Notify caller of the error. */
363 	return(EINVAL);
364     }
365 
366     tbuf[0] = uiop->uio_resid; 	/* Save this value. */
367     uiop->uio_rw = UIO_WRITE;
368     error = uiomove(&out->result, vmp->vm_outSize - (sizeof(int) * 2), uiop);
369     if (error) {
370 	myprintf(("vcwrite: error (%d) on uiomove (op %ld seq %ld)\n",
371 		  error, opcode, seq));
372 	return(EINVAL);
373     }
374 
375     /* I don't think these are used, but just in case. */
376     /* XXX - aren't these two already correct? -bnoble */
377     out->opcode = opcode;
378     out->unique = seq;
379     vmp->vm_outSize	= tbuf[0];	/* Amount of data transferred? */
380     vmp->vm_flags |= VM_WRITE;
381     wakeup(&vmp->vm_sleep);
382 
383     return(0);
384 }
385 
386 int
387 vc_nb_ioctl(dev_t dev, u_long cmd, void *addr, int flag,
388     struct lwp *l)
389 {
390     ENTRY;
391 
392     switch(cmd) {
393     case CODARESIZE: {
394 	struct coda_resize *data = (struct coda_resize *)addr;
395 	return(coda_nc_resize(data->hashsize, data->heapsize, IS_DOWNCALL));
396 	break;
397     }
398     case CODASTATS:
399 	if (coda_nc_use) {
400 	    coda_nc_gather_stats();
401 	    return(0);
402 	} else {
403 	    return(ENODEV);
404 	}
405 	break;
406     case CODAPRINT:
407 	if (coda_nc_use) {
408 	    print_coda_nc();
409 	    return(0);
410 	} else {
411 	    return(ENODEV);
412 	}
413 	break;
414     case CIOC_KERNEL_VERSION:
415 	switch (*(u_int *)addr) {
416 	case 0:
417 		*(u_int *)addr = coda_kernel_version;
418 		return 0;
419 		break;
420 	case 1:
421 	case 2:
422 		if (coda_kernel_version != *(u_int *)addr)
423 		    return ENOENT;
424 		else
425 		    return 0;
426 	default:
427 		return ENOENT;
428 	}
429     	break;
430     default :
431 	return(EINVAL);
432 	break;
433     }
434 }
435 
436 int
437 vc_nb_poll(dev_t dev, int events, struct lwp *l)
438 {
439     struct vcomm *vcp;
440     int event_msk = 0;
441 
442     ENTRY;
443 
444     if (minor(dev) >= NVCODA)
445 	return(ENXIO);
446 
447     vcp = &coda_mnttbl[minor(dev)].mi_vcomm;
448 
449     event_msk = events & (POLLIN|POLLRDNORM);
450     if (!event_msk)
451 	return(0);
452 
453     if (!TAILQ_EMPTY(&vcp->vc_requests))
454 	return(events & (POLLIN|POLLRDNORM));
455 
456     selrecord(l, &(vcp->vc_selproc));
457 
458     return(0);
459 }
460 
461 static void
462 filt_vc_nb_detach(struct knote *kn)
463 {
464 	struct vcomm *vcp = kn->kn_hook;
465 
466 	SLIST_REMOVE(&vcp->vc_selproc.sel_klist, kn, knote, kn_selnext);
467 }
468 
469 static int
470 filt_vc_nb_read(struct knote *kn, long hint)
471 {
472 	struct vcomm *vcp = kn->kn_hook;
473 	struct vmsg *vmp;
474 
475 	vmp = TAILQ_FIRST(&vcp->vc_requests);
476 	if (vmp == NULL)
477 		return (0);
478 
479 	kn->kn_data = vmp->vm_inSize;
480 	return (1);
481 }
482 
483 static const struct filterops vc_nb_read_filtops =
484 	{ 1, NULL, filt_vc_nb_detach, filt_vc_nb_read };
485 
486 int
487 vc_nb_kqfilter(dev_t dev, struct knote *kn)
488 {
489 	struct vcomm *vcp;
490 	struct klist *klist;
491 
492 	ENTRY;
493 
494 	if (minor(dev) >= NVCODA)
495 		return(ENXIO);
496 
497 	vcp = &coda_mnttbl[minor(dev)].mi_vcomm;
498 
499 	switch (kn->kn_filter) {
500 	case EVFILT_READ:
501 		klist = &vcp->vc_selproc.sel_klist;
502 		kn->kn_fop = &vc_nb_read_filtops;
503 		break;
504 
505 	default:
506 		return (EINVAL);
507 	}
508 
509 	kn->kn_hook = vcp;
510 
511 	SLIST_INSERT_HEAD(klist, kn, kn_selnext);
512 
513 	return (0);
514 }
515 
516 /*
517  * Statistics
518  */
519 struct coda_clstat coda_clstat;
520 
521 /*
522  * Key question: whether to sleep interruptably or uninterruptably when
523  * waiting for Venus.  The former seems better (cause you can ^C a
524  * job), but then GNU-EMACS completion breaks. Use tsleep with no
525  * timeout, and no longjmp happens. But, when sleeping
526  * "uninterruptibly", we don't get told if it returns abnormally
527  * (e.g. kill -9).
528  */
529 
530 int
531 coda_call(struct coda_mntinfo *mntinfo, int inSize, int *outSize,
532 	void *buffer)
533 {
534 	struct vcomm *vcp;
535 	struct vmsg *vmp;
536 	int error;
537 #ifdef	CTL_C
538 	struct lwp *l = curlwp;
539 	struct proc *p = l->l_proc;
540 	sigset_t psig_omask;
541 	int i;
542 	psig_omask = l->l_sigmask;	/* XXXSA */
543 #endif
544 	if (mntinfo == NULL) {
545 	    /* Unlikely, but could be a race condition with a dying warden */
546 	    return ENODEV;
547 	}
548 
549 	vcp = &(mntinfo->mi_vcomm);
550 
551 	coda_clstat.ncalls++;
552 	coda_clstat.reqs[((struct coda_in_hdr *)buffer)->opcode]++;
553 
554 	if (!VC_OPEN(vcp))
555 	    return(ENODEV);
556 
557 	CODA_ALLOC(vmp,struct vmsg *,sizeof(struct vmsg));
558 	/* Format the request message. */
559 	vmp->vm_data = buffer;
560 	vmp->vm_flags = 0;
561 	vmp->vm_inSize = inSize;
562 	vmp->vm_outSize
563 	    = *outSize ? *outSize : inSize; /* |buffer| >= inSize */
564 	vmp->vm_opcode = ((struct coda_in_hdr *)buffer)->opcode;
565 	vmp->vm_unique = ++vcp->vc_seq;
566 	if (codadebug)
567 	    myprintf(("Doing a call for %d.%d\n",
568 		      vmp->vm_opcode, vmp->vm_unique));
569 
570 	/* Fill in the common input args. */
571 	((struct coda_in_hdr *)buffer)->unique = vmp->vm_unique;
572 
573 	/* Append msg to request queue and poke Venus. */
574 	TAILQ_INSERT_TAIL(&vcp->vc_requests, vmp, vm_chain);
575 	selnotify(&(vcp->vc_selproc), 0, 0);
576 
577 	/* We can be interrupted while we wait for Venus to process
578 	 * our request.  If the interrupt occurs before Venus has read
579 	 * the request, we dequeue and return. If it occurs after the
580 	 * read but before the reply, we dequeue, send a signal
581 	 * message, and return. If it occurs after the reply we ignore
582 	 * it. In no case do we want to restart the syscall.  If it
583 	 * was interrupted by a venus shutdown (vcclose), return
584 	 * ENODEV.  */
585 
586 	/* Ignore return, We have to check anyway */
587 #ifdef	CTL_C
588 	/* This is work in progress.  Setting coda_pcatch lets tsleep reawaken
589 	   on a ^c or ^z.  The problem is that emacs sets certain interrupts
590 	   as SA_RESTART.  This means that we should exit sleep handle the
591 	   "signal" and then go to sleep again.  Mostly this is done by letting
592 	   the syscall complete and be restarted.  We are not idempotent and
593 	   can not do this.  A better solution is necessary.
594 	 */
595 	i = 0;
596 	do {
597 	    error = tsleep(&vmp->vm_sleep, (coda_call_sleep|coda_pcatch), "coda_call", hz*2);
598 	    if (error == 0)
599 	    	break;
600 	    mutex_enter(p->p_lock);
601 	    if (error == EWOULDBLOCK) {
602 #ifdef	CODA_VERBOSE
603 		    printf("coda_call: tsleep TIMEOUT %d sec\n", 2+2*i);
604 #endif
605     	    } else if (sigispending(l, SIGIO)) {
606 		    sigaddset(&l->l_sigmask, SIGIO);
607 #ifdef	CODA_VERBOSE
608 		    printf("coda_call: tsleep returns %d SIGIO, cnt %d\n", error, i);
609 #endif
610     	    } else if (sigispending(l, SIGALRM)) {
611 		    sigaddset(&l->l_sigmask, SIGALRM);
612 #ifdef	CODA_VERBOSE
613 		    printf("coda_call: tsleep returns %d SIGALRM, cnt %d\n", error, i);
614 #endif
615 	    } else {
616 		    sigset_t tmp;
617 		    tmp = p->p_sigpend.sp_set;	/* array assignment */
618 		    sigminusset(&l->l_sigmask, &tmp);
619 
620 #ifdef	CODA_VERBOSE
621 		    printf("coda_call: tsleep returns %d, cnt %d\n", error, i);
622 		    printf("coda_call: siglist = %x.%x.%x.%x, sigmask = %x.%x.%x.%x, mask %x.%x.%x.%x\n",
623 			    p->p_sigpend.sp_set.__bits[0], p->p_sigpend.sp_set.__bits[1],
624 			    p->p_sigpend.sp_set.__bits[2], p->p_sigpend.sp_set.__bits[3],
625 			    l->l_sigmask.__bits[0], l->l_sigmask.__bits[1],
626 			    l->l_sigmask.__bits[2], l->l_sigmask.__bits[3],
627 			    tmp.__bits[0], tmp.__bits[1], tmp.__bits[2], tmp.__bits[3]);
628 #endif
629 		    mutex_exit(p->p_lock);
630 		    break;
631 #ifdef	notyet
632 		    sigminusset(&l->l_sigmask, &p->p_sigpend.sp_set);
633 		    printf("coda_call: siglist = %x.%x.%x.%x, sigmask = %x.%x.%x.%x\n",
634 			    p->p_sigpend.sp_set.__bits[0], p->p_sigpend.sp_set.__bits[1],
635 			    p->p_sigpend.sp_set.__bits[2], p->p_sigpend.sp_set.__bits[3],
636 			    l->l_sigmask.__bits[0], l->l_sigmask.__bits[1],
637 			    l->l_sigmask.__bits[2], l->l_sigmask.__bits[3]);
638 #endif
639 	    }
640 	    mutex_exit(p->p_lock);
641 	} while (error && i++ < 128 && VC_OPEN(vcp));
642 	l->l_sigmask = psig_omask;	/* XXXSA */
643 #else
644 	(void) tsleep(&vmp->vm_sleep, coda_call_sleep, "coda_call", 0);
645 #endif
646 	if (VC_OPEN(vcp)) {	/* Venus is still alive */
647  	/* Op went through, interrupt or not... */
648 	    if (vmp->vm_flags & VM_WRITE) {
649 		error = 0;
650 		*outSize = vmp->vm_outSize;
651 	    }
652 
653 	    else if (!(vmp->vm_flags & VM_READ)) {
654 		/* Interrupted before venus read it. */
655 #ifdef	CODA_VERBOSE
656 		if (1)
657 #else
658 		if (codadebug)
659 #endif
660 		    myprintf(("interrupted before read: op = %d.%d, flags = %x\n",
661 			   vmp->vm_opcode, vmp->vm_unique, vmp->vm_flags));
662 
663 		TAILQ_REMOVE(&vcp->vc_requests, vmp, vm_chain);
664 		error = EINTR;
665 	    }
666 
667 	    else {
668 		/* (!(vmp->vm_flags & VM_WRITE)) means interrupted after
669                    upcall started */
670 		/* Interrupted after start of upcall, send venus a signal */
671 		struct coda_in_hdr *dog;
672 		struct vmsg *svmp;
673 
674 #ifdef	CODA_VERBOSE
675 		if (1)
676 #else
677 		if (codadebug)
678 #endif
679 		    myprintf(("Sending Venus a signal: op = %d.%d, flags = %x\n",
680 			   vmp->vm_opcode, vmp->vm_unique, vmp->vm_flags));
681 
682 		TAILQ_REMOVE(&vcp->vc_replies, vmp, vm_chain);
683 		error = EINTR;
684 
685 		CODA_ALLOC(svmp, struct vmsg *, sizeof (struct vmsg));
686 
687 		CODA_ALLOC((svmp->vm_data), char *, sizeof (struct coda_in_hdr));
688 		dog = (struct coda_in_hdr *)svmp->vm_data;
689 
690 		svmp->vm_flags = 0;
691 		dog->opcode = svmp->vm_opcode = CODA_SIGNAL;
692 		dog->unique = svmp->vm_unique = vmp->vm_unique;
693 		svmp->vm_inSize = sizeof (struct coda_in_hdr);
694 /*??? rvb */	svmp->vm_outSize = sizeof (struct coda_in_hdr);
695 
696 		if (codadebug)
697 		    myprintf(("coda_call: enqueing signal msg (%d, %d)\n",
698 			   svmp->vm_opcode, svmp->vm_unique));
699 
700 		/* insert at head of queue */
701 		TAILQ_INSERT_HEAD(&vcp->vc_requests, svmp, vm_chain);
702 		selnotify(&(vcp->vc_selproc), 0, 0);
703 	    }
704 	}
705 
706 	else {	/* If venus died (!VC_OPEN(vcp)) */
707 	    if (codadebug)
708 		myprintf(("vcclose woke op %d.%d flags %d\n",
709 		       vmp->vm_opcode, vmp->vm_unique, vmp->vm_flags));
710 
711 		error = ENODEV;
712 	}
713 
714 	CODA_FREE(vmp, sizeof(struct vmsg));
715 
716 	if (outstanding_upcalls > 0 && (--outstanding_upcalls == 0))
717 		wakeup(&outstanding_upcalls);
718 
719 	if (!error)
720 		error = ((struct coda_out_hdr *)buffer)->result;
721 	return(error);
722 }
723 
724