kprocdev.c - vx32 - Local 9vx git repository for patches.
 (HTM) git clone git://r-36.net/vx32
 (DIR) Log
 (DIR) Files
 (DIR) Refs
       ---
       kprocdev.c (7367B)
       ---
            1 /*
            2  * Defer all interactions with a device into a kproc.
            3  * It is not okay for cpu0 (the one that runs user code
            4  * and thus user system calls) to block in a host OS system call.
            5  * Any device that might do so needs to be wrapped using
            6  * makekprocdev, so that all the actual Dev callbacks happen
            7  * inside a kproc running on a different cpu (pthread).
            8  */
            9 
           10 #include "u.h"
           11 #include <pthread.h>
           12 #include "lib.h"
           13 #include "mem.h"
           14 #include "dat.h"
           15 #include "fns.h"
           16 #include "error.h"
           17 
           18 int tracekdev = 0;
           19 
           20 static Dev *kdev;
           21 static int nkdev;
           22 
           23 enum
           24 {
           25         CallBread = 1,
           26         CallBwrite,
           27         CallClose,
           28         CallCreate,
           29         CallOpen,
           30         CallRead,
           31         CallRemove,
           32         CallStat,
           33         CallWalk,
           34         CallWrite,
           35         CallWstat
           36 };
           37 typedef struct Kcall Kcall;
           38 struct Kcall
           39 {
           40         int call;
           41         int done;
           42         Proc *p;
           43         char err[ERRMAX];
           44         char note[ERRMAX];
           45         pthread_t pthread;
           46 
           47         Chan *c;
           48         Chan *nc;
           49         long n;
           50         vlong off;
           51         void *a;
           52         int mode;
           53         ulong perm;
           54         char *name;
           55         Walkqid *wq;
           56         char **wname;
           57         int nwname;
           58         Block *b;
           59 };
           60 
           61 typedef struct Kserve Kserve;
           62 struct Kserve
           63 {
           64         Rendez r;
           65         Kserve *next;
           66         Kcall *kc;
           67         Proc *p;
           68 };
           69 
           70 static struct {
           71         Lock lk;
           72         Kserve *servers;
           73         Kcall *calls;
           74 } kstate;
           75 
           76 static void
           77 kserve(Kcall *kc)
           78 {
           79         Dev *d;
           80 
           81         /* todo: change identity */
           82 
           83         d = &kdev[kc->c->type];
           84         switch(kc->call){
           85         default:
           86                 snprint(kc->err, sizeof kc->err, "unknown call %d", kc->call);
           87                 return;
           88         case CallWalk:
           89                 kc->wq = d->walk(kc->c, kc->nc, kc->wname, kc->nwname);
           90                 break;
           91         case CallStat:
           92                 kc->n = d->stat(kc->c, kc->a, kc->n);
           93                 break;
           94         case CallWstat:
           95                 kc->n = d->wstat(kc->c, kc->a,kc->n);
           96                 break;
           97         case CallOpen:
           98                 kc->nc = d->open(kc->c, kc->mode);
           99                 break;
          100         case CallCreate:
          101                 d->create(kc->c, kc->name, kc->mode, kc->perm);
          102                 break;
          103         case CallRead:
          104                 kc->n = d->read(kc->c, kc->a, kc->n, kc->off);
          105                 break;
          106         case CallWrite:
          107                 kc->n = d->write(kc->c, kc->a, kc->n, kc->off);
          108                 break;
          109         case CallClose:
          110                 d->close(kc->c);
          111                 break;
          112         case CallRemove:
          113                 d->remove(kc->c);
          114                 break;
          115         case CallBread:
          116                 kc->b = d->bread(kc->c, kc->n, kc->off);
          117                 break;
          118         case CallBwrite:
          119                 kc->n = d->bwrite(kc->c, kc->b, kc->off);
          120                 break;
          121         }        
          122 }
          123 
          124 static int
          125 havekc(void *v)
          126 {
          127         Kserve *s;
          128         
          129         s = v;
          130         return s->kc != nil;
          131 }
          132 
          133 static void
          134 kserver(void *v)
          135 {
          136         Kserve *s;
          137         Kcall *kc;
          138 
          139         s = v;
          140         s->p = up;
          141         for(;;){
          142                 /* wait for request */
          143                 while(s->kc == nil){
          144                         sleep(&s->r, havekc, s);
          145                 }
          146 
          147                 /* serve request */
          148                 kc = s->kc;
          149                 if(tracekdev)
          150                         print("kserver %ld has %ld %s [%p %p]\n",
          151                                 up->pid, kc->p->pid, kc->p->text, kc, kc->c);
          152                 s->kc = nil;
          153                 if(!waserror()){
          154                         kc->pthread = pthread_self();
          155                         kserve(kc);
          156                         kc->pthread = 0;
          157                         poperror();
          158                 }else
          159                         strcpy(kc->err, up->errstr);
          160                 kc->done = 1;
          161                 wakeup(&kc->p->sleep);
          162         }
          163 }
          164 
          165 static int
          166 donekc(void *v)
          167 {
          168         Kcall *kc;
          169         
          170         kc = v;
          171         return kc->done;
          172 }
          173 
          174 void
          175 kcall(Kcall *kc, int call)
          176 {
          177         Kserve *s;
          178         pthread_t p;
          179 
          180         kc->call = call;
          181         kc->p = up;
          182 
          183         if(up->kp){
          184                 /* already in a kproc; call directly. */
          185                 kserve(kc);
          186                 return;
          187         }
          188 
          189         /* acquire server */
          190         lock(&kstate.lk);
          191         if(kstate.servers){
          192                 s = kstate.servers;
          193                 kstate.servers = s->next;
          194                 s->kc = kc;
          195                 if(tracekdev)
          196                         print("kcall %ld %s has %ld [%p %p]\n",
          197                                 up->pid, up->text, s->p->pid, kc, kc->c);
          198                 wakeup(&s->r);
          199         }else{
          200                 s = malloc(sizeof *s);
          201                 s->kc = kc;
          202                 if(tracekdev)
          203                         print("kcall %ld %s forks new server\n", up->pid, up->text);
          204                 kproc("*io*", kserver, s);
          205         }
          206         unlock(&kstate.lk);
          207 
          208         while(waserror()){
          209                 strcpy(kc->note, up->errstr);
          210                 p = kc->pthread;
          211                 if(!kc->done && p)
          212                         pthread_kill(p, SIGUSR1);
          213         }
          214         while(!kc->done)
          215                 sleep(&up->sleep, donekc, kc);
          216         poperror();
          217 
          218         if(tracekdev)
          219                 print("kcall %ld %s releases %ld\n",
          220                         up->pid, up->text, s->p->pid);
          221         /* release server */
          222         lock(&kstate.lk);
          223         s->next = kstate.servers;
          224         kstate.servers = s;
          225         unlock(&kstate.lk);
          226 
          227         if(kc->err[0])
          228                 error(kc->err);
          229 }
          230 
          231 static Walkqid*
          232 kdevwalk(Chan *c, Chan *nc, char **name, int nname)
          233 {
          234         Kcall kc;
          235 
          236         // Okay to pass name pointers, because they
          237         // are kernel-allocated strings.
          238 
          239         memset(&kc, 0, sizeof kc);
          240         kc.c = c;
          241         kc.nc = nc;
          242         kc.wname = name;
          243         kc.nwname = nname;
          244         kcall(&kc, CallWalk);
          245         return kc.wq;
          246 }
          247 
          248 static int
          249 kdevstat(Chan *c, uchar *a, int n)
          250 {
          251         Kcall kc;
          252         uchar *buf;
          253 
          254         /*
          255          * Have to copy in case a is a user pointer -- the
          256          * kproc doesn't run in the address space of up.
          257          * TODO: Don't copy if a is a kernel pointer.
          258          */
          259         buf = smalloc(n);
          260         if(waserror()){
          261                 free(buf);
          262                 nexterror();
          263         }
          264 
          265         memset(&kc, 0, sizeof kc);
          266         kc.c = c;
          267         kc.a = buf;
          268         kc.n = n;
          269         kcall(&kc, CallStat);
          270         memmove(a, buf, kc.n);
          271         poperror();
          272         free(buf);
          273         return kc.n;
          274 }
          275 
          276 static Chan*
          277 kdevopen(Chan *c, int mode)
          278 {
          279         Kcall kc;
          280         
          281         memset(&kc, 0, sizeof kc);
          282         kc.c = c;
          283         kc.mode = mode;
          284         kcall(&kc, CallOpen);
          285         return kc.nc;
          286 }
          287 
          288 static void
          289 kdevcreate(Chan *c, char *name, int mode, ulong perm)
          290 {
          291         Kcall kc;
          292         
          293         memset(&kc, 0, sizeof kc);
          294         kc.c = c;
          295         kc.name = name;
          296         kc.mode = mode;
          297         kc.perm = perm;
          298         kcall(&kc, CallCreate);
          299 }
          300 
          301 static void
          302 kdevclose(Chan *c)
          303 {
          304         Kcall kc;
          305         
          306         memset(&kc, 0, sizeof kc);
          307         kc.c = c;
          308         kcall(&kc, CallClose);
          309 }
          310 
          311 static long
          312 kdevread(Chan *c, void *a, long n, vlong off)
          313 {
          314         Kcall kc;
          315         uchar *buf;
          316 
          317         /*
          318          * Have to copy in case a is a user pointer -- the
          319          * kproc doesn't run in the address space of up.
          320          * TODO: Don't copy if a is a kernel pointer.
          321          */
          322         buf = smalloc(n);
          323         if(waserror()){
          324                 free(buf);
          325                 nexterror();
          326         }
          327 
          328         memset(&kc, 0, sizeof kc);
          329         kc.c = c;
          330         kc.a = buf;
          331         kc.n = n;
          332         kc.off = off;
          333         kcall(&kc, CallRead);
          334         memmove(a, buf, kc.n);
          335         poperror();
          336         free(buf);
          337         return kc.n;
          338 }
          339 
          340 static long
          341 kdevwrite(Chan *c, void *a, long n, vlong off)
          342 {
          343         Kcall kc;
          344         uchar *buf;
          345 
          346         /*
          347          * Have to copy in case a is a user pointer -- the
          348          * kproc doesn't run in the address space of up.
          349          * TODO: Don't copy if a is a kernel pointer.
          350          */
          351         buf = smalloc(n);
          352         if(waserror()){
          353                 free(buf);
          354                 nexterror();
          355         }
          356 
          357         memmove(buf, a, n);
          358         memset(&kc, 0, sizeof kc);
          359         kc.c = c;
          360         kc.a = buf;
          361         kc.n = n;
          362         kc.off = off;
          363         kcall(&kc, CallWrite);
          364         poperror();
          365         free(buf);
          366         return kc.n;
          367 }
          368 
          369 static void
          370 kdevremove(Chan *c)
          371 {
          372         Kcall kc;
          373         
          374         memset(&kc, 0, sizeof kc);
          375         kc.c = c;
          376         kcall(&kc, CallRemove);
          377 }
          378 
          379 static int
          380 kdevwstat(Chan *c, uchar *a, int n)
          381 {
          382         Kcall kc;
          383         uchar *buf;
          384         
          385         /*
          386          * Have to copy in case a is a user pointer -- the
          387          * kproc doesn't run in the address space of up.
          388          * TODO: Don't copy if a is a kernel pointer.
          389          */
          390         buf = smalloc(n);
          391         if(waserror()){
          392                 free(buf);
          393                 nexterror();
          394         }        
          395         memmove(buf, a, n);
          396         memset(&kc, 0, sizeof kc);
          397         kc.c = c;
          398         kc.a = buf;
          399         kc.n = n;
          400         kcall(&kc, CallWstat);
          401         poperror();
          402         free(buf);
          403         return kc.n;
          404 }
          405 
          406 static Block*
          407 kdevbread(Chan *c, long n, ulong offset)
          408 {
          409         Kcall kc;
          410 
          411         memset(&kc, 0, sizeof kc);
          412         kc.c = c;
          413         kc.n = n;
          414         kc.off = offset;
          415         kcall(&kc, CallBread);
          416         return kc.b;
          417 }
          418 
          419 static long
          420 kdevbwrite(Chan *c, Block *bp, ulong offset)
          421 {
          422         Kcall kc;
          423         
          424         memset(&kc, 0, sizeof kc);
          425         kc.c = c;
          426         kc.b = bp;
          427         kc.off = offset;
          428         kcall(&kc, CallBwrite);
          429         return kc.n;
          430 }
          431 
          432 void
          433 makekprocdev(Dev *d)
          434 {
          435         int i;
          436         
          437         if(kdev == nil){
          438                 for(nkdev=0; devtab[nkdev]; nkdev++)
          439                         ;
          440                 kdev = malloc(nkdev*sizeof kdev[0]);
          441         }
          442         
          443         for(i=0; devtab[i] && devtab[i] != d; i++)
          444                 ;
          445         if(devtab[i] == nil)
          446                 panic("kdevinit");
          447         kdev[i] = *d;
          448 
          449         d->walk = kdevwalk;
          450         d->stat = kdevstat;
          451         d->open = kdevopen;
          452         d->create = kdevcreate;
          453         d->close = kdevclose;
          454         d->read = kdevread;
          455         d->bread = kdevbread;
          456         d->write = kdevwrite;
          457         d->bwrite = kdevbwrite;
          458         d->remove = kdevremove;
          459         d->wstat = kdevwstat;
          460 }
          461