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