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