xref: /netbsd-src/sbin/raidctl/rf_configure.c (revision 87d689fb734c654d2486f87f7be32f1b53ecdbec)
1 /*	$NetBSD: rf_configure.c,v 1.32 2017/11/22 00:31:31 kre Exp $ */
2 
3 /*
4  * Copyright (c) 1995 Carnegie-Mellon University.
5  * All rights reserved.
6  *
7  * Author: Mark Holland
8  *
9  * Permission to use, copy, modify and distribute this software and
10  * its documentation is hereby granted, provided that both the copyright
11  * notice and this permission notice appear in all copies of the
12  * software, derivative works or modified versions, and any portions
13  * thereof, and that both notices appear in supporting documentation.
14  *
15  * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
16  * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND
17  * FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
18  *
19  * Carnegie Mellon requests users of this software to return to
20  *
21  *  Software Distribution Coordinator  or  Software.Distribution@CS.CMU.EDU
22  *  School of Computer Science
23  *  Carnegie Mellon University
24  *  Pittsburgh PA 15213-3890
25  *
26  * any improvements or extensions that they make and grant Carnegie the
27  * rights to redistribute these changes.
28  */
29 
30 /***************************************************************
31  *
32  * rf_configure.c -- code related to configuring the raidframe system
33  *
34  * configuration is complicated by the fact that we want the same
35  * driver to work both in the kernel and at user level.  In the
36  * kernel, we can't read the configuration file, so we configure
37  * by running a user-level program that reads the config file,
38  * creates a data structure describing the configuration and
39  * passes it into the kernel via an ioctl.  Since we want the config
40  * code to be common between the two versions of the driver, we
41  * configure using the same two-step process when running at
42  * user level.  Of course, at user level, the config structure is
43  * passed directly to the config routine, rather than via ioctl.
44  *
45  * This file is not compiled into the kernel, so we have no
46  * need for KERNEL ifdefs.
47  *
48  **************************************************************/
49 #include <sys/cdefs.h>
50 
51 #ifndef lint
52 __RCSID("$NetBSD: rf_configure.c,v 1.32 2017/11/22 00:31:31 kre Exp $");
53 #endif
54 
55 
56 #include <stdio.h>
57 #include <stdlib.h>
58 #include <errno.h>
59 #include <strings.h>
60 #include <err.h>
61 #include <util.h>
62 #include <sys/types.h>
63 #include <sys/stat.h>
64 
65 #include <dev/raidframe/raidframevar.h>
66 #include <dev/raidframe/raidframeio.h>
67 #include "rf_configure.h"
68 
69 static char   *rf_find_non_white(char *, int);
70 static char   *rf_find_white(char *);
71 static int rf_search_file_for_start_of(const char *, char *, int, FILE *);
72 static int rf_get_next_nonblank_line(char *, int, FILE *, const char *);
73 
74 #define RF_MIN(a,b) (((a) < (b)) ? (a) : (b))
75 
76 static int     distSpareYes = 1;
77 static int     distSpareNo = 0;
78 
79 /*
80  * The mapsw[] table below contains all the various RAID types that might
81  * be supported by the kernel.  The actual supported types are found
82  * in sys/dev/raidframe/rf_layout.c.
83  */
84 
85 static const RF_LayoutSW_t mapsw[] = {
86 	/* parity declustering */
87 	{'T', "Parity declustering",
88 	 rf_MakeLayoutSpecificDeclustered, &distSpareNo},
89 	/* parity declustering with distributed sparing */
90 	{'D', "Distributed sparing parity declustering",
91 	 rf_MakeLayoutSpecificDeclustered, &distSpareYes},
92 	/* declustered P+Q */
93 	{'Q', "Declustered P+Q",
94 	 rf_MakeLayoutSpecificDeclustered, &distSpareNo},
95 	/* RAID 5 with rotated sparing */
96 	{'R', "RAID Level 5 rotated sparing", rf_MakeLayoutSpecificNULL, NULL},
97 	/* Chained Declustering */
98 	{'C', "Chained Declustering", rf_MakeLayoutSpecificNULL, NULL},
99 	/* Interleaved Declustering */
100 	{'I', "Interleaved Declustering", rf_MakeLayoutSpecificNULL, NULL},
101 	/* RAID level 0 */
102 	{'0', "RAID Level 0", rf_MakeLayoutSpecificNULL, NULL},
103 	/* RAID level 1 */
104 	{'1', "RAID Level 1", rf_MakeLayoutSpecificNULL, NULL},
105 	/* RAID level 4 */
106 	{'4', "RAID Level 4", rf_MakeLayoutSpecificNULL, NULL},
107 	/* RAID level 5 */
108 	{'5', "RAID Level 5", rf_MakeLayoutSpecificNULL, NULL},
109 	/* Evenodd */
110 	{'E', "EvenOdd", rf_MakeLayoutSpecificNULL, NULL},
111 	/* Declustered Evenodd */
112 	{'e', "Declustered EvenOdd",
113 	 rf_MakeLayoutSpecificDeclustered, &distSpareNo},
114 	/* parity logging */
115 	{'L', "Parity logging", rf_MakeLayoutSpecificNULL, NULL},
116 	/* end-of-list marker */
117 	{'\0', NULL, NULL, NULL}
118 };
119 
120 static const RF_LayoutSW_t *
121 rf_GetLayout(RF_ParityConfig_t parityConfig)
122 {
123 	const RF_LayoutSW_t *p;
124 
125 	/* look up the specific layout */
126 	for (p = &mapsw[0]; p->parityConfig; p++)
127 		if (p->parityConfig == parityConfig)
128 			break;
129 	if (!p->parityConfig)
130 		return NULL;
131 	return p;
132 }
133 
134 /*
135  * called from user level to read the configuration file and create
136  * a configuration control structure.  This is used in the user-level
137  * version of the driver, and in the user-level program that configures
138  * the system via ioctl.
139  */
140 int
141 rf_MakeConfig(char *configname, RF_Config_t *cfgPtr)
142 {
143 	int numscanned, val, r, c, retcode, aa, bb, cc;
144 	char buf[BUFSIZ], buf1[BUFSIZ], *cp;
145 	const RF_LayoutSW_t *lp;
146 	FILE *fp;
147 
148 	memset(cfgPtr, 0, sizeof(*cfgPtr));
149 
150 	fp = fopen(configname, "r");
151 	if (!fp) {
152 		warnx("Can't open config file %s", configname);
153 		return -1;
154 	}
155 	rewind(fp);
156 	if (rf_search_file_for_start_of("array", buf, sizeof(buf), fp)) {
157 		warnx("Unable to find start of \"array\" params in config "
158 		    "file %s", configname);
159 		retcode = -1;
160 		goto out;
161 	}
162 	rf_get_next_nonblank_line(buf, sizeof(buf), fp,
163 	    "Config file error (\"array\" section):  unable to get numRow "
164 	    "and numCol");
165 
166 	/*
167 	 * wackiness with aa, bb, cc to get around size problems on
168 	 * different platforms
169 	 */
170 	numscanned = sscanf(buf, "%d %d %d", &aa, &bb, &cc);
171 	if (numscanned != 3) {
172 		warnx("Config file error (\"array\" section): unable to get "
173 		    "numRow, numCol, numSpare");
174 		retcode = -1;
175 		goto out;
176 	}
177 	cfgPtr->numRow = (RF_RowCol_t) aa;
178 	cfgPtr->numCol = (RF_RowCol_t) bb;
179 	cfgPtr->numSpare = (RF_RowCol_t) cc;
180 
181 	/* debug section is optional */
182 	for (c = 0; c < RF_MAXDBGV; c++)
183 		cfgPtr->debugVars[c][0] = '\0';
184 	rewind(fp);
185 	if (!rf_search_file_for_start_of("debug", buf, sizeof(buf), fp)) {
186 		for (c = 0; c < RF_MAXDBGV; c++) {
187 			if (rf_get_next_nonblank_line(buf, sizeof(buf), fp,
188 			    NULL))
189 				break;
190 			cp = rf_find_non_white(buf, 0);
191 			if (!strncmp(cp, "START", sizeof("START") - 1))
192 				break;
193 			(void) strlcpy(cfgPtr->debugVars[c], cp,
194 			    sizeof(cfgPtr->debugVars[c]));
195 		}
196 	}
197 	rewind(fp);
198 	strlcpy(cfgPtr->diskQueueType, "fifo", sizeof(cfgPtr->diskQueueType));
199 	cfgPtr->maxOutstandingDiskReqs = 1;
200 
201 	/* scan the file for the block related to disk queues */
202 	if (rf_search_file_for_start_of("queue", buf, sizeof(buf), fp) ||
203 	    rf_get_next_nonblank_line(buf, sizeof(buf), fp, NULL)) {
204 		warnx("[No disk queue discipline specified in config file %s. "
205 		    "Using %s.]", configname, cfgPtr->diskQueueType);
206 	}
207 
208 	/*
209 	 * the queue specifier line contains two entries: 1st char of first
210 	 * word specifies queue to be used 2nd word specifies max num reqs
211 	 * that can be outstanding on the disk itself (typically 1)
212 	 */
213 	if (sscanf(buf, "%s %d", buf1, &val) != 2) {
214 		warnx("Can't determine queue type and/or max outstanding "
215 		    "reqs from line: %*s", (int)(sizeof(buf) - 1), buf);
216 		warnx("Using %s-%d", cfgPtr->diskQueueType,
217 		    cfgPtr->maxOutstandingDiskReqs);
218 	} else {
219 		char *ch;
220 		memcpy(cfgPtr->diskQueueType, buf1,
221 		    RF_MIN(sizeof(cfgPtr->diskQueueType), strlen(buf1) + 1));
222 		for (ch = buf1; *ch; ch++) {
223 			if (*ch == ' ') {
224 				*ch = '\0';
225 				break;
226 			}
227 		}
228 		cfgPtr->maxOutstandingDiskReqs = val;
229 	}
230 
231 	rewind(fp);
232 
233 	if (rf_search_file_for_start_of("disks", buf, sizeof(buf), fp)) {
234 		warnx("Can't find \"disks\" section in config file %s",
235 		    configname);
236 		retcode = -1;
237 		goto out;
238 	}
239 	for (r = 0; r < cfgPtr->numRow; r++) {
240 		for (c = 0; c < cfgPtr->numCol; c++) {
241 			char b1[MAXPATHLEN];
242 			const char *b;
243 
244 			if (rf_get_next_nonblank_line(
245 			    buf, sizeof(buf), fp, NULL)) {
246 				warnx("Config file error: unable to get device "
247 				    "file for disk at row %d col %d", r, c);
248 				retcode = -1;
249 				goto out;
250 			}
251 
252 			b = getfsspecname(b1, sizeof(b1), buf);
253 			if (b == NULL) {
254 				warnx("Config file error: warning: unable to "
255 				    "get device file for disk at row %d col "
256 				    "%d: %s", r, c, b1);
257 				b = buf;
258 			}
259 
260 			strlcpy(cfgPtr->devnames[r][c], b,
261 			    sizeof(cfgPtr->devnames[r][c]));
262 		}
263 	}
264 
265 	/* "spare" section is optional */
266 	rewind(fp);
267 	if (rf_search_file_for_start_of("spare", buf, sizeof(buf), fp))
268 		cfgPtr->numSpare = 0;
269 	for (c = 0; c < cfgPtr->numSpare; c++) {
270 		char b1[MAXPATHLEN];
271 		const char *b;
272 
273 		if (rf_get_next_nonblank_line(buf, sizeof(buf), fp, NULL)) {
274 			warnx("Config file error: unable to get device file "
275 			    "for spare disk %d", c);
276 			retcode = -1;
277 			goto out;
278 		}
279 
280 		b = getfsspecname(b1, sizeof(b1), buf);
281 		if (b == NULL) {
282 			warnx("Config file error: warning: unable to get "
283 			    "device file for spare disk %d: %s", c, b);
284 			b = buf;
285 		}
286 
287 	        strlcpy(cfgPtr->spare_names[r], b,
288 		    sizeof(cfgPtr->spare_names[r]));
289 	}
290 
291 	/* scan the file for the block related to layout */
292 	rewind(fp);
293 	if (rf_search_file_for_start_of("layout", buf, sizeof(buf), fp)) {
294 		warnx("Can't find \"layout\" section in configuration file %s",
295 		    configname);
296 		retcode = -1;
297 		goto out;
298 	}
299 	if (rf_get_next_nonblank_line(buf, sizeof(buf), fp, NULL)) {
300 		warnx("Config file error (\"layout\" section): unable to find "
301 		    "common layout param line");
302 		retcode = -1;
303 		goto out;
304 	}
305 	c = sscanf(buf, "%d %d %d %c", &aa, &bb, &cc, &cfgPtr->parityConfig);
306 	cfgPtr->sectPerSU = (RF_SectorNum_t) aa;
307 	cfgPtr->SUsPerPU = (RF_StripeNum_t) bb;
308 	cfgPtr->SUsPerRU = (RF_StripeNum_t) cc;
309 	if (c != 4) {
310 		warnx("Unable to scan common layout line");
311 		retcode = -1;
312 		goto out;
313 	}
314 	lp = rf_GetLayout(cfgPtr->parityConfig);
315 	if (lp == NULL) {
316 		warnx("Unknown parity config '%c'",
317 		    cfgPtr->parityConfig);
318 		retcode = -1;
319 		goto out;
320 	}
321 
322 	retcode = lp->MakeLayoutSpecific(fp, cfgPtr,
323 	    lp->makeLayoutSpecificArg);
324 out:
325 	fclose(fp);
326 	if (retcode < 0)
327 		retcode = errno = EINVAL;
328 	else
329 		errno = retcode;
330 	return retcode;
331 }
332 
333 
334 /*
335  * used in architectures such as RAID0 where there is no layout-specific
336  * information to be passed into the configuration code.
337  */
338 int
339 rf_MakeLayoutSpecificNULL(FILE *fp, RF_Config_t *cfgPtr, void *ignored)
340 {
341 	cfgPtr->layoutSpecificSize = 0;
342 	cfgPtr->layoutSpecific = NULL;
343 	return 0;
344 }
345 
346 int
347 rf_MakeLayoutSpecificDeclustered(FILE *configfp, RF_Config_t *cfgPtr, void *arg)
348 {
349 	int b, v, k, r, lambda, norotate, i, val, distSpare;
350 	char *cfgBuf, *bdfile, *p, *smname;
351 	char buf[BUFSIZ], smbuf[BUFSIZ];
352 	FILE *fp;
353 
354 	distSpare = *((int *) arg);
355 
356 	/* get the block design file name */
357 	if (rf_get_next_nonblank_line(buf, sizeof(buf), configfp,
358 	    "Can't find block design file name in config file"))
359 		return EINVAL;
360 	bdfile = rf_find_non_white(buf, 1);
361 	/* open bd file, check validity of configuration */
362 	if ((fp = fopen(bdfile, "r")) == NULL) {
363 		warn("RAID: config error: Can't open layout table file %s",
364 		    bdfile);
365 		return EINVAL;
366 	}
367 	if (fgets(buf, sizeof(buf), fp) == NULL) {
368 		warnx("RAID: config error: Can't read layout from layout "
369 		    "table file %s", bdfile);
370 		fclose(fp);
371 		return EINVAL;
372 	}
373 	i = sscanf(buf, "%u %u %u %u %u %u",
374 	    &b, &v, &k, &r, &lambda, &norotate);
375 	if (i == 5)
376 		norotate = 0;	/* no-rotate flag is optional */
377 	else if (i != 6) {
378 		warnx("Unable to parse header line in block design file");
379 		fclose(fp);
380 		return EINVAL;
381 	}
382 	/*
383 	 * set the sparemap directory.  In the in-kernel version, there's a
384 	 * daemon that's responsible for finding the sparemaps
385 	 */
386 	if (distSpare) {
387 		if (rf_get_next_nonblank_line(smbuf, sizeof(smbuf), configfp,
388 		    "Can't find sparemap file name in config file")) {
389 			fclose(fp);
390 			return EINVAL;
391 		}
392 		smname = rf_find_non_white(smbuf, 1);
393 		if (strlen(smname) >= RF_SPAREMAP_NAME_LEN) {
394 			warnx("sparemap file name '%s' too long (max %d)",
395 			    smname, RF_SPAREMAP_NAME_LEN - 1);
396 			fclose(fp);
397 			return EINVAL;
398 		}
399 	} else {
400 		smbuf[0] = '\0';
401 		smname = smbuf;
402 	}
403 
404 	/* allocate a buffer to hold the configuration info */
405 	cfgPtr->layoutSpecificSize = RF_SPAREMAP_NAME_LEN +
406 	    6 * sizeof(int) + b * k;
407 
408 	cfgBuf = (char *) malloc(cfgPtr->layoutSpecificSize);
409 	if (cfgBuf == NULL) {
410 		fclose(fp);
411 		return ENOMEM;
412 	}
413 	cfgPtr->layoutSpecific = (void *) cfgBuf;
414 	p = cfgBuf;
415 
416 	/* install name of sparemap file */
417 	for (i = 0; smname[i]; i++)
418 		*p++ = smname[i];
419 	/* pad with zeros */
420 	while (i < RF_SPAREMAP_NAME_LEN) {
421 		*p++ = '\0';
422 		i++;
423 	}
424 	if ((i & (sizeof(int) - 1)) != 0) {
425 		/* panic, unaligned data; RF_SPAREMAP_NAME_LEN invalid */
426 		warnx("Program Bug: (RF_SPAREMAP_NAME_LEN(%d) %% %zd) != 0",
427 		    RF_SPAREMAP_NAME_LEN, sizeof(int));
428 		fclose(fp);
429 		return EINVAL;
430 	}
431 
432 	/*
433 	 * fill in the buffer with the block design parameters
434 	 * and then the block design itself
435 	 */
436 	*((int *) p) = b;
437 	p += sizeof(int);
438 	*((int *) p) = v;
439 	p += sizeof(int);
440 	*((int *) p) = k;
441 	p += sizeof(int);
442 	*((int *) p) = r;
443 	p += sizeof(int);
444 	*((int *) p) = lambda;
445 	p += sizeof(int);
446 	*((int *) p) = norotate;
447 	p += sizeof(int);
448 
449 	while (fscanf(fp, "%d", &val) == 1)
450 		*p++ = (char) val;
451 	fclose(fp);
452 	if ((unsigned int)(p - cfgBuf) != cfgPtr->layoutSpecificSize) {
453 		warnx("Size mismatch creating layout specific data: is %tu sb "
454 		    "%zu bytes", p - cfgBuf, 6 * sizeof(int) + b * k);
455 		return EINVAL;
456 	}
457 	return 0;
458 }
459 
460 /****************************************************************************
461  *
462  * utilities
463  *
464  ***************************************************************************/
465 
466 /* finds a non-white character in the line */
467 static char *
468 rf_find_non_white(char *p, int eatnl)
469 {
470 	while (*p == ' ' || *p == '\t')
471 		p++;
472 	if (*p == '\n' && eatnl)
473 		*p = '\0';
474 	return p;
475 }
476 
477 /* finds a white character in the line */
478 static char *
479 rf_find_white(char *p)
480 {
481 	while (*p != '\0' && *p != ' ' && *p != '\t')
482 		p++;
483 	return p;
484 }
485 
486 /*
487  * searches a file for a line that says "START string", where string is
488  * specified as a parameter
489  */
490 static int
491 rf_search_file_for_start_of(const char *string, char *buf, int len, FILE *fp)
492 {
493 	char *p;
494 
495 	while (1) {
496 		if (fgets(buf, len, fp) == NULL)
497 			return -1;
498 		p = rf_find_non_white(buf, 0);
499 		if (!strncmp(p, "START", strlen("START"))) {
500 			p = rf_find_white(p);
501 			p = rf_find_non_white(p, 0);
502 			if (!strncmp(p, string, strlen(string)))
503 				return 0;
504 		}
505 	}
506 }
507 
508 /* reads from file fp into buf until it finds an interesting line */
509 static int
510 rf_get_next_nonblank_line(char *buf, int len, FILE *fp, const char *errmsg)
511 {
512 	char *p;
513 	size_t l;
514 
515 	while (fgets(buf, len, fp) != NULL) {
516 		p = rf_find_non_white(buf, 0);
517 		if (*p == '\n' || *p == '\0' || *p == '#')
518 			continue;
519 		l = strlen(buf);
520 		while (l > 0 && (buf[--l] == ' ' || buf[l] == '\n'))
521 			buf[l] = '\0';
522 		return 0;
523 	}
524 	if (errmsg)
525 		warnx("%s", errmsg);
526 	return 1;
527 }
528 
529 /*
530  * Allocates an array for the spare table, and initializes it from a file.
531  * In the user-level version, this is called when recon is initiated.
532  * When/if I move recon into the kernel, there'll be a daemon that does
533  * an ioctl into raidframe which will block until a spare table is needed.
534  * When it returns, it will read a spare table from the file system,
535  * pass it into the kernel via a different ioctl, and then block again
536  * on the original ioctl.
537  *
538  * This is specific to the declustered layout, but doesn't belong in
539  * rf_decluster.c because it uses stuff that can't be compiled into
540  * the kernel, and it needs to be compiled into the user-level sparemap daemon.
541  */
542 void *
543 rf_ReadSpareTable(RF_SparetWait_t *req, char *fname)
544 {
545 	int i, j, numFound, linecount, tableNum, tupleNum,
546 	    spareDisk, spareBlkOffset;
547 	char buf[BUFSIZ], targString[BUFSIZ], errString[BUFSIZ];
548 	RF_SpareTableEntry_t **table;
549 	FILE *fp = NULL;
550 	size_t len;
551 
552 	/* allocate and initialize the table */
553 	table = calloc(req->TablesPerSpareRegion, sizeof(*table));
554 	if (table == NULL) {
555 		warn("%s: Unable to allocate table", __func__);
556 		return NULL;
557 	}
558 	for (i = 0; i < req->TablesPerSpareRegion; i++) {
559 		table[i] = calloc(req->BlocksPerTable, sizeof(**table));
560 		if (table[i] == NULL) {
561 			warn("%s: Unable to allocate table:%d", __func__, i);
562 			goto out;
563 		}
564 		for (j = 0; j < req->BlocksPerTable; j++)
565 			table[i][j].spareDisk =
566 			    table[i][j].spareBlockOffsetInSUs = -1;
567 	}
568 
569 	/* 2.  open sparemap file, sanity check */
570 	if ((fp = fopen(fname, "r")) == NULL) {
571 		warn("%s: Can't open sparemap file %s", __func__, fname);
572 		goto out;
573 	}
574 	if (rf_get_next_nonblank_line(buf, 1024, fp,
575 	    "Invalid sparemap file:  can't find header line"))
576 		goto out;
577 
578 	len = strlen(buf);
579 	if (len != 0 && buf[len - 1] == '\n')
580 		buf[len - 1] = '\0';
581 
582 	snprintf(targString, sizeof(targString), "fdisk %d\n", req->fcol);
583 	snprintf(errString, sizeof(errString),
584 	    "Invalid sparemap file: Can't find \"fdisk %d\" line", req->fcol);
585 	for (;;) {
586 		rf_get_next_nonblank_line(buf, sizeof(buf), fp, errString);
587 		if (!strncmp(buf, targString, strlen(targString)))
588 			break;
589 	}
590 
591 	/* no more blank lines or comments allowed now */
592 	linecount = req->TablesPerSpareRegion * req->TableDepthInPUs;
593 	for (i = 0; i < linecount; i++) {
594 		char linebuf[BUFSIZ];
595 
596 		if (fgets(linebuf, BUFSIZ, fp) == NULL) {
597 			warnx("Sparemap file prematurely exhausted after %d "
598 			    "of %d lines", i, linecount);
599 			goto out;
600 		}
601 		numFound = sscanf(linebuf, " %d %d %d %d", &tableNum, &tupleNum,
602 		    &spareDisk, &spareBlkOffset);
603 		if (numFound != 4) {
604 			warnx("Sparemap file format error - "
605 			    "line %d of %d lines",
606 			    i + 1, linecount);
607 			goto out;
608 		}
609 
610 		table[tableNum][tupleNum].spareDisk = spareDisk;
611 		table[tableNum][tupleNum].spareBlockOffsetInSUs =
612 		    spareBlkOffset * req->SUsPerPU;
613 	}
614 
615 	fclose(fp);
616 	return (void *) table;
617 out:
618 	if (fp)
619 		fclose(fp);
620 	for (i = 0; i < req->TablesPerSpareRegion; i++)
621 		free(table[i]);
622 	free(table);
623 	return NULL;
624 }
625