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