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