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