vx32

Local 9vx git repository for patches.
git clone git://r-36.net/vx32
Log | Files | 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