xref: /plan9/sys/src/9/port/devsegment.c (revision 4e3613ab15c331a9ada113286cc0f2a35bc0373d)
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*
getgseg(Chan * c)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
putgseg(Globalseg * g)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
segmentgen(Chan * c,char *,Dirtab *,int,int s,Dir * dp)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
segmentinit(void)170 segmentinit(void)
171 {
172 	_globalsegattach = globalsegattach;
173 }
174 
175 static Chan*
segmentattach(char * spec)176 segmentattach(char *spec)
177 {
178 	return devattach('g', spec);
179 }
180 
181 static Walkqid*
segmentwalk(Chan * c,Chan * nc,char ** name,int nname)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
segmentstat(Chan * c,uchar * db,int n)188 segmentstat(Chan *c, uchar *db, int n)
189 {
190 	return devstat(c, db, n, 0, 0, segmentgen);
191 }
192 
193 static int
cmddone(void * arg)194 cmddone(void *arg)
195 {
196 	Globalseg *g = arg;
197 
198 	return g->cmd == Cnone;
199 }
200 
201 static Chan*
segmentopen(Chan * c,int omode)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
segmentclose(Chan * c)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
segmentcreate(Chan * c,char * name,int omode,ulong perm)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
segmentread(Chan * c,void * a,long n,vlong voff)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 		snprint(buf, sizeof buf, "va %#lux %#lux\n", g->s->base,
331 			g->s->top-g->s->base);
332 		return readstr(voff, a, n, buf);
333 	case Qdata:
334 		g = c->aux;
335 		if(voff > g->s->top - g->s->base)
336 			error(Ebadarg);
337 		if(voff + n > g->s->top - g->s->base)
338 			n = g->s->top - g->s->base - voff;
339 		qlock(&g->l);
340 		g->off = voff + g->s->base;
341 		g->data = smalloc(n);
342 		if(waserror()){
343 			free(g->data);
344 			qunlock(&g->l);
345 			nexterror();
346 		}
347 		g->dlen = n;
348 		docmd(g, Cread);
349 		memmove(a, g->data, g->dlen);
350 		free(g->data);
351 		qunlock(&g->l);
352 		poperror();
353 		return g->dlen;
354 	default:
355 		panic("segmentread");
356 	}
357 	return 0;	/* not reached */
358 }
359 
360 static long
segmentwrite(Chan * c,void * a,long n,vlong voff)361 segmentwrite(Chan *c, void *a, long n, vlong voff)
362 {
363 	Cmdbuf *cb;
364 	Globalseg *g;
365 	ulong va, len, top;
366 
367 	if(c->qid.type == QTDIR)
368 		error(Eperm);
369 
370 	switch(TYPE(c)){
371 	case Qctl:
372 		g = c->aux;
373 		cb = parsecmd(a, n);
374 		if(strcmp(cb->f[0], "va") == 0){
375 			if(g->s != nil)
376 				error("already has a virtual address");
377 			if(cb->nf < 3)
378 				error(Ebadarg);
379 			va = strtoul(cb->f[1], 0, 0);
380 			len = strtoul(cb->f[2], 0, 0);
381 			top = PGROUND(va + len);
382 			va = va&~(BY2PG-1);
383 			len = (top - va) / BY2PG;
384 			if(len == 0)
385 				error(Ebadarg);
386 			g->s = newseg(SG_SHARED, va, len);
387 		} else
388 			error(Ebadctl);
389 		break;
390 	case Qdata:
391 		g = c->aux;
392 		if(voff + n > g->s->top - g->s->base)
393 			error(Ebadarg);
394 		qlock(&g->l);
395 		g->off = voff + g->s->base;
396 		g->data = smalloc(n);
397 		if(waserror()){
398 			free(g->data);
399 			qunlock(&g->l);
400 			nexterror();
401 		}
402 		g->dlen = n;
403 		memmove(g->data, a, g->dlen);
404 		docmd(g, Cwrite);
405 		free(g->data);
406 		qunlock(&g->l);
407 		poperror();
408 		return g->dlen;
409 	default:
410 		panic("segmentwrite");
411 	}
412 	return 0;	/* not reached */
413 }
414 
415 static int
segmentwstat(Chan * c,uchar * dp,int n)416 segmentwstat(Chan *c, uchar *dp, int n)
417 {
418 	Globalseg *g;
419 	Dir *d;
420 
421 	if(c->qid.type == QTDIR)
422 		error(Eperm);
423 
424 	g = getgseg(c);
425 	if(waserror()){
426 		putgseg(g);
427 		nexterror();
428 	}
429 
430 	if(strcmp(g->uid, up->user) && !iseve())
431 		error(Eperm);
432 	d = smalloc(sizeof(Dir)+n);
433 	n = convM2D(dp, n, &d[0], (char*)&d[1]);
434 	g->perm = d->mode & 0777;
435 
436 	putgseg(g);
437 	poperror();
438 
439 	free(d);
440 	return n;
441 }
442 
443 static void
segmentremove(Chan * c)444 segmentremove(Chan *c)
445 {
446 	Globalseg *g;
447 	int x;
448 
449 	if(TYPE(c) != Qsegdir)
450 		error(Eperm);
451 	lock(&globalseglock);
452 	x = SEG(c);
453 	g = globalseg[x];
454 	globalseg[x] = nil;
455 	unlock(&globalseglock);
456 	if(g != nil)
457 		putgseg(g);
458 }
459 
460 /*
461  *  called by segattach()
462  */
463 static Segment*
globalsegattach(Proc * p,char * name)464 globalsegattach(Proc *p, char *name)
465 {
466 	int x;
467 	Globalseg *g;
468 	Segment *s;
469 
470 	g = nil;
471 	if(waserror()){
472 		unlock(&globalseglock);
473 		nexterror();
474 	}
475 	lock(&globalseglock);
476 	for(x = 0; x < nelem(globalseg); x++){
477 		g = globalseg[x];
478 		if(g != nil && strcmp(g->name, name) == 0)
479 			break;
480 	}
481 	if(x == nelem(globalseg)){
482 		unlock(&globalseglock);
483 		poperror();
484 		return nil;
485 	}
486 	devpermcheck(g->uid, g->perm, ORDWR);
487 	s = g->s;
488 	if(s == nil)
489 		error("global segment not assigned a virtual address");
490 	if(isoverlap(p, s->base, s->top - s->base) != nil)
491 		error("overlaps existing segment");
492 	incref(s);
493 	unlock(&globalseglock);
494 	poperror();
495 	return s;
496 }
497 
498 static void
docmd(Globalseg * g,int cmd)499 docmd(Globalseg *g, int cmd)
500 {
501 	g->err[0] = 0;
502 	g->cmd = cmd;
503 	wakeup(&g->cmdwait);
504 	sleep(&g->replywait, cmddone, g);
505 	if(g->err[0])
506 		error(g->err);
507 }
508 
509 static int
cmdready(void * arg)510 cmdready(void *arg)
511 {
512 	Globalseg *g = arg;
513 
514 	return g->cmd != Cnone;
515 }
516 
517 static void
segmentkproc(void * arg)518 segmentkproc(void *arg)
519 {
520 	Globalseg *g = arg;
521 	int done;
522 	int sno;
523 
524 	for(sno = 0; sno < NSEG; sno++)
525 		if(up->seg[sno] == nil && sno != ESEG)
526 			break;
527 	if(sno == NSEG)
528 		panic("segmentkproc");
529 	g->kproc = up;
530 
531 	incref(g->s);
532 	up->seg[sno] = g->s;
533 
534 	for(done = 0; !done;){
535 		sleep(&g->cmdwait, cmdready, g);
536 		if(waserror()){
537 			strncpy(g->err, up->errstr, sizeof(g->err));
538 		} else {
539 			switch(g->cmd){
540 			case Cstart:
541 				break;
542 			case Cdie:
543 				done = 1;
544 				break;
545 			case Cread:
546 				memmove(g->data, (char*)g->off, g->dlen);
547 				break;
548 			case Cwrite:
549 				memmove((char*)g->off, g->data, g->dlen);
550 				break;
551 			}
552 			poperror();
553 		}
554 		g->cmd = Cnone;
555 		wakeup(&g->replywait);
556 	}
557 }
558 
559 Dev segmentdevtab = {
560 	'g',
561 	"segment",
562 
563 	devreset,
564 	segmentinit,
565 	devshutdown,
566 	segmentattach,
567 	segmentwalk,
568 	segmentstat,
569 	segmentopen,
570 	segmentcreate,
571 	segmentclose,
572 	segmentread,
573 	devbread,
574 	segmentwrite,
575 	devbwrite,
576 	segmentremove,
577 	segmentwstat,
578 };
579 
580