xref: /plan9/sys/src/9/port/devsegment.c (revision 96cbc34f1b36a29efdcfd47b10e70703a690febc)
1 #include	"u.h"
2 #include	"../port/lib.h"
3 #include	"mem.h"
4 #include	"dat.h"
5 #include	"fns.h"
6 #include	"../port/error.h"
7 
8 enum
9 {
10 	Qtopdir,
11 	Qsegdir,
12 	Qctl,
13 	Qdata,
14 
15 	/* commands to kproc */
16 	Cnone=0,
17 	Cread,
18 	Cwrite,
19 	Cstart,
20 	Cdie,
21 };
22 
23 #define TYPE(x) 	(int)( (c)->qid.path & 0x7 )
24 #define SEG(x)	 	( ((c)->qid.path >> 3) & 0x3f )
25 #define PATH(s, t) 	( ((s)<<3) | (t) )
26 
27 typedef struct Globalseg Globalseg;
28 struct Globalseg
29 {
30 	Ref;
31 	Segment	*s;
32 
33 	char	*name;
34 	char	*uid;
35 	vlong	length;
36 	long	perm;
37 
38 	/* kproc to do reading and writing */
39 	QLock	l;		/* sync kproc access */
40 	Rendez	cmdwait;	/* where kproc waits */
41 	Rendez	replywait;	/* where requestor waits */
42 	Proc	*kproc;
43 	char	*data;
44 	long	off;
45 	int	dlen;
46 	int	cmd;
47 	char	err[64];
48 };
49 
50 static Globalseg *globalseg[100];
51 static Lock globalseglock;
52 
53 
54 	Segment* (*_globalsegattach)(Proc*, char*);
55 static	Segment* globalsegattach(Proc *p, char *name);
56 static	int	cmddone(void*);
57 static	void	segmentkproc(void*);
58 static	void	docmd(Globalseg *g, int cmd);
59 
60 /*
61  *  returns with globalseg incref'd
62  */
63 static Globalseg*
64 getgseg(Chan *c)
65 {
66 	int x;
67 	Globalseg *g;
68 
69 	x = SEG(c);
70 	lock(&globalseglock);
71 	if(x >= nelem(globalseg))
72 		panic("getgseg");
73 	g = globalseg[x];
74 	if(g != nil)
75 		incref(g);
76 	unlock(&globalseglock);
77 	if(g == nil)
78 		error("global segment disappeared");
79 	return g;
80 }
81 
82 static void
83 putgseg(Globalseg *g)
84 {
85 	if(decref(g) > 0)
86 		return;
87 	if(g->s != nil)
88 		putseg(g->s);
89 	if(g->kproc)
90 		docmd(g, Cdie);
91 	free(g->name);
92 	free(g->uid);
93 	free(g);
94 }
95 
96 static int
97 segmentgen(Chan *c, char*, Dirtab*, int, int s, Dir *dp)
98 {
99 	Qid q;
100 	Globalseg *g;
101 	ulong size;
102 
103 	switch(TYPE(c)) {
104 	case Qtopdir:
105 		if(s == DEVDOTDOT){
106 			q.vers = 0;
107 			q.path = PATH(0, Qtopdir);
108 			q.type = QTDIR;
109 			devdir(c, q, "#g", 0, eve, DMDIR|0777, dp);
110 			break;
111 		}
112 
113 		if(s >= nelem(globalseg))
114 			return -1;
115 
116 		lock(&globalseglock);
117 		g = globalseg[s];
118 		if(g == nil){
119 			unlock(&globalseglock);
120 			return 0;
121 		}
122 		q.vers = 0;
123 		q.path = PATH(s, Qsegdir);
124 		q.type = QTDIR;
125 		devdir(c, q, g->name, 0, g->uid, DMDIR|0777, dp);
126 		unlock(&globalseglock);
127 
128 		break;
129 	case Qsegdir:
130 		if(s == DEVDOTDOT){
131 			q.vers = 0;
132 			q.path = PATH(0, Qtopdir);
133 			q.type = QTDIR;
134 			devdir(c, q, "#g", 0, eve, DMDIR|0777, dp);
135 			break;
136 		}
137 		/* fall through */
138 	case Qctl:
139 	case Qdata:
140 		switch(s){
141 		case 0:
142 			g = getgseg(c);
143 			q.vers = 0;
144 			q.path = PATH(SEG(c), Qctl);
145 			q.type = QTFILE;
146 			devdir(c, q, "ctl", 0, g->uid, g->perm, dp);
147 			putgseg(g);
148 			break;
149 		case 1:
150 			g = getgseg(c);
151 			q.vers = 0;
152 			q.path = PATH(SEG(c), Qdata);
153 			q.type = QTFILE;
154 			if(g->s != nil)
155 				size = g->s->top - g->s->base;
156 			else
157 				size = 0;
158 			devdir(c, q, "data", size, g->uid, g->perm, dp);
159 			putgseg(g);
160 			break;
161 		default:
162 			return -1;
163 		}
164 		break;
165 	}
166 	return 1;
167 }
168 
169 static void
170 segmentinit(void)
171 {
172 	_globalsegattach = globalsegattach;
173 }
174 
175 static Chan*
176 segmentattach(char *spec)
177 {
178 	return devattach('g', spec);
179 }
180 
181 static Walkqid*
182 segmentwalk(Chan *c, Chan *nc, char **name, int nname)
183 {
184 	return devwalk(c, nc, name, nname, 0, 0, segmentgen);
185 }
186 
187 static int
188 segmentstat(Chan *c, uchar *db, int n)
189 {
190 	return devstat(c, db, n, 0, 0, segmentgen);
191 }
192 
193 static int
194 cmddone(void *arg)
195 {
196 	Globalseg *g = arg;
197 
198 	return g->cmd == Cnone;
199 }
200 
201 static Chan*
202 segmentopen(Chan *c, int omode)
203 {
204 	Globalseg *g;
205 
206 	switch(TYPE(c)){
207 	case Qtopdir:
208 	case Qsegdir:
209 		if(omode != 0)
210 			error(Eisdir);
211 		break;
212 	case Qctl:
213 		g = getgseg(c);
214 		if(waserror()){
215 			putgseg(g);
216 			nexterror();
217 		}
218 		devpermcheck(g->uid, g->perm, omode);
219 		c->aux = g;
220 		poperror();
221 		c->flag |= COPEN;
222 		break;
223 	case Qdata:
224 		g = getgseg(c);
225 		if(waserror()){
226 			putgseg(g);
227 			nexterror();
228 		}
229 		devpermcheck(g->uid, g->perm, omode);
230 		if(g->s == nil)
231 			error("segment not yet allocated");
232 		if(g->kproc == nil){
233 			qlock(&g->l);
234 			if(waserror()){
235 				qunlock(&g->l);
236 				nexterror();
237 			}
238 			if(g->kproc == nil){
239 				g->cmd = Cnone;
240 				kproc(g->name, segmentkproc, g);
241 				docmd(g, Cstart);
242 			}
243 			qunlock(&g->l);
244 			poperror();
245 		}
246 		c->aux = g;
247 		poperror();
248 		c->flag |= COPEN;
249 		break;
250 	default:
251 		panic("segmentopen");
252 	}
253 	c->mode = openmode(omode);
254 	c->offset = 0;
255 	return c;
256 }
257 
258 static void
259 segmentclose(Chan *c)
260 {
261 	if(TYPE(c) == Qtopdir)
262 		return;
263 	if(c->flag & COPEN)
264 		putgseg(c->aux);
265 }
266 
267 static void
268 segmentcreate(Chan *c, char *name, int omode, ulong perm)
269 {
270 	int x, xfree;
271 	Globalseg *g;
272 
273 	if(TYPE(c) != Qtopdir)
274 		error(Eperm);
275 
276 	if(isphysseg(name))
277 		error(Eexist);
278 
279 	if((perm & DMDIR) == 0)
280 		error(Ebadarg);
281 
282 	if(waserror()){
283 		unlock(&globalseglock);
284 		nexterror();
285 	}
286 	lock(&globalseglock);
287 	xfree = -1;
288 	for(x = 0; x < nelem(globalseg); x++){
289 		g = globalseg[x];
290 		if(g == nil){
291 			if(xfree < 0)
292 				xfree = x;
293 		} else {
294 			if(strcmp(g->name, name) == 0)
295 				error(Eexist);
296 		}
297 	}
298 	if(xfree < 0)
299 		error("too many global segments");
300 	g = smalloc(sizeof(Globalseg));
301 	g->ref = 1;
302 	kstrdup(&g->name, name);
303 	kstrdup(&g->uid, up->user);
304 	g->perm = 0660;
305 	globalseg[xfree] = g;
306 	unlock(&globalseglock);
307 	poperror();
308 
309 	c->qid.path = PATH(x, Qsegdir);
310 	c->qid.type = QTDIR;
311 	c->qid.vers = 0;
312 	c->mode = openmode(omode);
313 	c->mode = OWRITE;
314 }
315 
316 static long
317 segmentread(Chan *c, void *a, long n, vlong voff)
318 {
319 	Globalseg *g;
320 	char buf[32];
321 
322 	if(c->qid.type == QTDIR)
323 		return devdirread(c, a, n, (Dirtab *)0, 0L, segmentgen);
324 
325 	switch(TYPE(c)){
326 	case Qctl:
327 		g = c->aux;
328 		if(g->s == nil)
329 			error("segment not yet allocated");
330 		sprint(buf, "va %#lux %#lux\n", g->s->base, g->s->top-g->s->base);
331 		return readstr(voff, a, n, buf);
332 	case Qdata:
333 		g = c->aux;
334 		if(voff > g->s->top - g->s->base)
335 			error(Ebadarg);
336 		if(voff + n > g->s->top - g->s->base)
337 			n = g->s->top - g->s->base - voff;
338 		qlock(&g->l);
339 		g->off = voff + g->s->base;
340 		g->data = smalloc(n);
341 		if(waserror()){
342 			free(g->data);
343 			qunlock(&g->l);
344 			nexterror();
345 		}
346 		g->dlen = n;
347 		docmd(g, Cread);
348 		memmove(a, g->data, g->dlen);
349 		free(g->data);
350 		qunlock(&g->l);
351 		poperror();
352 		return g->dlen;
353 	default:
354 		panic("segmentread");
355 	}
356 	return 0;	/* not reached */
357 }
358 
359 static long
360 segmentwrite(Chan *c, void *a, long n, vlong voff)
361 {
362 	Cmdbuf *cb;
363 	Globalseg *g;
364 	ulong va, len, top;
365 
366 	if(c->qid.type == QTDIR)
367 		error(Eperm);
368 
369 	switch(TYPE(c)){
370 	case Qctl:
371 		g = c->aux;
372 		cb = parsecmd(a, n);
373 		if(strcmp(cb->f[0], "va") == 0){
374 			if(g->s != nil)
375 				error("already has a virtual address");
376 			if(cb->nf < 3)
377 				error(Ebadarg);
378 			va = strtoul(cb->f[1], 0, 0);
379 			len = strtoul(cb->f[2], 0, 0);
380 			top = PGROUND(va + len);
381 			va = va&~(BY2PG-1);
382 			len = (top - va) / BY2PG;
383 			if(len == 0)
384 				error(Ebadarg);
385 			g->s = newseg(SG_SHARED, va, len);
386 		} else
387 			error(Ebadctl);
388 		break;
389 	case Qdata:
390 		g = c->aux;
391 		if(voff + n > g->s->top - g->s->base)
392 			error(Ebadarg);
393 		qlock(&g->l);
394 		g->off = voff + g->s->base;
395 		g->data = smalloc(n);
396 		if(waserror()){
397 			free(g->data);
398 			qunlock(&g->l);
399 			nexterror();
400 		}
401 		g->dlen = n;
402 		memmove(g->data, a, g->dlen);
403 		docmd(g, Cwrite);
404 		free(g->data);
405 		qunlock(&g->l);
406 		poperror();
407 		return g->dlen;
408 	default:
409 		panic("segmentwrite");
410 	}
411 	return 0;	/* not reached */
412 }
413 
414 static int
415 segmentwstat(Chan *c, uchar *dp, int n)
416 {
417 	Globalseg *g;
418 	Dir *d;
419 
420 	if(c->qid.type == QTDIR)
421 		error(Eperm);
422 
423 	g = getgseg(c);
424 	if(waserror()){
425 		putgseg(g);
426 		nexterror();
427 	}
428 
429 	if(strcmp(g->uid, up->user) && !iseve())
430 		error(Eperm);
431 	d = smalloc(sizeof(Dir)+n);
432 	n = convM2D(dp, n, &d[0], (char*)&d[1]);
433 	g->perm = d->mode & 0777;
434 
435 	putgseg(g);
436 	poperror();
437 
438 	free(d);
439 	return n;
440 }
441 
442 static void
443 segmentremove(Chan *c)
444 {
445 	Globalseg *g;
446 	int x;
447 
448 	if(TYPE(c) != Qsegdir)
449 		error(Eperm);
450 	lock(&globalseglock);
451 	x = SEG(c);
452 	g = globalseg[x];
453 	globalseg[x] = nil;
454 	unlock(&globalseglock);
455 	if(g != nil)
456 		putgseg(g);
457 }
458 
459 /*
460  *  called by segattach()
461  */
462 static Segment*
463 globalsegattach(Proc *p, char *name)
464 {
465 	int x;
466 	Globalseg *g;
467 	Segment *s;
468 
469 	g = nil;
470 	if(waserror()){
471 		unlock(&globalseglock);
472 		nexterror();
473 	}
474 	lock(&globalseglock);
475 	for(x = 0; x < nelem(globalseg); x++){
476 		g = globalseg[x];
477 		if(g != nil && strcmp(g->name, name) == 0)
478 			break;
479 	}
480 	if(x == nelem(globalseg)){
481 		unlock(&globalseglock);
482 		poperror();
483 		return nil;
484 	}
485 	devpermcheck(g->uid, g->perm, ORDWR);
486 	s = g->s;
487 	if(s == nil)
488 		error("global segment not assigned a virtual address");
489 	if(isoverlap(p, s->base, s->top - s->base) != nil)
490 		error("overlaps existing segment");
491 	incref(s);
492 	unlock(&globalseglock);
493 	poperror();
494 	return s;
495 }
496 
497 static void
498 docmd(Globalseg *g, int cmd)
499 {
500 	g->err[0] = 0;
501 	g->cmd = cmd;
502 	wakeup(&g->cmdwait);
503 	sleep(&g->replywait, cmddone, g);
504 	if(g->err[0])
505 		error(g->err);
506 }
507 
508 static int
509 cmdready(void *arg)
510 {
511 	Globalseg *g = arg;
512 
513 	return g->cmd != Cnone;
514 }
515 
516 static void
517 segmentkproc(void *arg)
518 {
519 	Globalseg *g = arg;
520 	int done;
521 	int sno;
522 
523 	for(sno = 0; sno < NSEG; sno++)
524 		if(up->seg[sno] == nil && sno != ESEG)
525 			break;
526 	if(sno == NSEG)
527 		panic("segmentkproc");
528 	g->kproc = up;
529 
530 	incref(g->s);
531 	up->seg[sno] = g->s;
532 
533 	for(done = 0; !done;){
534 		sleep(&g->cmdwait, cmdready, g);
535 		if(waserror()){
536 			strncpy(g->err, up->errstr, sizeof(g->err));
537 		} else {
538 			switch(g->cmd){
539 			case Cstart:
540 				break;
541 			case Cdie:
542 				done = 1;
543 				break;
544 			case Cread:
545 				memmove(g->data, (char*)g->off, g->dlen);
546 				break;
547 			case Cwrite:
548 				memmove((char*)g->off, g->data, g->dlen);
549 				break;
550 			}
551 			poperror();
552 		}
553 		g->cmd = Cnone;
554 		wakeup(&g->replywait);
555 	}
556 }
557 
558 Dev segmentdevtab = {
559 	'g',
560 	"segment",
561 
562 	devreset,
563 	segmentinit,
564 	devshutdown,
565 	segmentattach,
566 	segmentwalk,
567 	segmentstat,
568 	segmentopen,
569 	segmentcreate,
570 	segmentclose,
571 	segmentread,
572 	devbread,
573 	segmentwrite,
574 	devbwrite,
575 	segmentremove,
576 	segmentwstat,
577 };
578 
579