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