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