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