]> CyberLeo.Net >> Repos - FreeBSD/releng/8.1.git/blob - sys/fs/coda/coda_psdev.c
Copy stable/8 to releng/8.1 in preparation for 8.1-RC1.
[FreeBSD/releng/8.1.git] / sys / fs / coda / coda_psdev.c
1 /*-
2  *             Coda: an Experimental Distributed File System
3  *                              Release 3.1
4  *
5  *           Copyright (c) 1987-1998 Carnegie Mellon University
6  *                          All Rights Reserved
7  *
8  * Permission  to  use, copy, modify and distribute this software and its
9  * documentation is hereby granted,  provided  that  both  the  copyright
10  * notice  and  this  permission  notice  appear  in  all  copies  of the
11  * software, derivative works or  modified  versions,  and  any  portions
12  * thereof, and that both notices appear in supporting documentation, and
13  * that credit is given to Carnegie Mellon University  in  all  documents
14  * and publicity pertaining to direct or indirect use of this code or its
15  * derivatives.
16  *
17  * CODA IS AN EXPERIMENTAL SOFTWARE SYSTEM AND IS  KNOWN  TO  HAVE  BUGS,
18  * SOME  OF  WHICH MAY HAVE SERIOUS CONSEQUENCES.  CARNEGIE MELLON ALLOWS
19  * FREE USE OF THIS SOFTWARE IN ITS "AS IS" CONDITION.   CARNEGIE  MELLON
20  * DISCLAIMS  ANY  LIABILITY  OF  ANY  KIND  FOR  ANY  DAMAGES WHATSOEVER
21  * RESULTING DIRECTLY OR INDIRECTLY FROM THE USE OF THIS SOFTWARE  OR  OF
22  * ANY DERIVATIVE WORK.
23  *
24  * Carnegie  Mellon  encourages  users  of  this  software  to return any
25  * improvements or extensions that  they  make,  and  to  grant  Carnegie
26  * Mellon the rights to redistribute these changes without encumbrance.
27  *
28  *      @(#) src/sys/coda/coda_psdev.c,v 1.1.1.1 1998/08/29 21:14:52 rvb Exp $
29  */
30 /*-
31  * Mach Operating System
32  * Copyright (c) 1989 Carnegie-Mellon University
33  * All rights reserved.  The CMU software License Agreement specifies
34  * the terms and conditions for use and redistribution.
35  */
36
37 /*
38  * This code was written for the Coda filesystem at Carnegie Mellon
39  * University.  Contributers include David Steere, James Kistler, and
40  * M. Satyanarayanan.  */
41
42 /*
43  * These routines define the psuedo device for communication between Coda's
44  * Venus and Minicache in Mach 2.6. They used to be in cfs_subr.c, but I
45  * moved them to make it easier to port the Minicache without porting coda.
46  * -- DCS 10/12/94
47  */
48
49 /*
50  * These routines are the device entry points for Venus.
51  */
52
53 #include <sys/cdefs.h>
54 __FBSDID("$FreeBSD$");
55
56 #include <sys/param.h>
57 #include <sys/systm.h>
58 #include <sys/conf.h>
59 #include <sys/ioccom.h>
60 #include <sys/kernel.h>
61 #include <sys/lock.h>
62 #include <sys/malloc.h>
63 #include <sys/file.h>           /* must come after sys/malloc.h */
64 #include <sys/mount.h>
65 #include <sys/mutex.h>
66 #include <sys/poll.h>
67 #include <sys/proc.h>
68 #include <sys/filedesc.h>
69
70 #include <fs/coda/coda.h>
71 #include <fs/coda/cnode.h>
72 #include <fs/coda/coda_io.h>
73 #include <fs/coda/coda_psdev.h>
74
75 /*
76  * Variables to determine how Coda sleeps and whether or not it is
77  * interruptible when it does sleep waiting for Venus.
78  */
79 /* #define      CTL_C */
80
81 #ifdef CTL_C
82 #include <sys/signalvar.h>
83 #endif
84
85 int coda_psdev_print_entry = 0;
86 static int outstanding_upcalls = 0;
87 int coda_call_sleep = PZERO - 1;
88 #ifdef CTL_C
89 int coda_pcatch = PCATCH;
90 #else
91 #endif
92
93 #define ENTRY do {                                                      \
94         if (coda_psdev_print_entry)                                     \
95                 myprintf(("Entered %s\n", __func__));                   \
96 } while (0)
97
98 struct vmsg {
99         TAILQ_ENTRY(vmsg)       vm_chain;
100         caddr_t         vm_data;
101         u_short         vm_flags;
102         u_short         vm_inSize;      /* Size is at most 5000 bytes */
103         u_short         vm_outSize;
104         u_short         vm_opcode;      /* Copied from data to save ptr deref */
105         int             vm_unique;
106         caddr_t         vm_sleep;       /* Not used by Mach. */
107 };
108
109 #define VM_READ         1
110 #define VM_WRITE        2
111 #define VM_INTR         4               /* Unused. */
112
113 int
114 vc_open(struct cdev *dev, int flag, int mode, struct thread *td)
115 {
116         struct vcomm *vcp;
117         struct coda_mntinfo *mnt;
118
119         ENTRY;
120         mnt = dev2coda_mntinfo(dev);
121         KASSERT(mnt, ("Coda: tried to open uninitialized cfs device"));
122         vcp = &mnt->mi_vcomm;
123         if (VC_OPEN(vcp))
124                 return (EBUSY);
125         bzero(&(vcp->vc_selproc), sizeof (struct selinfo));
126         TAILQ_INIT(&vcp->vc_requests);
127         TAILQ_INIT(&vcp->vc_replies);
128         MARK_VC_OPEN(vcp);
129         mnt->mi_vfsp = NULL;
130         mnt->mi_rootvp = NULL;
131         return (0);
132 }
133
134 int
135 vc_close(struct cdev *dev, int flag, int mode, struct thread *td)
136 {
137         struct vcomm *vcp;
138         struct vmsg *vmp, *nvmp = NULL;
139         struct coda_mntinfo *mi;
140         int err;
141
142         ENTRY;
143         mi = dev2coda_mntinfo(dev);
144         KASSERT(mi, ("Coda: closing unknown cfs device"));
145         vcp = &mi->mi_vcomm;
146         KASSERT(VC_OPEN(vcp), ("Coda: closing unopened cfs device"));
147
148         /*
149          * Prevent future operations on this vfs from succeeding by
150          * auto-unmounting any vfs mounted via this device.  This frees user
151          * or sysadm from having to remember where all mount points are
152          * located.  Put this before WAKEUPs to avoid queuing new messages
153          * between the WAKEUP and the unmount (which can happen if we're
154          * unlucky).
155          */
156         if (mi->mi_rootvp == NULL) {
157                 /*
158                  * Just a simple open/close with no mount.
159                  */
160                 MARK_VC_CLOSED(vcp);
161                 return (0);
162         }
163
164         /*
165          * Let unmount know this is for real.
166          */
167         VTOC(mi->mi_rootvp)->c_flags |= C_UNMOUNTING;
168         coda_unmounting(mi->mi_vfsp);
169
170         /*
171          * Wakeup clients so they can return.
172          */
173         outstanding_upcalls = 0;
174         TAILQ_FOREACH_SAFE(vmp, &vcp->vc_requests, vm_chain, nvmp) {
175                 /*
176                  * Free signal request messages and don't wakeup cause no one
177                  * is waiting.
178                  */
179                 if (vmp->vm_opcode == CODA_SIGNAL) {
180                         CODA_FREE((caddr_t)vmp->vm_data,
181                             (u_int)VC_IN_NO_DATA);
182                         CODA_FREE((caddr_t)vmp, (u_int)sizeof(struct vmsg));
183                         continue;
184                 }
185                 outstanding_upcalls++;
186                 wakeup(&vmp->vm_sleep);
187         }
188         TAILQ_FOREACH(vmp, &vcp->vc_replies, vm_chain) {
189                 outstanding_upcalls++;
190                 wakeup(&vmp->vm_sleep);
191         }
192         MARK_VC_CLOSED(vcp);
193         if (outstanding_upcalls) {
194 #ifdef CODA_VERBOSE
195                 printf("presleep: outstanding_upcalls = %d\n",
196                     outstanding_upcalls);
197 #endif
198                 (void) tsleep(&outstanding_upcalls, coda_call_sleep,
199                     "coda_umount", 0);
200 #ifdef CODA_VERBOSE
201                 printf("postsleep: outstanding_upcalls = %d\n",
202                     outstanding_upcalls);
203 #endif
204         }
205         err = dounmount(mi->mi_vfsp, flag, td);
206         if (err)
207                 myprintf(("Error %d unmounting vfs in vcclose(%s)\n", err,
208                     devtoname(dev)));
209         return (0);
210 }
211
212 int
213 vc_read(struct cdev *dev, struct uio *uiop, int flag)
214 {
215         struct vcomm *vcp;
216         struct vmsg *vmp;
217         int error = 0;
218
219         ENTRY;
220         vcp = &dev2coda_mntinfo(dev)->mi_vcomm;
221
222         /*
223          * Get message at head of request queue.
224          */
225         vmp = TAILQ_FIRST(&vcp->vc_requests);
226         if (vmp == NULL)
227                 return (0);     /* Nothing to read */
228
229         /*
230          * Move the input args into userspace.
231          *
232          * XXXRW: This is not safe in the presence of >1 reader, as vmp is
233          * still on the head of the list.
234          */
235         uiop->uio_rw = UIO_READ;
236         error = uiomove(vmp->vm_data, vmp->vm_inSize, uiop);
237         if (error) {
238                 myprintf(("vcread: error (%d) on uiomove\n", error));
239                 error = EINVAL;
240         }
241         TAILQ_REMOVE(&vcp->vc_requests, vmp, vm_chain);
242
243         /*
244          * If request was a signal, free up the message and don't enqueue it
245          * in the reply queue.
246          */
247         if (vmp->vm_opcode == CODA_SIGNAL) {
248                 if (codadebug)
249                         myprintf(("vcread: signal msg (%d, %d)\n",
250                             vmp->vm_opcode, vmp->vm_unique));
251                 CODA_FREE((caddr_t)vmp->vm_data, (u_int)VC_IN_NO_DATA);
252                 CODA_FREE((caddr_t)vmp, (u_int)sizeof(struct vmsg));
253                 return (error);
254         }
255         vmp->vm_flags |= VM_READ;
256         TAILQ_INSERT_TAIL(&vcp->vc_replies, vmp, vm_chain);
257         return (error);
258 }
259
260 int
261 vc_write(struct cdev *dev, struct uio *uiop, int flag)
262 {
263         struct vcomm *vcp;
264         struct vmsg *vmp;
265         struct coda_out_hdr *out;
266         u_long seq;
267         u_long opcode;
268         int buf[2];
269         int error = 0;
270
271         ENTRY;
272         vcp = &dev2coda_mntinfo(dev)->mi_vcomm;
273
274         /*
275          * Peek at the opcode, unique without transfering the data.
276          */
277         uiop->uio_rw = UIO_WRITE;
278         error = uiomove((caddr_t)buf, sizeof(int) * 2, uiop);
279         if (error) {
280                 myprintf(("vcwrite: error (%d) on uiomove\n", error));
281                 return (EINVAL);
282         }
283         opcode = buf[0];
284         seq = buf[1];
285         if (codadebug)
286                 myprintf(("vcwrite got a call for %ld.%ld\n", opcode, seq));
287         if (DOWNCALL(opcode)) {
288                 union outputArgs pbuf;
289
290                 /*
291                  * Get the rest of the data.
292                  */
293                 uiop->uio_rw = UIO_WRITE;
294                 error = uiomove((caddr_t)&pbuf.coda_purgeuser.oh.result,
295                     sizeof(pbuf) - (sizeof(int)*2), uiop);
296                 if (error) {
297                         myprintf(("vcwrite: error (%d) on uiomove (Op %ld "
298                             "seq %ld)\n", error, opcode, seq));
299                         return (EINVAL);
300                 }
301                 return (handleDownCall(dev2coda_mntinfo(dev), opcode, &pbuf));
302         }
303
304         /*
305          * Look for the message on the (waiting for) reply queue.
306          */
307         TAILQ_FOREACH(vmp, &vcp->vc_replies, vm_chain) {
308                 if (vmp->vm_unique == seq)
309                         break;
310         }
311         if (vmp == NULL) {
312                 if (codadebug)
313                         myprintf(("vcwrite: msg (%ld, %ld) not found\n",
314                             opcode, seq));
315                 return (ESRCH);
316         }
317
318         /*
319          * Remove the message from the reply queue.
320          */
321         TAILQ_REMOVE(&vcp->vc_replies, vmp, vm_chain);
322
323         /*
324          * Move data into response buffer.
325          */
326         out = (struct coda_out_hdr *)vmp->vm_data;
327
328         /*
329          * Don't need to copy opcode and uniquifier.
330          *
331          * Get the rest of the data.
332          */
333         if (vmp->vm_outSize < uiop->uio_resid) {
334                 myprintf(("vcwrite: more data than asked for (%d < %zd)\n",
335                     vmp->vm_outSize, uiop->uio_resid));
336
337                 /*
338                  * Notify caller of the error.
339                  */
340                 wakeup(&vmp->vm_sleep);
341                 return (EINVAL);
342         }
343
344         /*
345          * Save the value.
346          */
347         buf[0] = uiop->uio_resid;
348         uiop->uio_rw = UIO_WRITE;
349         error = uiomove((caddr_t) &out->result, vmp->vm_outSize -
350             (sizeof(int) * 2), uiop);
351         if (error) {
352                 myprintf(("vcwrite: error (%d) on uiomove (op %ld seq %ld)\n",
353                     error, opcode, seq));
354                 return (EINVAL);
355         }
356
357         /*
358          * I don't think these are used, but just in case.
359          *
360          * XXX - aren't these two already correct? -bnoble
361          */
362         out->opcode = opcode;
363         out->unique = seq;
364         vmp->vm_outSize = buf[0];       /* Amount of data transferred? */
365         vmp->vm_flags |= VM_WRITE;
366         error = 0;
367         if (opcode == CODA_OPEN_BY_FD) {
368                 struct coda_open_by_fd_out *tmp =
369                     (struct coda_open_by_fd_out *)out;
370                 struct file *fp;
371                 struct vnode *vp = NULL;
372
373                 if (tmp->oh.result == 0) {
374                         error = getvnode(uiop->uio_td->td_proc->p_fd,
375                             tmp->fd, &fp);
376                         if (!error) {
377                                 /*
378                                  * XXX: Since the whole driver runs with
379                                  * Giant, don't actually need to acquire it
380                                  * explicitly here yet.
381                                  */
382                                 mtx_lock(&Giant);
383                                 vp = fp->f_vnode;
384                                 VREF(vp);
385                                 fdrop(fp, uiop->uio_td);
386                                 mtx_unlock(&Giant);
387                         }
388                 }
389                 tmp->vp = vp;
390         }
391         wakeup(&vmp->vm_sleep);
392         return (error);
393 }
394
395 int
396 vc_ioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flag,
397     struct thread *t)
398 {
399
400         ENTRY;
401         switch(cmd) {
402         case CODARESIZE:
403                 return (ENODEV);
404
405         case CODASTATS:
406                 return (ENODEV);
407
408         case CODAPRINT:
409                 return (ENODEV);
410
411         case CIOC_KERNEL_VERSION:
412                 switch (*(u_int *)addr) {
413                 case 0:
414                         *(u_int *)addr = coda_kernel_version;
415                         return (0);
416
417                 case 1:
418                 case 2:
419                         if (coda_kernel_version != *(u_int *)addr)
420                                 return (ENOENT);
421                         else
422                                 return (0);
423
424                 default:
425                         return (ENOENT);
426                 }
427
428         default:
429                 return (EINVAL);
430         }
431 }
432
433 int
434 vc_poll(struct cdev *dev, int events, struct thread *td)
435 {
436         struct vcomm *vcp;
437         int event_msk = 0;
438
439         ENTRY;
440         vcp = &dev2coda_mntinfo(dev)->mi_vcomm;
441         event_msk = events & (POLLIN|POLLRDNORM);
442         if (!event_msk)
443                 return (0);
444         if (!TAILQ_EMPTY(&vcp->vc_requests))
445                 return (events & (POLLIN|POLLRDNORM));
446         selrecord(td, &(vcp->vc_selproc));
447         return (0);
448 }
449
450 /*
451  * Statistics.
452  */
453 struct coda_clstat coda_clstat;
454
455 /*
456  * Key question: whether to sleep interuptably or uninteruptably when waiting
457  * for Venus.  The former seems better (cause you can ^C a job), but then
458  * GNU-EMACS completion breaks.  Use tsleep with no timeout, and no longjmp
459  * happens.  But, when sleeping "uninterruptibly", we don't get told if it
460  * returns abnormally (e.g. kill -9).
461  */
462 int
463 coda_call(struct coda_mntinfo *mntinfo, int inSize, int *outSize,
464     caddr_t buffer)
465 {
466         struct vcomm *vcp;
467         struct vmsg *vmp;
468         int error;
469 #ifdef CTL_C
470         struct thread *td = curthread;
471         struct proc *p = td->td_proc;
472         sigset_t psig_omask;
473         sigset_t tempset;
474         int i;
475 #endif
476
477         /*
478          * Unlikely, but could be a race condition with a dying warden.
479          */
480         if (mntinfo == NULL)
481                 return ENODEV;
482         vcp = &(mntinfo->mi_vcomm);
483         coda_clstat.ncalls++;
484         coda_clstat.reqs[((struct coda_in_hdr *)buffer)->opcode]++;
485         if (!VC_OPEN(vcp))
486                 return (ENODEV);
487         CODA_ALLOC(vmp,struct vmsg *,sizeof(struct vmsg));
488
489         /*
490          * Format the request message.
491          */
492         vmp->vm_data = buffer;
493         vmp->vm_flags = 0;
494         vmp->vm_inSize = inSize;
495         vmp->vm_outSize
496             = *outSize ? *outSize : inSize; /* |buffer| >= inSize */
497         vmp->vm_opcode = ((struct coda_in_hdr *)buffer)->opcode;
498         vmp->vm_unique = ++vcp->vc_seq;
499         if (codadebug)
500                 myprintf(("Doing a call for %d.%d\n", vmp->vm_opcode,
501                     vmp->vm_unique));
502
503         /*
504          * Fill in the common input args.
505          */
506         ((struct coda_in_hdr *)buffer)->unique = vmp->vm_unique;
507
508         /*
509          * Append msg to request queue and poke Venus.
510          */
511         TAILQ_INSERT_TAIL(&vcp->vc_requests, vmp, vm_chain);
512         selwakeuppri(&(vcp->vc_selproc), coda_call_sleep);
513
514         /*
515          * We can be interrupted while we wait for Venus to process our
516          * request.  If the interrupt occurs before Venus has read the
517          * request, we dequeue and return. If it occurs after the read but
518          * before the reply, we dequeue, send a signal message, and return.
519          * If it occurs after the reply we ignore it.  In no case do we want
520          * to restart the syscall.  If it was interrupted by a venus shutdown
521          * (vcclose), return ENODEV.
522          *
523          * Ignore return, we have to check anyway.
524          */
525 #ifdef CTL_C
526         /*
527          * This is work in progress.  Setting coda_pcatch lets tsleep
528          * reawaken on a ^c or ^z.  The problem is that emacs sets certain
529          * interrupts as SA_RESTART.  This means that we should exit sleep
530          * handle the "signal" and then go to sleep again.  Mostly this is
531          * done by letting the syscall complete and be restarted.  We are not
532          * idempotent and can not do this.  A better solution is necessary.
533          */
534         i = 0;
535         PROC_LOCK(p);
536         psig_omask = td->td_sigmask;
537         do {
538                 error = msleep(&vmp->vm_sleep, &p->p_mtx,
539                     (coda_call_sleep|coda_pcatch), "coda_call", hz*2);
540                 if (error == 0)
541                         break;
542                 else if (error == EWOULDBLOCK) {
543 #ifdef CODA_VERBOSE
544                         printf("coda_call: tsleep TIMEOUT %d sec\n", 2+2*i);
545 #endif
546                 }
547                 else {
548                         SIGEMPTYSET(tempset);
549                         SIGADDSET(tempset, SIGIO);
550                         if (SIGSETEQ(td->td_siglist, tempset)) {
551                                 SIGADDSET(td->td_sigmask, SIGIO);
552 #ifdef CODA_VERBOSE
553                                 printf("coda_call: tsleep returns %d SIGIO, "
554                                     "cnt %d\n", error, i);
555 #endif
556                         } else {
557                                 SIGDELSET(tempset, SIGIO);
558                                 SIGADDSET(tempset, SIGALRM);
559                                 if (SIGSETEQ(td->td_siglist, tempset)) {
560                                         SIGADDSET(td->td_sigmask, SIGALRM);
561 #ifdef CODA_VERBOSE
562                                         printf("coda_call: tsleep returns "
563                                             "%d SIGALRM, cnt %d\n", error, i);
564 #endif
565                                 } else {
566 #ifdef CODA_VERBOSE
567                                         printf("coda_call: tsleep returns "
568                                             "%d, cnt %d\n", error, i);
569 #endif
570
571 #ifdef notyet
572                                         tempset = td->td_siglist;
573                                         SIGSETNAND(tempset, td->td_sigmask);
574                                         printf("coda_call: siglist = %p, "
575                                             "sigmask = %p, mask %p\n",
576                                             td->td_siglist, td->td_sigmask,
577                                             tempset);
578                                         break;
579                                         SIGSETOR(td->td_sigmask, td->td_siglist);
580                                         tempset = td->td_siglist;
581                                         SIGSETNAND(tempset, td->td_sigmask);
582                                         printf("coda_call: new mask, "
583                                             "siglist = %p, sigmask = %p, "
584                                             "mask %p\n", td->td_siglist,
585                                             td->td_sigmask, tempset);
586 #endif
587                                 }
588                         }
589                 }
590         } while (error && i++ < 128 && VC_OPEN(vcp));
591         td->td_sigmask = psig_omask;
592         signotify(td);
593         PROC_UNLOCK(p);
594 #else
595         (void)tsleep(&vmp->vm_sleep, coda_call_sleep, "coda_call", 0);
596 #endif
597         if (VC_OPEN(vcp)) {
598                 /*
599                  * Venus is still alive.
600                  *
601                  * Op went through, interrupt or not...
602                  */
603                 if (vmp->vm_flags & VM_WRITE) {
604                         error = 0;
605                         *outSize = vmp->vm_outSize;
606                 } else if (!(vmp->vm_flags & VM_READ)) {
607                         /* Interrupted before venus read it. */
608 #ifdef CODA_VERBOSE
609                         if (1)
610 #else
611                         if (codadebug)
612 #endif
613                                 myprintf(("interrupted before read: op = "
614                                     "%d.%d, flags = %x\n", vmp->vm_opcode,
615                                     vmp->vm_unique, vmp->vm_flags));
616                         TAILQ_REMOVE(&vcp->vc_requests, vmp, vm_chain);
617                         error = EINTR;
618                 } else {
619                         /*
620                          * (!(vmp->vm_flags & VM_WRITE)) means interrupted
621                          * after upcall started.
622                          *
623                          * Interrupted after start of upcall, send venus a
624                          * signal.
625                          */
626                         struct coda_in_hdr *dog;
627                         struct vmsg *svmp;
628
629 #ifdef CODA_VERBOSE
630                         if (1)
631 #else
632                         if (codadebug)
633 #endif
634                                 myprintf(("Sending Venus a signal: op = "
635                                     "%d.%d, flags = %x\n", vmp->vm_opcode,
636                                     vmp->vm_unique, vmp->vm_flags));
637                         TAILQ_REMOVE(&vcp->vc_requests, vmp, vm_chain);
638                         error = EINTR;
639                         CODA_ALLOC(svmp, struct vmsg *, sizeof(struct vmsg));
640                         CODA_ALLOC((svmp->vm_data), char *,
641                             sizeof(struct coda_in_hdr));
642                         dog = (struct coda_in_hdr *)svmp->vm_data;
643                         svmp->vm_flags = 0;
644                         dog->opcode = svmp->vm_opcode = CODA_SIGNAL;
645                         dog->unique = svmp->vm_unique = vmp->vm_unique;
646                         svmp->vm_inSize = sizeof (struct coda_in_hdr);
647 /*??? rvb */            svmp->vm_outSize = sizeof (struct coda_in_hdr);
648                         if (codadebug)
649                                 myprintf(("coda_call: enqueing signal msg "
650                                     "(%d, %d)\n", svmp->vm_opcode,
651                                     svmp->vm_unique));
652
653                         /*
654                          * Insert at head of queue!
655                          *
656                          * XXXRW: Actually, the tail.
657                          */
658                         TAILQ_INSERT_TAIL(&vcp->vc_requests, svmp, vm_chain);
659                         selwakeuppri(&(vcp->vc_selproc), coda_call_sleep);
660                 }
661         } else {
662                 /* If venus died (!VC_OPEN(vcp)) */
663                 if (codadebug)
664                         myprintf(("vcclose woke op %d.%d flags %d\n",
665                             vmp->vm_opcode, vmp->vm_unique, vmp->vm_flags));
666                 error = ENODEV;
667         }
668         CODA_FREE(vmp, sizeof(struct vmsg));
669         if (outstanding_upcalls > 0 && (--outstanding_upcalls == 0))
670                 wakeup(&outstanding_upcalls);
671         if (!error)
672                 error = ((struct coda_out_hdr *)buffer)->result;
673         return (error);
674 }