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