xref: /netbsd-src/sys/coda/coda_psdev.c (revision 481fca6e59249d8ffcf24fef7cfbe7b131bfb080)
1 /*	$NetBSD: coda_psdev.c,v 1.13 2000/03/30 11:24:16 augustss 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 psuedo 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 
52 /* These routines are the device entry points for Venus. */
53 
54 extern int coda_nc_initialized;    /* Set if cache has been initialized */
55 
56 #ifdef	_LKM
57 #define	NVCODA 4
58 #else
59 #include <vcoda.h>
60 #endif
61 
62 #include <sys/param.h>
63 #include <sys/systm.h>
64 #include <sys/kernel.h>
65 #include <sys/malloc.h>
66 #include <sys/proc.h>
67 #include <sys/mount.h>
68 #include <sys/file.h>
69 #include <sys/ioctl.h>
70 #include <sys/poll.h>
71 #include <sys/select.h>
72 
73 #include <coda/coda.h>
74 #include <coda/cnode.h>
75 #include <coda/coda_namecache.h>
76 #include <coda/coda_io.h>
77 #include <coda/coda_psdev.h>
78 
79 #define CTL_C
80 
81 int coda_psdev_print_entry = 0;
82 static
83 int outstanding_upcalls = 0;
84 int coda_call_sleep = PZERO - 1;
85 #ifdef	CTL_C
86 int coda_pcatch = PCATCH;
87 #else
88 #endif
89 
90 #define ENTRY if(coda_psdev_print_entry) myprintf(("Entered %s\n",__FUNCTION__))
91 
92 void vcodaattach(int n);
93 
94 struct vmsg {
95     struct queue vm_chain;
96     caddr_t	 vm_data;
97     u_short	 vm_flags;
98     u_short      vm_inSize;	/* Size is at most 5000 bytes */
99     u_short	 vm_outSize;
100     u_short	 vm_opcode; 	/* copied from data to save ptr lookup */
101     int		 vm_unique;
102     caddr_t	 vm_sleep;	/* Not used by Mach. */
103 };
104 
105 #define	VM_READ	    1
106 #define	VM_WRITE    2
107 #define	VM_INTR	    4
108 
109 /* vcodaattach: do nothing */
110 void
111 vcodaattach(n)
112     int n;
113 {
114 }
115 
116 /*
117  * These functions are written for NetBSD.
118  */
119 int
120 vc_nb_open(dev, flag, mode, p)
121     dev_t        dev;
122     int          flag;
123     int          mode;
124     struct proc *p;             /* NetBSD only */
125 {
126     struct vcomm *vcp;
127 
128     ENTRY;
129 
130     if (minor(dev) >= NVCODA || minor(dev) < 0)
131 	return(ENXIO);
132 
133     if (!coda_nc_initialized)
134 	coda_nc_init();
135 
136     vcp = &coda_mnttbl[minor(dev)].mi_vcomm;
137     if (VC_OPEN(vcp))
138 	return(EBUSY);
139 
140     bzero(&(vcp->vc_selproc), sizeof (struct selinfo));
141     INIT_QUEUE(vcp->vc_requests);
142     INIT_QUEUE(vcp->vc_replys);
143     MARK_VC_OPEN(vcp);
144 
145     coda_mnttbl[minor(dev)].mi_vfsp = NULL;
146     coda_mnttbl[minor(dev)].mi_rootvp = NULL;
147 
148     return(0);
149 }
150 
151 int
152 vc_nb_close (dev, flag, mode, p)
153     dev_t        dev;
154     int          flag;
155     int          mode;
156     struct proc *p;
157 {
158     struct vcomm *vcp;
159     struct vmsg *vmp, *nvmp = NULL;
160     struct coda_mntinfo *mi;
161     int                 err;
162 
163     ENTRY;
164 
165     if (minor(dev) >= NVCODA || minor(dev) < 0)
166 	return(ENXIO);
167 
168     mi = &coda_mnttbl[minor(dev)];
169     vcp = &(mi->mi_vcomm);
170 
171     if (!VC_OPEN(vcp))
172 	panic("vcclose: not open");
173 
174     /* prevent future operations on this vfs from succeeding by auto-
175      * unmounting any vfs mounted via this device. This frees user or
176      * sysadm from having to remember where all mount points are located.
177      * Put this before WAKEUPs to avoid queuing new messages between
178      * the WAKEUP and the unmount (which can happen if we're unlucky)
179      */
180     if (!mi->mi_rootvp) {
181 	/* just a simple open/close w no mount */
182 	MARK_VC_CLOSED(vcp);
183 	return 0;
184     }
185 
186     /* Let unmount know this is for real */
187     VTOC(mi->mi_rootvp)->c_flags |= C_UNMOUNTING;
188     if (vfs_busy(mi->mi_vfsp, 0, 0))
189 	return (EBUSY);
190     coda_unmounting(mi->mi_vfsp);
191 
192     /* Wakeup clients so they can return. */
193     for (vmp = (struct vmsg *)GETNEXT(vcp->vc_requests);
194 	 !EOQ(vmp, vcp->vc_requests);
195 	 vmp = nvmp)
196     {
197     	nvmp = (struct vmsg *)GETNEXT(vmp->vm_chain);
198 	/* Free signal request messages and don't wakeup cause
199 	   no one is waiting. */
200 	if (vmp->vm_opcode == CODA_SIGNAL) {
201 	    CODA_FREE((caddr_t)vmp->vm_data, (u_int)VC_IN_NO_DATA);
202 	    CODA_FREE((caddr_t)vmp, (u_int)sizeof(struct vmsg));
203 	    continue;
204 	}
205 	outstanding_upcalls++;
206 	wakeup(&vmp->vm_sleep);
207     }
208 
209     for (vmp = (struct vmsg *)GETNEXT(vcp->vc_replys);
210 	 !EOQ(vmp, vcp->vc_replys);
211 	 vmp = (struct vmsg *)GETNEXT(vmp->vm_chain))
212     {
213 	outstanding_upcalls++;
214 	wakeup(&vmp->vm_sleep);
215     }
216 
217     MARK_VC_CLOSED(vcp);
218 
219     if (outstanding_upcalls) {
220 #ifdef	CODA_VERBOSE
221 	printf("presleep: outstanding_upcalls = %d\n", outstanding_upcalls);
222     	(void) tsleep(&outstanding_upcalls, coda_call_sleep, "coda_umount", 0);
223 	printf("postsleep: outstanding_upcalls = %d\n", outstanding_upcalls);
224 #else
225     	(void) tsleep(&outstanding_upcalls, coda_call_sleep, "coda_umount", 0);
226 #endif
227     }
228 
229     err = dounmount(mi->mi_vfsp, flag, p);
230     if (err)
231 	myprintf(("Error %d unmounting vfs in vcclose(%d)\n",
232 	           err, minor(dev)));
233     return 0;
234 }
235 
236 int
237 vc_nb_read(dev, uiop, flag)
238     dev_t        dev;
239     struct uio  *uiop;
240     int          flag;
241 {
242     struct vcomm *	vcp;
243     struct vmsg *vmp;
244     int error = 0;
245 
246     ENTRY;
247 
248     if (minor(dev) >= NVCODA || minor(dev) < 0)
249 	return(ENXIO);
250 
251     vcp = &coda_mnttbl[minor(dev)].mi_vcomm;
252     /* Get message at head of request queue. */
253     if (EMPTY(vcp->vc_requests))
254 	return(0);	/* Nothing to read */
255 
256     vmp = (struct vmsg *)GETNEXT(vcp->vc_requests);
257 
258     /* Move the input args into userspace */
259     uiop->uio_rw = UIO_READ;
260     error = uiomove(vmp->vm_data, vmp->vm_inSize, uiop);
261     if (error) {
262 	myprintf(("vcread: error (%d) on uiomove\n", error));
263 	error = EINVAL;
264     }
265 
266 #ifdef OLD_DIAGNOSTIC
267     if (vmp->vm_chain.forw == 0 || vmp->vm_chain.back == 0)
268 	panic("vc_nb_read: bad chain");
269 #endif
270 
271     REMQUE(vmp->vm_chain);
272 
273     /* If request was a signal, free up the message and don't
274        enqueue it in the reply queue. */
275     if (vmp->vm_opcode == CODA_SIGNAL) {
276 	if (codadebug)
277 	    myprintf(("vcread: signal msg (%d, %d)\n",
278 		      vmp->vm_opcode, vmp->vm_unique));
279 	CODA_FREE((caddr_t)vmp->vm_data, (u_int)VC_IN_NO_DATA);
280 	CODA_FREE((caddr_t)vmp, (u_int)sizeof(struct vmsg));
281 	return(error);
282     }
283 
284     vmp->vm_flags |= VM_READ;
285     INSQUE(vmp->vm_chain, vcp->vc_replys);
286 
287     return(error);
288 }
289 
290 int
291 vc_nb_write(dev, uiop, flag)
292     dev_t        dev;
293     struct uio  *uiop;
294     int          flag;
295 {
296     struct vcomm *	vcp;
297     struct vmsg *vmp;
298     struct coda_out_hdr *out;
299     u_long seq;
300     u_long opcode;
301     int buf[2];
302     int error = 0;
303 
304     ENTRY;
305 
306     if (minor(dev) >= NVCODA || minor(dev) < 0)
307 	return(ENXIO);
308 
309     vcp = &coda_mnttbl[minor(dev)].mi_vcomm;
310 
311     /* Peek at the opcode, unique without transfering the data. */
312     uiop->uio_rw = UIO_WRITE;
313     error = uiomove((caddr_t)buf, sizeof(int) * 2, uiop);
314     if (error) {
315 	myprintf(("vcwrite: error (%d) on uiomove\n", error));
316 	return(EINVAL);
317     }
318 
319     opcode = buf[0];
320     seq = buf[1];
321 
322     if (codadebug)
323 	myprintf(("vcwrite got a call for %ld.%ld\n", opcode, seq));
324 
325     if (DOWNCALL(opcode)) {
326 	union outputArgs pbuf;
327 
328 	/* get the rest of the data. */
329 	uiop->uio_rw = UIO_WRITE;
330 	error = uiomove((caddr_t)&pbuf.coda_purgeuser.oh.result, sizeof(pbuf) - (sizeof(int)*2), uiop);
331 	if (error) {
332 	    myprintf(("vcwrite: error (%d) on uiomove (Op %ld seq %ld)\n",
333 		      error, opcode, seq));
334 	    return(EINVAL);
335 	    }
336 
337 	return handleDownCall(opcode, &pbuf);
338     }
339 
340     /* Look for the message on the (waiting for) reply queue. */
341     for (vmp = (struct vmsg *)GETNEXT(vcp->vc_replys);
342 	 !EOQ(vmp, vcp->vc_replys);
343 	 vmp = (struct vmsg *)GETNEXT(vmp->vm_chain))
344     {
345 	if (vmp->vm_unique == seq) break;
346     }
347 
348     if (EOQ(vmp, vcp->vc_replys)) {
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     REMQUE(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     buf[0] = uiop->uio_resid; 	/* Save this value. */
371     uiop->uio_rw = UIO_WRITE;
372     error = uiomove((caddr_t) &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	= buf[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, cmd, addr, flag, p)
392     dev_t         dev;
393     u_long        cmd;
394     caddr_t       addr;
395     int           flag;
396     struct proc  *p;
397 {
398     ENTRY;
399 
400     switch(cmd) {
401     case CODARESIZE: {
402 	struct coda_resize *data = (struct coda_resize *)addr;
403 	return(coda_nc_resize(data->hashsize, data->heapsize, IS_DOWNCALL));
404 	break;
405     }
406     case CODASTATS:
407 	if (coda_nc_use) {
408 	    coda_nc_gather_stats();
409 	    return(0);
410 	} else {
411 	    return(ENODEV);
412 	}
413 	break;
414     case CODAPRINT:
415 	if (coda_nc_use) {
416 	    print_coda_nc();
417 	    return(0);
418 	} else {
419 	    return(ENODEV);
420 	}
421 	break;
422     case CIOC_KERNEL_VERSION:
423 	switch (*(u_int *)addr) {
424 	case 0:
425 		*(u_int *)addr = coda_kernel_version;
426 		return 0;
427 		break;
428 	case 1:
429 	case 2:
430 		if (coda_kernel_version != *(u_int *)addr)
431 		    return ENOENT;
432 		else
433 		    return 0;
434 	default:
435 		return ENOENT;
436 	}
437     	break;
438     default :
439 	return(EINVAL);
440 	break;
441     }
442 }
443 
444 int
445 vc_nb_poll(dev, events, p)
446     dev_t         dev;
447     int           events;
448     struct proc  *p;
449 {
450     struct vcomm *vcp;
451     int event_msk = 0;
452 
453     ENTRY;
454 
455     if (minor(dev) >= NVCODA || minor(dev) < 0)
456 	return(ENXIO);
457 
458     vcp = &coda_mnttbl[minor(dev)].mi_vcomm;
459 
460     event_msk = events & (POLLIN|POLLRDNORM);
461     if (!event_msk)
462 	return(0);
463 
464     if (!EMPTY(vcp->vc_requests))
465 	return(events & (POLLIN|POLLRDNORM));
466 
467     selrecord(p, &(vcp->vc_selproc));
468 
469     return(0);
470 }
471 
472 /*
473  * Statistics
474  */
475 struct coda_clstat coda_clstat;
476 
477 /*
478  * Key question: whether to sleep interuptably or uninteruptably when
479  * waiting for Venus.  The former seems better (cause you can ^C a
480  * job), but then GNU-EMACS completion breaks. Use tsleep with no
481  * timeout, and no longjmp happens. But, when sleeping
482  * "uninterruptibly", we don't get told if it returns abnormally
483  * (e.g. kill -9).
484  */
485 
486 int
487 coda_call(mntinfo, inSize, outSize, buffer)
488      struct coda_mntinfo *mntinfo; int inSize; int *outSize; caddr_t buffer;
489 {
490 	struct vcomm *vcp;
491 	struct vmsg *vmp;
492 	int error;
493 #ifdef	CTL_C
494 	struct proc *p = curproc;
495 	sigset_t psig_omask;
496 	int i;
497 	psig_omask = p->p_siglist;	/* array assignment */
498 #endif
499 	if (mntinfo == NULL) {
500 	    /* Unlikely, but could be a race condition with a dying warden */
501 	    return ENODEV;
502 	}
503 
504 	vcp = &(mntinfo->mi_vcomm);
505 
506 	coda_clstat.ncalls++;
507 	coda_clstat.reqs[((struct coda_in_hdr *)buffer)->opcode]++;
508 
509 	if (!VC_OPEN(vcp))
510 	    return(ENODEV);
511 
512 	CODA_ALLOC(vmp,struct vmsg *,sizeof(struct vmsg));
513 	/* Format the request message. */
514 	vmp->vm_data = buffer;
515 	vmp->vm_flags = 0;
516 	vmp->vm_inSize = inSize;
517 	vmp->vm_outSize
518 	    = *outSize ? *outSize : inSize; /* |buffer| >= inSize */
519 	vmp->vm_opcode = ((struct coda_in_hdr *)buffer)->opcode;
520 	vmp->vm_unique = ++vcp->vc_seq;
521 	if (codadebug)
522 	    myprintf(("Doing a call for %d.%d\n",
523 		      vmp->vm_opcode, vmp->vm_unique));
524 
525 	/* Fill in the common input args. */
526 	((struct coda_in_hdr *)buffer)->unique = vmp->vm_unique;
527 
528 	/* Append msg to request queue and poke Venus. */
529 	INSQUE(vmp->vm_chain, vcp->vc_requests);
530 	selwakeup(&(vcp->vc_selproc));
531 
532 	/* We can be interrupted while we wait for Venus to process
533 	 * our request.  If the interrupt occurs before Venus has read
534 	 * the request, we dequeue and return. If it occurs after the
535 	 * read but before the reply, we dequeue, send a signal
536 	 * message, and return. If it occurs after the reply we ignore
537 	 * it. In no case do we want to restart the syscall.  If it
538 	 * was interrupted by a venus shutdown (vcclose), return
539 	 * ENODEV.  */
540 
541 	/* Ignore return, We have to check anyway */
542 #ifdef	CTL_C
543 	/* This is work in progress.  Setting coda_pcatch lets tsleep reawaken
544 	   on a ^c or ^z.  The problem is that emacs sets certain interrupts
545 	   as SA_RESTART.  This means that we should exit sleep handle the
546 	   "signal" and then go to sleep again.  Mostly this is done by letting
547 	   the syscall complete and be restarted.  We are not idempotent and
548 	   can not do this.  A better solution is necessary.
549 	 */
550 	i = 0;
551 	do {
552 	    error = tsleep(&vmp->vm_sleep, (coda_call_sleep|coda_pcatch), "coda_call", hz*2);
553 	    if (error == 0)
554 	    	break;
555 	    else if (error == EWOULDBLOCK) {
556 #ifdef	CODA_VERBOSE
557 		    printf("coda_call: tsleep TIMEOUT %d sec\n", 2+2*i);
558 #endif
559     	    } else if (sigismember(&p->p_siglist, SIGIO)) {
560 		    sigaddset(&p->p_sigmask, SIGIO);
561 #ifdef	CODA_VERBOSE
562 		    printf("coda_call: tsleep returns %d SIGIO, cnt %d\n", error, i);
563 #endif
564     	    } else if (sigismember(&p->p_siglist, SIGALRM)) {
565 		    sigaddset(&p->p_sigmask, SIGALRM);
566 #ifdef	CODA_VERBOSE
567 		    printf("coda_call: tsleep returns %d SIGALRM, cnt %d\n", error, i);
568 #endif
569 	    } else {
570 		    sigset_t tmp;
571 		    tmp = p->p_siglist;		/* array assignment */
572 		    sigminusset(&p->p_sigmask, &tmp);
573 
574 #ifdef	CODA_VERBOSE
575 		    printf("coda_call: tsleep returns %d, cnt %d\n", error, i);
576 		    printf("coda_call: siglist = %x.%x.%x.%x, sigmask = %x.%x.%x.%x, mask %x.%x.%x.%x\n",
577 			    p->p_siglist.__bits[0], p->p_siglist.__bits[1],
578 			    p->p_siglist.__bits[2], p->p_siglist.__bits[3],
579 			    p->p_sigmask.__bits[0], p->p_sigmask.__bits[1],
580 			    p->p_sigmask.__bits[2], p->p_sigmask.__bits[3],
581 			    tmp.__bits[0], tmp.__bits[1], tmp.__bits[2], tmp.__bits[3]);
582 #endif
583 		    break;
584 #ifdef	notyet
585 		    sigminusset(&p->p_sigmask, &p->p_siglist);
586 		    printf("coda_call: siglist = %x.%x.%x.%x, sigmask = %x.%x.%x.%x\n",
587 			    p->p_siglist.__bits[0], p->p_siglist.__bits[1],
588 			    p->p_siglist.__bits[2], p->p_siglist.__bits[3],
589 			    p->p_sigmask.__bits[0], p->p_sigmask.__bits[1],
590 			    p->p_sigmask.__bits[2], p->p_sigmask.__bits[3]);
591 #endif
592 	    }
593 	} while (error && i++ < 128 && VC_OPEN(vcp));
594 	p->p_siglist = psig_omask;	/* array assignment */
595 #else
596 	(void) tsleep(&vmp->vm_sleep, coda_call_sleep, "coda_call", 0);
597 #endif
598 	if (VC_OPEN(vcp)) {	/* Venus is still alive */
599  	/* Op went through, interrupt or not... */
600 	    if (vmp->vm_flags & VM_WRITE) {
601 		error = 0;
602 		*outSize = vmp->vm_outSize;
603 	    }
604 
605 	    else if (!(vmp->vm_flags & VM_READ)) {
606 		/* Interrupted before venus read it. */
607 #ifdef	CODA_VERBOSE
608 		if (1)
609 #else
610 		if (codadebug)
611 #endif
612 		    myprintf(("interrupted before read: op = %d.%d, flags = %x\n",
613 			   vmp->vm_opcode, vmp->vm_unique, vmp->vm_flags));
614 		REMQUE(vmp->vm_chain);
615 		error = EINTR;
616 	    }
617 
618 	    else {
619 		/* (!(vmp->vm_flags & VM_WRITE)) means interrupted after
620                    upcall started */
621 		/* Interrupted after start of upcall, send venus a signal */
622 		struct coda_in_hdr *dog;
623 		struct vmsg *svmp;
624 
625 #ifdef	CODA_VERBOSE
626 		if (1)
627 #else
628 		if (codadebug)
629 #endif
630 		    myprintf(("Sending Venus a signal: op = %d.%d, flags = %x\n",
631 			   vmp->vm_opcode, vmp->vm_unique, vmp->vm_flags));
632 
633 		REMQUE(vmp->vm_chain);
634 		error = EINTR;
635 
636 		CODA_ALLOC(svmp, struct vmsg *, sizeof (struct vmsg));
637 
638 		CODA_ALLOC((svmp->vm_data), char *, sizeof (struct coda_in_hdr));
639 		dog = (struct coda_in_hdr *)svmp->vm_data;
640 
641 		svmp->vm_flags = 0;
642 		dog->opcode = svmp->vm_opcode = CODA_SIGNAL;
643 		dog->unique = svmp->vm_unique = vmp->vm_unique;
644 		svmp->vm_inSize = sizeof (struct coda_in_hdr);
645 /*??? rvb */	svmp->vm_outSize = sizeof (struct coda_in_hdr);
646 
647 		if (codadebug)
648 		    myprintf(("coda_call: enqueing signal msg (%d, %d)\n",
649 			   svmp->vm_opcode, svmp->vm_unique));
650 
651 		/* insert at head of queue! */
652 		INSQUE(svmp->vm_chain, vcp->vc_requests);
653 		selwakeup(&(vcp->vc_selproc));
654 	    }
655 	}
656 
657 	else {	/* If venus died (!VC_OPEN(vcp)) */
658 	    if (codadebug)
659 		myprintf(("vcclose woke op %d.%d flags %d\n",
660 		       vmp->vm_opcode, vmp->vm_unique, vmp->vm_flags));
661 
662 		error = ENODEV;
663 	}
664 
665 	CODA_FREE(vmp, sizeof(struct vmsg));
666 
667 	if (outstanding_upcalls > 0 && (--outstanding_upcalls == 0))
668 		wakeup(&outstanding_upcalls);
669 
670 	if (!error)
671 		error = ((struct coda_out_hdr *)buffer)->result;
672 	return(error);
673 }
674 
675