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