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