xref: /inferno-os/emu/port/devds.c (revision 6cde411a8ffd477459336cedf48034e46f56f913)
1 /*
2  * (file system) device subsystems
3  * '#k'.
4  * Follows device config in Ken's file server.
5  * Builds mirrors, device cats, interleaving, and partition of devices out of
6  * other (inner) devices.
7  *
8  * This code is from Plan 9, and subject to the Lucent Public License 1.02.
9  * Only the name changed for Inferno (name clash).
10  */
11 
12 #include "dat.h"
13 #include "fns.h"
14 #include "../port/error.h"
15 
16 enum {
17 	Fmirror,	// mirror of others
18 	Fcat,		// catenation of others
19 	Finter,		// interleaving of others
20 	Fpart,		// part of others
21 
22 	Blksize	= 8*1024,	// for Finter only
23 	Maxconf	= 1024,		// max length for config
24 
25 	Nfsdevs = 64,
26 	Ndevs	= 8,
27 
28 	Qtop	= 0,	// top dir (contains "ds")
29 	Qdir	= 1,	// actual dir
30 	Qctl	= 2,	// ctl file
31 	Qfirst	= 3,	// first fs file
32 };
33 
34 #define	Cfgstr	"fsdev:\n"
35 
36 typedef struct Fsdev Fsdev;
37 
38 struct Fsdev
39 {
40 	int	type;
41 	char	*name;		// name for this fsdev
42 	vlong	start;		// start address (for Fpart)
43 	vlong	size;		// min(idev sizes)
44 	int	ndevs;		// number of inner devices
45 	char	*iname[Ndevs];	// inner device names
46 	Chan	*idev[Ndevs];	// inner devices
47 	vlong	isize[Ndevs];	// sizes for inneer devices
48 };
49 
50 /*
51  * Once configured, a fsdev is never removed. The name of those
52  * configured is never nil. We have no locks here.
53  */
54 static Fsdev	fsdev[Nfsdevs];
55 
56 static Qid	tqid = {Qtop, 0, QTDIR};
57 static Qid	dqid = {Qdir, 0, QTDIR};
58 static Qid	cqid = {Qctl, 0, 0};
59 
60 static Cmdtab configs[] = {
61 	Fmirror,"mirror",	0,
62 	Fcat,	"cat",		0,
63 	Finter,	"inter",	0,
64 	Fpart,	"part",		5,
65 };
66 
67 static char	_confstr[Maxconf];
68 static int	configed;
69 
70 
71 static Fsdev*
72 path2dev(int i, int mustexist)
73 {
74 	if (i < 0 || i >= nelem(fsdev))
75 		error("bug: bad index in devfsdev");
76 	if (mustexist && fsdev[i].name == nil)
77 		error(Enonexist);
78 
79 	if (fsdev[i].name == nil)
80 		return nil;
81 	else
82 		return &fsdev[i];
83 }
84 
85 static Fsdev*
86 devalloc(void)
87 {
88 	int	i;
89 
90 	for (i = 0; i < nelem(fsdev); i++)
91 		if (fsdev[i].name == nil)
92 			break;
93 	if (i == nelem(fsdev))
94 		error(Enodev);
95 
96 	return &fsdev[i];
97 }
98 
99 static void
100 setdsize(Fsdev* mp)
101 {
102 	uchar	buf[128];	/* old DIRLEN plus a little should be plenty */
103 	int	i;
104 	Chan	*mc;
105 	Dir	d;
106 	long	l;
107 
108 	if (mp->type != Fpart){
109 		mp->start= 0;
110 		mp->size = 0LL;
111 	}
112 	for (i = 0; i < mp->ndevs; i++){
113 		mc = mp->idev[i];
114 		l = devtab[mc->type]->stat(mc, buf, sizeof(buf));
115 		convM2D(buf, l, &d, nil);
116 		mp->isize[i] = d.length;
117 		switch(mp->type){
118 		case Fmirror:
119 			if (mp->size == 0LL || mp->size > d.length)
120 				mp->size = d.length;
121 			break;
122 		case Fcat:
123 			mp->size += d.length;
124 			break;
125 		case Finter:
126 			// truncate to multiple of Blksize
127 			d.length = (d.length & ~(Blksize-1));
128 			mp->isize[i] = d.length;
129 			mp->size += d.length;
130 			break;
131 		case Fpart:
132 			// should raise errors here?
133 			if (mp->start > d.length)
134 				mp->start = d.length;
135 			if (d.length < mp->start + mp->size)
136 				mp->size = d.length - mp->start;
137 			break;
138 		}
139 	}
140 }
141 
142 static void
143 mpshut(Fsdev *mp)
144 {
145 	int	i;
146 	char	*nm;
147 
148 	nm = mp->name;
149 	mp->name = nil;		// prevent others from using this.
150 	if (nm)
151 		free(nm);
152 	for (i = 0; i < mp->ndevs; i++){
153 		if (mp->idev[i] != nil)
154 			cclose(mp->idev[i]);
155 		if (mp->iname[i])
156 			free(mp->iname[i]);
157 	}
158 	memset(mp, 0, sizeof(*mp));
159 }
160 
161 
162 static void
163 mconfig(char* a, long n)	// "name idev0 idev1"
164 {
165 	static	QLock	lck;
166 	Cmdbuf	*cb;
167 	Cmdtab	*ct;
168 	Fsdev	*mp;
169 	int	i;
170 	char	*oldc;
171 	char	*c;
172 	vlong	size, start;
173 
174 	size = 0;
175 	start = 0;
176 	if (_confstr[0] == 0)
177 		seprint(_confstr, _confstr+sizeof(_confstr), Cfgstr);
178 	oldc = _confstr + strlen(_confstr);
179 	qlock(&lck);
180 	if (waserror()){
181 		*oldc = 0;
182 		qunlock(&lck);
183 		nexterror();
184 	}
185 	cb = parsecmd(a, n);
186 	if(waserror()){
187 		free(cb);
188 		nexterror();
189 	}
190 	c = oldc;
191 	for (i = 0; i < cb->nf; i++)
192 		c = seprint(c, _confstr+sizeof(_confstr), "%s ", cb->f[i]);
193 	*(c-1) = '\n';
194 	ct = lookupcmd(cb, configs, nelem(configs));
195 	cb->f++;	// skip command
196 	cb->nf--;
197 	if (ct->index == Fpart){
198 		size = strtoll(cb->f[3], nil, 10);
199 		cb->nf--;
200 		start = strtoll(cb->f[2], nil, 10);
201 		cb->nf--;
202 	}
203 	for (i = 0; i < nelem(fsdev); i++)
204 		if (fsdev[i].name != nil && strcmp(fsdev[i].name, cb->f[0])==0)
205 			error(Eexist);
206 	if (cb->nf - 1 > Ndevs)
207 		error("too many devices; fix me");
208 	for (i = 0; i < cb->nf; i++)
209 		validname(cb->f[i], (i != 0));
210 	mp = devalloc();
211 	if(waserror()){
212 		mpshut(mp);
213 		nexterror();
214 	}
215 	mp->type = ct->index;
216 	if (mp->type == Fpart){
217 		mp->size = size;
218 		mp->start = start;
219 	}
220 	kstrdup(&mp->name, cb->f[0]);
221 	for (i = 1; i < cb->nf; i++){
222 		kstrdup(&mp->iname[i-1], cb->f[i]);
223 		mp->idev[i-1] = namec(mp->iname[i-1], Aopen, ORDWR, 0);
224 		if (mp->idev[i-1] == nil)
225 			error(Egreg);
226 		mp->ndevs++;
227 	}
228 	setdsize(mp);
229 	poperror();
230 	free(cb);
231 	poperror();
232 	poperror();
233 	configed = 1;
234 	qunlock(&lck);
235 
236 }
237 
238 static void
239 rdconf(void)
240 {
241 	int	mustrd;
242 	char	*s;
243 	char	*c;
244 	char	*p;
245 	char	*e;
246 	Chan	*cc;
247 
248 	mustrd = 0;
249 	s = "/dev/sdC0/fscfg";
250 	if (waserror()){
251 		configed = 1;
252 		if (!mustrd)
253 			return;
254 		nexterror();
255 	}
256 	cc = namec(s, Aopen, OREAD, 0);
257 	if(waserror()){
258 		cclose(cc);
259 		nexterror();
260 	}
261 	devtab[cc->type]->read(cc, _confstr, sizeof(_confstr), 0);
262 	poperror();
263 	cclose(cc);
264 	if (strncmp(_confstr, Cfgstr, strlen(Cfgstr)) != 0)
265 		error("config string must start with `fsdev:'");
266 	kstrdup(&c, _confstr + strlen(Cfgstr));
267 	if(waserror()){
268 		free(c);
269 		nexterror();
270 	}
271 	memset(_confstr, 0, sizeof(_confstr));
272 	for (p = c; p != nil && *p != 0; p = e){
273 		e = strchr(p, '\n');
274 		if (e == p){
275 			e++;
276 			continue;
277 		}
278 		if (e == nil)
279 			e = p + strlen(p);
280 		mconfig(p, e - p);
281 	}
282 	poperror();
283 	poperror();
284 }
285 
286 
287 static int
288 mgen(Chan *c, char *name, Dirtab *tab, int ntab, int i, Dir *dp)
289 {
290 	Qid	qid;
291 	Fsdev	*mp;
292 
293 	if (c->qid.path == Qtop){
294 		switch(i){
295 		case DEVDOTDOT:
296 			devdir(c, tqid, "#k", 0, eve, DMDIR|0775, dp);
297 			return 1;
298 		case 0:
299 			devdir(c, dqid, "ds", 0, eve, DMDIR|0775, dp);
300 			return 1;
301 		default:
302 			return -1;
303 		}
304 	}
305 	if (c->qid.path != Qdir){
306 		switch(i){
307 		case DEVDOTDOT:
308 			devdir(c, dqid, "ds", 0, eve, DMDIR|0775, dp);
309 			return 1;
310 		default:
311 			return -1;
312 		}
313 	}
314 	switch(i){
315 	case DEVDOTDOT:
316 		devdir(c, tqid, "#k", 0, eve, DMDIR|0775, dp);
317 		return 1;
318 	case 0:
319 		devdir(c, cqid, "ctl", 0, eve, 0664, dp);
320 		return 1;
321 	}
322 	i--;	// for ctl
323 	qid.path = Qfirst + i;
324 	qid.vers = 0;
325 	qid.type = 0;
326 	mp = path2dev(i, 0);
327 	if (mp == nil)
328 		return -1;
329 	kstrcpy(up->genbuf, mp->name, sizeof(up->genbuf));
330 	devdir(c, qid, up->genbuf, mp->size, eve, 0664, dp);
331 	return 1;
332 }
333 
334 static Chan*
335 mattach(char *spec)
336 {
337 	*_confstr = 0;
338 	return devattach(L'k', spec);
339 }
340 
341 static Walkqid*
342 mwalk(Chan *c, Chan *nc, char **name, int nname)
343 {
344 	if (!configed)
345 		rdconf();
346 	return devwalk(c, nc, name, nname, 0, 0, mgen);
347 }
348 
349 static int
350 mstat(Chan *c, uchar *db, int n)
351 {
352 	Dir	d;
353 	Fsdev	*mp;
354 	int	p;
355 
356 	p = c->qid.path;
357 	memset(&d, 0, sizeof(d));
358 	switch(p){
359 	case Qtop:
360 		devdir(c, tqid, "#k", 0, eve, DMDIR|0775, &d);
361 		break;
362 	case Qdir:
363 		devdir(c, dqid, "ds", 0, eve, DMDIR|0775, &d);
364 		break;
365 	case Qctl:
366 		devdir(c, cqid, "ctl", 0, eve, 0664, &d);
367 		break;
368 	default:
369 		mp = path2dev(p - Qfirst, 1);
370 		devdir(c, c->qid, mp->name, mp->size, eve, 0664, &d);
371 	}
372 	n = convD2M(&d, db, n);
373 	if (n == 0)
374 		error(Ebadarg);
375 	return n;
376 }
377 
378 static Chan*
379 mopen(Chan *c, int omode)
380 {
381 	if((c->qid.type & QTDIR) && omode != OREAD)
382 		error(Eperm);
383 	if (omode & OTRUNC)
384 		omode &= ~OTRUNC;
385 	c->mode = openmode(omode);
386 	c->flag |= COPEN;
387 	c->offset = 0;
388 	return c;
389 }
390 
391 static void
392 mclose(Chan *c)
393 {
394 	// that's easy
395 }
396 
397 static long
398 catio(Fsdev *mp, int isread, void *a, long n, vlong off)
399 {
400 	int	i;
401 	Chan*	mc;
402 	long	l, wl, res;
403 	//print("catio %d %p %ld %lld\n", isread, a, n, off);
404 	res = n;
405 	for (i = 0; n >= 0 && i < mp->ndevs ; i++){
406 		mc = mp->idev[i];
407 		if (off > mp->isize[i]){
408 			off -= mp->isize[i];
409 			continue;
410 		}
411 		if (off + n > mp->isize[i])
412 			l = mp->isize[i] - off;
413 		else
414 			l = n;
415 		//print("\tdev %d %p %ld %lld\n", i, a, l, off);
416 
417 		if (isread)
418 			wl = devtab[mc->type]->read(mc, a, l, off);
419 		else
420 			wl = devtab[mc->type]->write(mc, a, l, off);
421 		if (wl != l)
422 			error("#k: write failed");
423 		a = (char*)a + l;
424 		off = 0;
425 		n -= l;
426 	}
427 	//print("\tres %ld\n", res - n);
428 	return res - n;
429 }
430 
431 static long
432 interio(Fsdev *mp, int isread, void *a, long n, vlong off)
433 {
434 	int	i;
435 	Chan*	mc;
436 	long	l, wl, wsz;
437 	vlong	woff, blk, mblk;
438 	long	boff, res;
439 
440 	blk  = off / Blksize;
441 	boff = off % Blksize;
442 	wsz  = Blksize - boff;
443 	res = n;
444 	while(n > 0){
445 		i    = blk % mp->ndevs;
446 		mc   = mp->idev[i];
447 		mblk = blk / mp->ndevs;
448 		woff = mblk * Blksize + boff;
449 		if (n > wsz)
450 			l = wsz;
451 		else
452 			l = n;
453 		if (isread)
454 			wl = devtab[mc->type]->read(mc, a, l, woff);
455 		else
456 			wl = devtab[mc->type]->write(mc, a, l, woff);
457 		if (wl != l || l == 0)
458 			error(Eio);
459 		a = (char*)a + l;
460 		n -= l;
461 		blk++;
462 		boff = 0;
463 		wsz = Blksize;
464 	}
465 	return res;
466 }
467 
468 static long
469 mread(Chan *c, void *a, long n, vlong off)
470 {
471 	int	i;
472 	Fsdev	*mp;
473 	Chan	*mc;
474 	long	l;
475 	long	res;
476 
477 	if (c->qid.type & QTDIR)
478 		return devdirread(c, a, n, 0, 0, mgen);
479 	if (c->qid.path == Qctl)
480 		return readstr((long)off, a, n, _confstr + strlen(Cfgstr));
481 	i = c->qid.path - Qfirst;
482 	mp = path2dev(i, 1);
483 
484 	if (off >= mp->size)
485 		return 0;
486 	if (off + n > mp->size)
487 		n = mp->size - off;
488 	if (n == 0)
489 		return 0;
490 
491 	res = -1;
492 	switch(mp->type){
493 	case Fmirror:
494 		for (i = 0; i < mp->ndevs; i++){
495 			mc = mp->idev[i];
496 			if (waserror()){
497 				// if a read fails we let the user know and try
498 				// another device.
499 				print("#k: mread: (%llx %d): %s\n",
500 					c->qid.path, i, up->env->errstr);
501 				continue;
502 			}
503 			l = devtab[mc->type]->read(mc, a, n, off);
504 			poperror();
505 			if (l >=0){
506 				res = l;
507 				break;
508 			}
509 		}
510 		if (i == mp->ndevs)
511 			error(Eio);
512 		break;
513 	case Fcat:
514 		res = catio(mp, 1, a, n, off);
515 		break;
516 	case Finter:
517 		res = interio(mp, 1, a, n, off);
518 		break;
519 	case Fpart:
520 		off += mp->start;
521 		mc = mp->idev[0];
522 		res = devtab[mc->type]->read(mc, a, n, off);
523 		break;
524 	}
525 	return res;
526 }
527 
528 static long
529 mwrite(Chan *c, void *a, long n, vlong off)
530 {
531 	Fsdev	*mp;
532 	long	l, res;
533 	int	i;
534 	Chan	*mc;
535 
536 	if (c->qid.type & QTDIR)
537 		error(Eperm);
538 	if (c->qid.path == Qctl){
539 		mconfig(a, n);
540 		return n;
541 	}
542 	mp = path2dev(c->qid.path - Qfirst, 1);
543 
544 	if (off >= mp->size)
545 		return 0;
546 	if (off + n > mp->size)
547 		n = mp->size - off;
548 	if (n == 0)
549 		return 0;
550 	res = n;
551 	switch(mp->type){
552 	case Fmirror:
553 		for (i = mp->ndevs-1; i >=0; i--){
554 			mc = mp->idev[i];
555 			l = devtab[mc->type]->write(mc, a, n, off);
556 			if (l < res)
557 				res = l;
558 		}
559 		break;
560 	case Fcat:
561 		res = catio(mp, 0, a, n, off);
562 		break;
563 	case Finter:
564 		res = interio(mp, 0, a, n, off);
565 		break;
566 	case Fpart:
567 		mc = mp->idev[0];
568 		off += mp->start;
569 		l = devtab[mc->type]->write(mc, a, n, off);
570 		if (l < res)
571 			res = l;
572 		break;
573 	}
574 	return res;
575 }
576 
577 Dev dsdevtab = {
578 	'k',
579 	"ds",
580 
581 	devinit,
582 	mattach,
583 	mwalk,
584 	mstat,
585 	mopen,
586 	devcreate,
587 	mclose,
588 	mread,
589 	devbread,
590 	mwrite,
591 	devbwrite,
592 	devremove,
593 	devwstat,
594 };
595