proc->thread stage 2: MAJOR revamping of system calls, ucred, jail API,
[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  * $DragonFly: src/sys/vfs/coda/Attic/coda_psdev.c,v 1.3 2003/06/23 17:55:26 dillon Exp $
32  * 
33  */
34
35 /* 
36  * Mach Operating System
37  * Copyright (c) 1989 Carnegie-Mellon University
38  * All rights reserved.  The CMU software License Agreement specifies
39  * the terms and conditions for use and redistribution.
40  */
41
42 /*
43  * This code was written for the Coda file system at Carnegie Mellon
44  * University.  Contributers include David Steere, James Kistler, and
45  * M. Satyanarayanan.  */
46
47 /* 
48  * These routines define the psuedo device for communication between
49  * Coda's Venus and Minicache in Mach 2.6. They used to be in cfs_subr.c, 
50  * but I moved them to make it easier to port the Minicache without 
51  * porting coda. -- DCS 10/12/94
52  */
53
54 /* These routines are the device entry points for Venus. */
55
56 extern int coda_nc_initialized;    /* Set if cache has been initialized */
57
58 #include <vcoda.h>
59
60 #include <sys/param.h>
61 #include <sys/systm.h>
62 #include <sys/kernel.h>
63 #include <sys/proc.h>
64 #include <sys/malloc.h>
65 #include <sys/mount.h>
66 #include <sys/file.h>
67 #include <sys/ioccom.h>
68 #include <sys/poll.h>
69 #include <sys/conf.h>
70
71 #include <coda/coda.h>
72 #include <coda/cnode.h>
73 #include <coda/coda_namecache.h>
74 #include <coda/coda_io.h>
75 #include <coda/coda_psdev.h>
76
77 #define CTL_C
78
79 #ifdef CTL_C
80 #include <sys/signalvar.h>
81 #endif
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 int 
119 vc_nb_open(dev_t dev, int flag, int mode, d_thread_t *td)
120 {
121     register struct vcomm *vcp;
122     
123     ENTRY;
124
125     if (minor(dev) >= NVCODA || minor(dev) < 0)
126         return(ENXIO);
127     
128     if (!coda_nc_initialized)
129         coda_nc_init();
130     
131     vcp = &coda_mnttbl[minor(dev)].mi_vcomm;
132     if (VC_OPEN(vcp))
133         return(EBUSY);
134     
135     bzero(&(vcp->vc_selproc), sizeof (struct selinfo));
136     INIT_QUEUE(vcp->vc_requests);
137     INIT_QUEUE(vcp->vc_replys);
138     MARK_VC_OPEN(vcp);
139     
140     coda_mnttbl[minor(dev)].mi_vfsp = NULL;
141     coda_mnttbl[minor(dev)].mi_rootvp = NULL;
142
143     return(0);
144 }
145
146 int 
147 vc_nb_close (dev_t dev, int flag, int mode, d_thread_t *td)
148 {
149     struct vcomm *vcp;
150     struct vmsg *vmp, *nvmp = NULL;
151     struct coda_mntinfo *mi;
152     struct proc *p;
153     int                 err;
154         
155     ENTRY;
156
157     p = td->td_proc;
158     KKASSERT(p != NULL);
159
160     if (minor(dev) >= NVCODA || minor(dev) < 0)
161         return(ENXIO);
162
163     mi = &coda_mnttbl[minor(dev)];
164     vcp = &(mi->mi_vcomm);
165     
166     if (!VC_OPEN(vcp))
167         panic("vcclose: not open");
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, p);
224     if (err)
225         myprintf(("Error %d unmounting vfs in vcclose(%d)\n", 
226                    err, minor(dev)));
227     return 0;
228 }
229
230 int 
231 vc_nb_read(dev, uiop, flag)   
232     dev_t        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     if (minor(dev) >= NVCODA || minor(dev) < 0)
243         return(ENXIO);
244     
245     vcp = &coda_mnttbl[minor(dev)].mi_vcomm;
246     /* Get message at head of request queue. */
247     if (EMPTY(vcp->vc_requests))
248         return(0);      /* Nothing to read */
249     
250     vmp = (struct vmsg *)GETNEXT(vcp->vc_requests);
251     
252     /* Move the input args into userspace */
253     uiop->uio_rw = UIO_READ;
254     error = uiomove(vmp->vm_data, vmp->vm_inSize, uiop);
255     if (error) {
256         myprintf(("vcread: error (%d) on uiomove\n", error));
257         error = EINVAL;
258     }
259
260 #ifdef OLD_DIAGNOSTIC    
261     if (vmp->vm_chain.forw == 0 || vmp->vm_chain.back == 0)
262         panic("vc_nb_read: bad chain");
263 #endif
264
265     REMQUE(vmp->vm_chain);
266     
267     /* If request was a signal, free up the message and don't
268        enqueue it in the reply queue. */
269     if (vmp->vm_opcode == CODA_SIGNAL) {
270         if (codadebug)
271             myprintf(("vcread: signal msg (%d, %d)\n", 
272                       vmp->vm_opcode, vmp->vm_unique));
273         CODA_FREE((caddr_t)vmp->vm_data, (u_int)VC_IN_NO_DATA);
274         CODA_FREE((caddr_t)vmp, (u_int)sizeof(struct vmsg));
275         return(error);
276     }
277     
278     vmp->vm_flags |= VM_READ;
279     INSQUE(vmp->vm_chain, vcp->vc_replys);
280     
281     return(error);
282 }
283
284 int
285 vc_nb_write(dev, uiop, flag)   
286     dev_t        dev;  
287     struct uio  *uiop; 
288     int          flag;
289 {
290     register struct vcomm *     vcp;
291     register struct vmsg *vmp;
292     struct coda_out_hdr *out;
293     u_long seq;
294     u_long opcode;
295     int buf[2];
296     int error = 0;
297
298     ENTRY;
299
300     if (minor(dev) >= NVCODA || minor(dev) < 0)
301         return(ENXIO);
302     
303     vcp = &coda_mnttbl[minor(dev)].mi_vcomm;
304     
305     /* Peek at the opcode, unique without transfering the data. */
306     uiop->uio_rw = UIO_WRITE;
307     error = uiomove((caddr_t)buf, sizeof(int) * 2, uiop);
308     if (error) {
309         myprintf(("vcwrite: error (%d) on uiomove\n", error));
310         return(EINVAL);
311     }
312     
313     opcode = buf[0];
314     seq = buf[1];
315         
316     if (codadebug)
317         myprintf(("vcwrite got a call for %ld.%ld\n", opcode, seq));
318     
319     if (DOWNCALL(opcode)) {
320         union outputArgs pbuf;
321         
322         /* get the rest of the data. */
323         uiop->uio_rw = UIO_WRITE;
324         error = uiomove((caddr_t)&pbuf.coda_purgeuser.oh.result, sizeof(pbuf) - (sizeof(int)*2), uiop);
325         if (error) {
326             myprintf(("vcwrite: error (%d) on uiomove (Op %ld seq %ld)\n", 
327                       error, opcode, seq));
328             return(EINVAL);
329             }
330         
331         return handleDownCall(opcode, &pbuf);
332     }
333     
334     /* Look for the message on the (waiting for) reply queue. */
335     for (vmp = (struct vmsg *)GETNEXT(vcp->vc_replys);
336          !EOQ(vmp, vcp->vc_replys);
337          vmp = (struct vmsg *)GETNEXT(vmp->vm_chain))
338     {
339         if (vmp->vm_unique == seq) break;
340     }
341     
342     if (EOQ(vmp, vcp->vc_replys)) {
343         if (codadebug)
344             myprintf(("vcwrite: msg (%ld, %ld) not found\n", opcode, seq));
345         
346         return(ESRCH);
347         }
348     
349     /* Remove the message from the reply queue */
350     REMQUE(vmp->vm_chain);
351     
352     /* move data into response buffer. */
353     out = (struct coda_out_hdr *)vmp->vm_data;
354     /* Don't need to copy opcode and uniquifier. */
355     
356     /* get the rest of the data. */
357     if (vmp->vm_outSize < uiop->uio_resid) {
358         myprintf(("vcwrite: more data than asked for (%d < %d)\n",
359                   vmp->vm_outSize, uiop->uio_resid));
360         wakeup(&vmp->vm_sleep);         /* Notify caller of the error. */
361         return(EINVAL);
362     } 
363     
364     buf[0] = uiop->uio_resid;   /* Save this value. */
365     uiop->uio_rw = UIO_WRITE;
366     error = uiomove((caddr_t) &out->result, vmp->vm_outSize - (sizeof(int) * 2), uiop);
367     if (error) {
368         myprintf(("vcwrite: error (%d) on uiomove (op %ld seq %ld)\n", 
369                   error, opcode, seq));
370         return(EINVAL);
371     }
372     
373     /* I don't think these are used, but just in case. */
374     /* XXX - aren't these two already correct? -bnoble */
375     out->opcode = opcode;
376     out->unique = seq;
377     vmp->vm_outSize     = buf[0];       /* Amount of data transferred? */
378     vmp->vm_flags |= VM_WRITE;
379     wakeup(&vmp->vm_sleep);
380     
381     return(0);
382 }
383
384 int
385 vc_nb_ioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, d_thread_t *td)
386 {
387     ENTRY;
388
389     switch(cmd) {
390     case CODARESIZE: {
391         struct coda_resize *data = (struct coda_resize *)addr;
392         return(coda_nc_resize(data->hashsize, data->heapsize, IS_DOWNCALL));
393         break;
394     }
395     case CODASTATS:
396         if (coda_nc_use) {
397             coda_nc_gather_stats();
398             return(0);
399         } else {
400             return(ENODEV);
401         }
402         break;
403     case CODAPRINT:
404         if (coda_nc_use) {
405             print_coda_nc();
406             return(0);
407         } else {
408             return(ENODEV);
409         }
410         break;
411     case CIOC_KERNEL_VERSION:
412         switch (*(u_int *)addr) {
413         case 0:
414                 *(u_int *)addr = coda_kernel_version;
415                 return 0;
416                 break;
417         case 1:
418         case 2:
419                 if (coda_kernel_version != *(u_int *)addr)
420                     return ENOENT;
421                 else
422                     return 0;
423         default:
424                 return ENOENT;
425         }
426         break;
427     default :
428         return(EINVAL);
429         break;
430     }
431 }
432
433 int
434 vc_nb_poll(dev_t dev, int events, d_thread_t *td)
435 {
436     register struct vcomm *vcp;
437     int event_msk = 0;
438
439     ENTRY;
440     
441     if (minor(dev) >= NVCODA || minor(dev) < 0)
442         return(ENXIO);
443     
444     vcp = &coda_mnttbl[minor(dev)].mi_vcomm;
445     
446     event_msk = events & (POLLIN|POLLRDNORM);
447     if (!event_msk)
448         return(0);
449     
450     if (!EMPTY(vcp->vc_requests))
451         return(events & (POLLIN|POLLRDNORM));
452
453     selrecord(td, &(vcp->vc_selproc));
454     
455     return(0);
456 }
457
458 /*
459  * Statistics
460  */
461 struct coda_clstat coda_clstat;
462
463 /* 
464  * Key question: whether to sleep interuptably or uninteruptably when
465  * waiting for Venus.  The former seems better (cause you can ^C a
466  * job), but then GNU-EMACS completion breaks. Use tsleep with no
467  * timeout, and no longjmp happens. But, when sleeping
468  * "uninterruptibly", we don't get told if it returns abnormally
469  * (e.g. kill -9).  
470  */
471
472 int
473 coda_call(mntinfo, inSize, outSize, buffer) 
474      struct coda_mntinfo *mntinfo; int inSize; int *outSize; caddr_t buffer;
475 {
476         struct vcomm *vcp;
477         struct vmsg *vmp;
478         int error;
479 #ifdef  CTL_C
480         struct proc *p = curproc;
481         sigset_t psig_omask = p->p_sigmask;
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         selwakeup(&(vcp->vc_selproc));
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         do {
538                 error = tsleep(&vmp->vm_sleep,
539                                (coda_call_sleep|coda_pcatch), "coda_call",
540                                hz*2);
541                 if (error == 0)
542                         break;
543                 else if (error == EWOULDBLOCK) {
544 #ifdef  CODA_VERBOSE
545                         printf("coda_call: tsleep TIMEOUT %d sec\n", 2+2*i);
546 #endif
547                 }
548                 else {
549                         SIGEMPTYSET(tempset);
550                         SIGADDSET(tempset, SIGIO);
551                         if (SIGSETEQ(p->p_siglist, tempset)) {
552                                 SIGADDSET(p->p_sigmask, SIGIO);
553 #ifdef  CODA_VERBOSE
554                                 printf("coda_call: tsleep returns %d SIGIO, cnt %d\n",
555                                        error, i);
556 #endif
557                         } else {
558                                 SIGDELSET(tempset, SIGIO);
559                                 SIGADDSET(tempset, SIGALRM);
560                                 if (SIGSETEQ(p->p_siglist, tempset)) {
561                                         SIGADDSET(p->p_sigmask, SIGALRM);
562 #ifdef  CODA_VERBOSE
563                                         printf("coda_call: tsleep returns %d SIGALRM, cnt %d\n",
564                                                error, i);
565 #endif
566                                 }
567                                 else {
568                                         printf("coda_call: tsleep returns %d, cnt %d\n",
569                                                error, i);
570
571 #if notyet
572                                         tempset = p->p_siglist;
573                                         SIGSETNAND(tempset, p->p_sigmask);
574                                         printf("coda_call: siglist = %p, sigmask = %p, mask %p\n",
575                                                p->p_siglist, p->p_sigmask,
576                                                tempset);
577                                         break;
578                                         SIGSETOR(p->p_sigmask, p->p_siglist);
579                                         tempset = p->p_siglist;
580                                         SIGSETNAND(tempset, p->p_sigmask);
581                                         printf("coda_call: new mask, siglist = %p, sigmask = %p, mask %p\n",
582                                                p->p_siglist, p->p_sigmask,
583                                                tempset);
584 #endif
585                                 }
586                         }
587                 }
588         } while (error && i++ < 128 && VC_OPEN(vcp));
589         p->p_sigmask = psig_omask;
590 #else
591         (void) tsleep(&vmp->vm_sleep, coda_call_sleep, "coda_call", 0);
592 #endif
593         if (VC_OPEN(vcp)) {     /* Venus is still alive */
594         /* Op went through, interrupt or not... */
595             if (vmp->vm_flags & VM_WRITE) {
596                 error = 0;
597                 *outSize = vmp->vm_outSize;
598             }
599
600             else if (!(vmp->vm_flags & VM_READ)) { 
601                 /* Interrupted before venus read it. */
602 #ifdef  CODA_VERBOSE
603                 if (1)
604 #else
605                 if (codadebug)
606 #endif
607                     myprintf(("interrupted before read: op = %d.%d, flags = %x\n",
608                            vmp->vm_opcode, vmp->vm_unique, vmp->vm_flags));
609                 REMQUE(vmp->vm_chain);
610                 error = EINTR;
611             }
612             
613             else {      
614                 /* (!(vmp->vm_flags & VM_WRITE)) means interrupted after
615                    upcall started */
616                 /* Interrupted after start of upcall, send venus a signal */
617                 struct coda_in_hdr *dog;
618                 struct vmsg *svmp;
619                 
620 #ifdef  CODA_VERBOSE
621                 if (1)
622 #else
623                 if (codadebug)
624 #endif
625                     myprintf(("Sending Venus a signal: op = %d.%d, flags = %x\n",
626                            vmp->vm_opcode, vmp->vm_unique, vmp->vm_flags));
627                 
628                 REMQUE(vmp->vm_chain);
629                 error = EINTR;
630                 
631                 CODA_ALLOC(svmp, struct vmsg *, sizeof (struct vmsg));
632
633                 CODA_ALLOC((svmp->vm_data), char *, sizeof (struct coda_in_hdr));
634                 dog = (struct coda_in_hdr *)svmp->vm_data;
635                 
636                 svmp->vm_flags = 0;
637                 dog->opcode = svmp->vm_opcode = CODA_SIGNAL;
638                 dog->unique = svmp->vm_unique = vmp->vm_unique;
639                 svmp->vm_inSize = sizeof (struct coda_in_hdr);
640 /*??? rvb */    svmp->vm_outSize = sizeof (struct coda_in_hdr);
641                 
642                 if (codadebug)
643                     myprintf(("coda_call: enqueing signal msg (%d, %d)\n",
644                            svmp->vm_opcode, svmp->vm_unique));
645                 
646                 /* insert at head of queue! */
647                 INSQUE(svmp->vm_chain, vcp->vc_requests);
648                 selwakeup(&(vcp->vc_selproc));
649             }
650         }
651
652         else {  /* If venus died (!VC_OPEN(vcp)) */
653             if (codadebug)
654                 myprintf(("vcclose woke op %d.%d flags %d\n",
655                        vmp->vm_opcode, vmp->vm_unique, vmp->vm_flags));
656             
657                 error = ENODEV;
658         }
659
660         CODA_FREE(vmp, sizeof(struct vmsg));
661
662         if (outstanding_upcalls > 0 && (--outstanding_upcalls == 0))
663                 wakeup(&outstanding_upcalls);
664
665         if (!error)
666                 error = ((struct coda_out_hdr *)buffer)->result;
667         return(error);
668 }