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