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