rf_configure.c revision 1.16 1 /* $NetBSD: rf_configure.c,v 1.16 2003/05/17 01:05:03 itojun 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 (at) 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
50 #include <stdio.h>
51 #include <stdlib.h>
52 #include <errno.h>
53 #include <strings.h>
54 #include <sys/types.h>
55 #include <sys/stat.h>
56
57 #include <dev/raidframe/raidframevar.h>
58 #include <dev/raidframe/raidframeio.h>
59 #include "rf_configure.h"
60
61 RF_LayoutSW_t *rf_GetLayout(RF_ParityConfig_t parityConfig);
62 char *rf_find_non_white(char *p);
63 char *rf_find_white(char *p);
64 #define RF_MIN(a,b) (((a) < (b)) ? (a) : (b))
65 #define RF_ERRORMSG(s) printf((s))
66 #define RF_ERRORMSG1(s,a) printf((s),(a))
67 #define RF_ERRORMSG2(s,a,b) printf((s),(a),(b))
68
69 /*
70 * XXX we include this here so we don't need to drag rf_debugMem.c into
71 * the picture... This is userland, afterall...
72 */
73
74 /*
75 * XXX sucky hack to override the defn. of RF_Malloc as given in
76 * rf_debugMem.c... but I *really* don't want (nor need) to link with
77 * that file here in userland.. GO
78 */
79
80 #undef RF_Malloc
81 #define RF_Malloc(_p_, _size_, _cast_) \
82 { \
83 _p_ = _cast_ malloc((u_long)_size_); \
84 bzero((char *)_p_, _size_); \
85 }
86
87 int distSpareYes = 1;
88 int distSpareNo = 0;
89
90 /* The mapsw[] table below contains all the various RAID types that might
91 be supported by the kernel. The actual supported types are found
92 in sys/dev/raidframe/rf_layout.c. */
93
94 static RF_LayoutSW_t mapsw[] = {
95 /* parity declustering */
96 {'T', "Parity declustering",
97 rf_MakeLayoutSpecificDeclustered, &distSpareNo},
98 /* parity declustering with distributed sparing */
99 {'D', "Distributed sparing parity declustering",
100 rf_MakeLayoutSpecificDeclustered, &distSpareYes},
101 /* declustered P+Q */
102 {'Q', "Declustered P+Q",
103 rf_MakeLayoutSpecificDeclustered, &distSpareNo},
104 /* RAID 5 with rotated sparing */
105 {'R', "RAID Level 5 rotated sparing", rf_MakeLayoutSpecificNULL, NULL},
106 /* Chained Declustering */
107 {'C', "Chained Declustering", rf_MakeLayoutSpecificNULL, NULL},
108 /* Interleaved Declustering */
109 {'I', "Interleaved Declustering", rf_MakeLayoutSpecificNULL, NULL},
110 /* RAID level 0 */
111 {'0', "RAID Level 0", rf_MakeLayoutSpecificNULL, NULL},
112 /* RAID level 1 */
113 {'1', "RAID Level 1", rf_MakeLayoutSpecificNULL, NULL},
114 /* RAID level 4 */
115 {'4', "RAID Level 4", rf_MakeLayoutSpecificNULL, NULL},
116 /* RAID level 5 */
117 {'5', "RAID Level 5", rf_MakeLayoutSpecificNULL, NULL},
118 /* Evenodd */
119 {'E', "EvenOdd", rf_MakeLayoutSpecificNULL, NULL},
120 /* Declustered Evenodd */
121 {'e', "Declustered EvenOdd",
122 rf_MakeLayoutSpecificDeclustered, &distSpareNo},
123 /* parity logging */
124 {'L', "Parity logging", rf_MakeLayoutSpecificNULL, NULL},
125 /* end-of-list marker */
126 {'\0', NULL, NULL, NULL}
127 };
128 RF_LayoutSW_t *
129 rf_GetLayout(RF_ParityConfig_t parityConfig)
130 {
131 RF_LayoutSW_t *p;
132
133 /* look up the specific layout */
134 for (p = &mapsw[0]; p->parityConfig; p++)
135 if (p->parityConfig == parityConfig)
136 break;
137 if (!p->parityConfig)
138 return (NULL);
139 return (p);
140 }
141
142 static int rf_search_file_for_start_of(const char *string, char *buf,
143 int len, FILE * fp);
144 static int rf_get_next_nonblank_line(char *buf, int len, FILE * fp,
145 const char *errmsg);
146
147 /*
148 * called from user level to read the configuration file and create
149 * a configuration control structure. This is used in the user-level
150 * version of the driver, and in the user-level program that configures
151 * the system via ioctl.
152 */
153 int
154 rf_MakeConfig(configname, cfgPtr)
155 char *configname;
156 RF_Config_t *cfgPtr;
157 {
158 int numscanned, val, r, c, retcode, aa, bb, cc;
159 char buf[256], buf1[256], *cp;
160 RF_LayoutSW_t *lp;
161 FILE *fp;
162
163 bzero((char *) cfgPtr, sizeof(RF_Config_t));
164
165 fp = fopen(configname, "r");
166 if (!fp) {
167 printf("Can't open config file %s\n", configname);
168 return (-1);
169 }
170 rewind(fp);
171 if (rf_search_file_for_start_of("array", buf, 256, fp)) {
172 printf("Unable to find start of \"array\" params in config file %s\n", configname);
173 retcode = -1;
174 goto out;
175 }
176 rf_get_next_nonblank_line(buf, 256, fp, "Config file error (\"array\" section): unable to get numRow and numCol\n");
177
178 /*
179 * wackiness with aa, bb, cc to get around size problems on
180 * different platforms
181 */
182 numscanned = sscanf(buf, "%d %d %d", &aa, &bb, &cc);
183 if (numscanned != 3) {
184 printf("Config file error (\"array\" section): unable to get numRow, numCol, numSpare\n");
185 retcode = -1;
186 goto out;
187 }
188 cfgPtr->numRow = (RF_RowCol_t) aa;
189 cfgPtr->numCol = (RF_RowCol_t) bb;
190 cfgPtr->numSpare = (RF_RowCol_t) cc;
191
192 /* debug section is optional */
193 for (c = 0; c < RF_MAXDBGV; c++)
194 cfgPtr->debugVars[c][0] = '\0';
195 rewind(fp);
196 if (!rf_search_file_for_start_of("debug", buf, 256, fp)) {
197 for (c = 0; c < RF_MAXDBGV; c++) {
198 if (rf_get_next_nonblank_line(buf, 256, fp, NULL))
199 break;
200 cp = rf_find_non_white(buf);
201 if (!strncmp(cp, "START", strlen("START")))
202 break;
203 (void) strcpy(&cfgPtr->debugVars[c][0], cp);
204 }
205 }
206 rewind(fp);
207 strcpy(cfgPtr->diskQueueType, "fifo");
208 cfgPtr->maxOutstandingDiskReqs = 1;
209 /* scan the file for the block related to disk queues */
210 if (rf_search_file_for_start_of("queue", buf, 256, fp)) {
211 RF_ERRORMSG2("[No disk queue discipline specified in config file %s. Using %s.]\n", configname, cfgPtr->diskQueueType);
212 } else {
213 if (rf_get_next_nonblank_line(buf, 256, fp, NULL)) {
214 RF_ERRORMSG2("[No disk queue discipline specified in config file %s. Using %s.]\n", configname, cfgPtr->diskQueueType);
215 }
216 }
217
218 /* the queue specifier line contains two entries: 1st char of first
219 * word specifies queue to be used 2nd word specifies max num reqs
220 * that can be outstanding on the disk itself (typically 1) */
221 if (sscanf(buf, "%255s %d", buf1, &val) != 2) {
222 RF_ERRORMSG1("Can't determine queue type and/or max outstanding reqs from line: %s", buf);
223 RF_ERRORMSG2("Using %s-%d\n", cfgPtr->diskQueueType, cfgPtr->maxOutstandingDiskReqs);
224 } else {
225 char *ch;
226 bcopy(buf1, cfgPtr->diskQueueType,
227 RF_MIN(sizeof(cfgPtr->diskQueueType), strlen(buf1) + 1));
228 for (ch = buf1; *ch; ch++) {
229 if (*ch == ' ') {
230 *ch = '\0';
231 break;
232 }
233 }
234 cfgPtr->maxOutstandingDiskReqs = val;
235 }
236
237 rewind(fp);
238
239 if (rf_search_file_for_start_of("disks", buf, 256, fp)) {
240 RF_ERRORMSG1("Can't find \"disks\" section in config file %s\n", configname);
241 retcode = -1;
242 goto out;
243 }
244 for (r = 0; r < cfgPtr->numRow; r++) {
245 for (c = 0; c < cfgPtr->numCol; c++) {
246 if (rf_get_next_nonblank_line(
247 &cfgPtr->devnames[r][c][0], 50, fp, NULL)) {
248 RF_ERRORMSG2("Config file error: unable to get device file for disk at row %d col %d\n", r, c);
249 retcode = -1;
250 goto out;
251 }
252 }
253 }
254
255 /* "spare" section is optional */
256 rewind(fp);
257 if (rf_search_file_for_start_of("spare", buf, 256, fp))
258 cfgPtr->numSpare = 0;
259 for (c = 0; c < cfgPtr->numSpare; c++) {
260 if (rf_get_next_nonblank_line(&cfgPtr->spare_names[c][0],
261 256, fp, NULL)) {
262 RF_ERRORMSG1("Config file error: unable to get device file for spare disk %d\n", c);
263 retcode = -1;
264 goto out;
265 }
266 }
267
268 /* scan the file for the block related to layout */
269 rewind(fp);
270 if (rf_search_file_for_start_of("layout", buf, 256, fp)) {
271 RF_ERRORMSG1("Can't find \"layout\" section in configuration file %s\n", configname);
272 retcode = -1;
273 goto out;
274 }
275 if (rf_get_next_nonblank_line(buf, 256, fp, NULL)) {
276 RF_ERRORMSG("Config file error (\"layout\" section): unable to find common layout param line\n");
277 retcode = -1;
278 goto out;
279 }
280 c = sscanf(buf, "%d %d %d %c", &aa, &bb, &cc, &cfgPtr->parityConfig);
281 cfgPtr->sectPerSU = (RF_SectorNum_t) aa;
282 cfgPtr->SUsPerPU = (RF_StripeNum_t) bb;
283 cfgPtr->SUsPerRU = (RF_StripeNum_t) cc;
284 if (c != 4) {
285 RF_ERRORMSG("Unable to scan common layout line\n");
286 retcode = -1;
287 goto out;
288 }
289 lp = rf_GetLayout(cfgPtr->parityConfig);
290 if (lp == NULL) {
291 RF_ERRORMSG1("Unknown parity config '%c'\n",
292 cfgPtr->parityConfig);
293 retcode = -1;
294 goto out;
295 }
296
297 retcode = lp->MakeLayoutSpecific(fp, cfgPtr, lp->makeLayoutSpecificArg);
298 out:
299 fclose(fp);
300 if (retcode < 0)
301 retcode = errno = EINVAL;
302 else
303 errno = retcode;
304 return (retcode);
305 }
306
307
308 /* used in architectures such as RAID0 where there is no layout-specific
309 * information to be passed into the configuration code.
310 */
311 int
312 rf_MakeLayoutSpecificNULL(fp, cfgPtr, ignored)
313 FILE *fp;
314 RF_Config_t *cfgPtr;
315 void *ignored;
316 {
317 cfgPtr->layoutSpecificSize = 0;
318 cfgPtr->layoutSpecific = NULL;
319 return (0);
320 }
321
322 int
323 rf_MakeLayoutSpecificDeclustered(configfp, cfgPtr, arg)
324 FILE *configfp;
325 RF_Config_t *cfgPtr;
326 void *arg;
327 {
328 int b, v, k, r, lambda, norotate, i, val, distSpare;
329 char *cfgBuf, *bdfile, *p, *smname;
330 char buf[256], smbuf[256];
331 FILE *fp;
332
333 distSpare = *((int *) arg);
334
335 /* get the block design file name */
336 if (rf_get_next_nonblank_line(buf, 256, configfp,
337 "Can't find block design file name in config file\n"))
338 return (EINVAL);
339 bdfile = rf_find_non_white(buf);
340 if (bdfile[strlen(bdfile) - 1] == '\n') {
341 /* strip newline char */
342 bdfile[strlen(bdfile) - 1] = '\0';
343 }
344 /* open bd file, check validity of configuration */
345 if ((fp = fopen(bdfile, "r")) == NULL) {
346 RF_ERRORMSG1("RAID: config error: Can't open layout table file %s\n", bdfile);
347 return (EINVAL);
348 }
349 if (fgets(buf, 256, fp) == NULL) {
350 RF_ERRORMSG1("RAID: config error: Can't read layout from layout table file %s\n", bdfile);
351 return (EINVAL);
352 }
353 i = sscanf(buf, "%u %u %u %u %u %u", &b, &v, &k, &r, &lambda, &norotate);
354 if (i == 5)
355 norotate = 0; /* no-rotate flag is optional */
356 else if (i != 6) {
357 RF_ERRORMSG("Unable to parse header line in block design file\n");
358 return (EINVAL);
359 }
360 /* set the sparemap directory. In the in-kernel version, there's a
361 * daemon that's responsible for finding the sparemaps */
362 if (distSpare) {
363 if (rf_get_next_nonblank_line(smbuf, 256, configfp,
364 "Can't find sparemap file name in config file\n"))
365 return (EINVAL);
366 smname = rf_find_non_white(smbuf);
367 if (smname[strlen(smname) - 1] == '\n') {
368 /* strip newline char */
369 smname[strlen(smname) - 1] = '\0';
370 }
371 } else {
372 smbuf[0] = '\0';
373 smname = smbuf;
374 }
375
376 /* allocate a buffer to hold the configuration info */
377 cfgPtr->layoutSpecificSize = RF_SPAREMAP_NAME_LEN +
378 6 * sizeof(int) + b * k;
379 /* can't use RF_Malloc here b/c debugMem module not yet init'd */
380 cfgBuf = (char *) malloc(cfgPtr->layoutSpecificSize);
381 cfgPtr->layoutSpecific = (void *) cfgBuf;
382 p = cfgBuf;
383
384 /* install name of sparemap file */
385 for (i = 0; smname[i]; i++)
386 *p++ = smname[i];
387 /* pad with zeros */
388 while (i < RF_SPAREMAP_NAME_LEN) {
389 *p++ = '\0';
390 i++;
391 }
392
393 /*
394 * fill in the buffer with the block design parameters
395 * and then the block design itself
396 */
397 *((int *) p) = b;
398 p += sizeof(int);
399 *((int *) p) = v;
400 p += sizeof(int);
401 *((int *) p) = k;
402 p += sizeof(int);
403 *((int *) p) = r;
404 p += sizeof(int);
405 *((int *) p) = lambda;
406 p += sizeof(int);
407 *((int *) p) = norotate;
408 p += sizeof(int);
409
410 while (fscanf(fp, "%d", &val) == 1)
411 *p++ = (char) val;
412 fclose(fp);
413 if (p - cfgBuf != cfgPtr->layoutSpecificSize) {
414 RF_ERRORMSG2("Size mismatch creating layout specific data: is %d sb %d bytes\n", (int) (p - cfgBuf), (int) (6 * sizeof(int) + b * k));
415 return (EINVAL);
416 }
417 return (0);
418 }
419
420 /****************************************************************************
421 *
422 * utilities
423 *
424 ***************************************************************************/
425
426 /* finds a non-white character in the line */
427 char *
428 rf_find_non_white(char *p)
429 {
430 for (; *p != '\0' && (*p == ' ' || *p == '\t'); p++);
431 return (p);
432 }
433
434 /* finds a white character in the line */
435 char *
436 rf_find_white(char *p)
437 {
438 for (; *p != '\0' && (*p != ' ' && *p != '\t'); p++);
439 return (p);
440 }
441
442 /*
443 * searches a file for a line that says "START string", where string is
444 * specified as a parameter
445 */
446 static int
447 rf_search_file_for_start_of(string, buf, len, fp)
448 const char *string;
449 char *buf;
450 int len;
451 FILE *fp;
452 {
453 char *p;
454
455 while (1) {
456 if (fgets(buf, len, fp) == NULL)
457 return (-1);
458 p = rf_find_non_white(buf);
459 if (!strncmp(p, "START", strlen("START"))) {
460 p = rf_find_white(p);
461 p = rf_find_non_white(p);
462 if (!strncmp(p, string, strlen(string)))
463 return (0);
464 }
465 }
466 }
467
468 /* reads from file fp into buf until it finds an interesting line */
469 int
470 rf_get_next_nonblank_line(buf, len, fp, errmsg)
471 char *buf;
472 int len;
473 FILE *fp;
474 const char *errmsg;
475 {
476 char *p;
477
478 while (fgets(buf, 256, fp) != NULL) {
479 p = rf_find_non_white(buf);
480 if (*p == '\n' || *p == '\0' || *p == '#')
481 continue;
482 return (0);
483 }
484 if (errmsg)
485 RF_ERRORMSG1("%s", errmsg);
486 return (1);
487 }
488
489 /*
490 * Allocates an array for the spare table, and initializes it from a file.
491 * In the user-level version, this is called when recon is initiated.
492 * When/if I move recon into the kernel, there'll be a daemon that does
493 * an ioctl into raidframe which will block until a spare table is needed.
494 * When it returns, it will read a spare table from the file system,
495 * pass it into the kernel via a different ioctl, and then block again
496 * on the original ioctl.
497 *
498 * This is specific to the declustered layout, but doesn't belong in
499 * rf_decluster.c because it uses stuff that can't be compiled into
500 * the kernel, and it needs to be compiled into the user-level sparemap daemon.
501 *
502 */
503 void *
504 rf_ReadSpareTable(req, fname)
505 RF_SparetWait_t *req;
506 char *fname;
507 {
508 int i, j, numFound, linecount, tableNum, tupleNum,
509 spareDisk, spareBlkOffset;
510 char buf[1024], targString[100], errString[100];
511 RF_SpareTableEntry_t **table;
512 FILE *fp;
513
514 /* allocate and initialize the table */
515 RF_Malloc(table,
516 req->TablesPerSpareRegion * sizeof(RF_SpareTableEntry_t *),
517 (RF_SpareTableEntry_t **));
518 for (i = 0; i < req->TablesPerSpareRegion; i++) {
519 RF_Malloc(table[i],
520 req->BlocksPerTable * sizeof(RF_SpareTableEntry_t),
521 (RF_SpareTableEntry_t *));
522 for (j = 0; j < req->BlocksPerTable; j++)
523 table[i][j].spareDisk =
524 table[i][j].spareBlockOffsetInSUs = -1;
525 }
526
527 /* 2. open sparemap file, sanity check */
528 if ((fp = fopen(fname, "r")) == NULL) {
529 fprintf(stderr,
530 "rf_ReadSpareTable: Can't open sparemap file %s\n", fname);
531 return (NULL);
532 }
533 if (rf_get_next_nonblank_line(buf, 1024, fp,
534 "Invalid sparemap file: can't find header line\n"))
535 return (NULL);
536 if (buf[strlen(buf) - 1] == '\n')
537 buf[strlen(buf) - 1] = '\0';
538
539 sprintf(targString, "fdisk %d\n", req->fcol);
540 sprintf(errString,
541 "Invalid sparemap file: can't find \"fdisk %d\" line\n",
542 req->fcol);
543 while (1) {
544 rf_get_next_nonblank_line(buf, 1024, fp, errString);
545 if (!strncmp(buf, targString, strlen(targString)))
546 break;
547 }
548
549 /* no more blank lines or comments allowed now */
550 linecount = req->TablesPerSpareRegion * req->TableDepthInPUs;
551 for (i = 0; i < linecount; i++) {
552 numFound = fscanf(fp, " %d %d %d %d", &tableNum, &tupleNum,
553 &spareDisk, &spareBlkOffset);
554 if (numFound != 4) {
555 fprintf(stderr, "Sparemap file prematurely exhausted after %d of %d lines\n", i, linecount);
556 return (NULL);
557 }
558
559 table[tableNum][tupleNum].spareDisk = spareDisk;
560 table[tableNum][tupleNum].spareBlockOffsetInSUs =
561 spareBlkOffset * req->SUsPerPU;
562 }
563
564 fclose(fp);
565 return ((void *) table);
566 }
567