1 /* $NetBSD: rf_configure.c,v 1.23 2006/03/19 01:57:11 dan Exp $ */
4 * Copyright (c) 1995 Carnegie-Mellon University.
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.
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.
19 * Carnegie Mellon requests users of this software to return to
21 * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
22 * School of Computer Science
23 * Carnegie Mellon University
24 * Pittsburgh PA 15213-3890
26 * any improvements or extensions that they make and grant Carnegie the
27 * rights to redistribute these changes.
30 /***************************************************************
32 * rf_configure.c -- code related to configuring the raidframe system
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.
45 * This file is not compiled into the kernel, so we have no
46 * need for KERNEL ifdefs.
48 **************************************************************/
49 #include <sys/cdefs.h>
52 __RCSID("$NetBSD: rf_configure.c,v 1.23 2006/03/19 01:57:11 dan Exp $");
60 #include <sys/types.h>
63 #include <dev/raidframe/raidframevar.h>
64 #include <dev/raidframe/raidframeio.h>
65 #include "rf_configure.h"
67 RF_LayoutSW_t
*rf_GetLayout(RF_ParityConfig_t parityConfig
);
68 char *rf_find_non_white(char *p
);
69 char *rf_find_white(char *p
);
70 #define RF_MIN(a,b) (((a) < (b)) ? (a) : (b))
71 #define RF_ERRORMSG(s) printf((s))
72 #define RF_ERRORMSG1(s,a) printf((s),(a))
73 #define RF_ERRORMSG2(s,a,b) printf((s),(a),(b))
78 /* The mapsw[] table below contains all the various RAID types that might
79 be supported by the kernel. The actual supported types are found
80 in sys/dev/raidframe/rf_layout.c. */
82 static RF_LayoutSW_t mapsw
[] = {
83 /* parity declustering */
84 {'T', "Parity declustering",
85 rf_MakeLayoutSpecificDeclustered
, &distSpareNo
},
86 /* parity declustering with distributed sparing */
87 {'D', "Distributed sparing parity declustering",
88 rf_MakeLayoutSpecificDeclustered
, &distSpareYes
},
90 {'Q', "Declustered P+Q",
91 rf_MakeLayoutSpecificDeclustered
, &distSpareNo
},
92 /* RAID 5 with rotated sparing */
93 {'R', "RAID Level 5 rotated sparing", rf_MakeLayoutSpecificNULL
, NULL
},
94 /* Chained Declustering */
95 {'C', "Chained Declustering", rf_MakeLayoutSpecificNULL
, NULL
},
96 /* Interleaved Declustering */
97 {'I', "Interleaved Declustering", rf_MakeLayoutSpecificNULL
, NULL
},
99 {'0', "RAID Level 0", rf_MakeLayoutSpecificNULL
, NULL
},
101 {'1', "RAID Level 1", rf_MakeLayoutSpecificNULL
, NULL
},
103 {'4', "RAID Level 4", rf_MakeLayoutSpecificNULL
, NULL
},
105 {'5', "RAID Level 5", rf_MakeLayoutSpecificNULL
, NULL
},
107 {'E', "EvenOdd", rf_MakeLayoutSpecificNULL
, NULL
},
108 /* Declustered Evenodd */
109 {'e', "Declustered EvenOdd",
110 rf_MakeLayoutSpecificDeclustered
, &distSpareNo
},
112 {'L', "Parity logging", rf_MakeLayoutSpecificNULL
, NULL
},
113 /* end-of-list marker */
114 {'\0', NULL
, NULL
, NULL
}
117 rf_GetLayout(RF_ParityConfig_t parityConfig
)
121 /* look up the specific layout */
122 for (p
= &mapsw
[0]; p
->parityConfig
; p
++)
123 if (p
->parityConfig
== parityConfig
)
125 if (!p
->parityConfig
)
130 static int rf_search_file_for_start_of(const char *string
, char *buf
,
132 static int rf_get_next_nonblank_line(char *buf
, int len
, FILE * fp
,
136 * called from user level to read the configuration file and create
137 * a configuration control structure. This is used in the user-level
138 * version of the driver, and in the user-level program that configures
139 * the system via ioctl.
142 rf_MakeConfig(char *configname
, RF_Config_t
*cfgPtr
)
144 int numscanned
, val
, r
, c
, retcode
, aa
, bb
, cc
;
145 char buf
[256], buf1
[256], *cp
;
149 bzero((char *) cfgPtr
, sizeof(RF_Config_t
));
151 fp
= fopen(configname
, "r");
153 printf("Can't open config file %s\n", configname
);
157 if (rf_search_file_for_start_of("array", buf
, 256, fp
)) {
158 printf("Unable to find start of \"array\" params in config file %s\n", configname
);
162 rf_get_next_nonblank_line(buf
, 256, fp
, "Config file error (\"array\" section): unable to get numRow and numCol\n");
165 * wackiness with aa, bb, cc to get around size problems on
166 * different platforms
168 numscanned
= sscanf(buf
, "%d %d %d", &aa
, &bb
, &cc
);
169 if (numscanned
!= 3) {
170 printf("Config file error (\"array\" section): unable to get numRow, numCol, numSpare\n");
174 cfgPtr
->numRow
= (RF_RowCol_t
) aa
;
175 cfgPtr
->numCol
= (RF_RowCol_t
) bb
;
176 cfgPtr
->numSpare
= (RF_RowCol_t
) cc
;
178 /* debug section is optional */
179 for (c
= 0; c
< RF_MAXDBGV
; c
++)
180 cfgPtr
->debugVars
[c
][0] = '\0';
182 if (!rf_search_file_for_start_of("debug", buf
, 256, fp
)) {
183 for (c
= 0; c
< RF_MAXDBGV
; c
++) {
184 if (rf_get_next_nonblank_line(buf
, 256, fp
, NULL
))
186 cp
= rf_find_non_white(buf
);
187 if (!strncmp(cp
, "START", strlen("START")))
189 (void) strlcpy(&cfgPtr
->debugVars
[c
][0], cp
,
190 sizeof(cfgPtr
->debugVars
[c
]));
194 strlcpy(cfgPtr
->diskQueueType
, "fifo", sizeof(cfgPtr
->diskQueueType
));
195 cfgPtr
->maxOutstandingDiskReqs
= 1;
196 /* scan the file for the block related to disk queues */
197 if (rf_search_file_for_start_of("queue", buf
, 256, fp
)) {
198 RF_ERRORMSG2("[No disk queue discipline specified in config file %s. Using %s.]\n", configname
, cfgPtr
->diskQueueType
);
200 if (rf_get_next_nonblank_line(buf
, 256, fp
, NULL
)) {
201 RF_ERRORMSG2("[No disk queue discipline specified in config file %s. Using %s.]\n", configname
, cfgPtr
->diskQueueType
);
205 /* the queue specifier line contains two entries: 1st char of first
206 * word specifies queue to be used 2nd word specifies max num reqs
207 * that can be outstanding on the disk itself (typically 1) */
208 if (sscanf(buf
, "%255s %d", buf1
, &val
) != 2) {
209 RF_ERRORMSG1("Can't determine queue type and/or max outstanding reqs from line: %s", buf
);
210 RF_ERRORMSG2("Using %s-%d\n", cfgPtr
->diskQueueType
, cfgPtr
->maxOutstandingDiskReqs
);
213 bcopy(buf1
, cfgPtr
->diskQueueType
,
214 RF_MIN(sizeof(cfgPtr
->diskQueueType
), strlen(buf1
) + 1));
215 for (ch
= buf1
; *ch
; ch
++) {
221 cfgPtr
->maxOutstandingDiskReqs
= val
;
226 if (rf_search_file_for_start_of("disks", buf
, 256, fp
)) {
227 RF_ERRORMSG1("Can't find \"disks\" section in config file %s\n", configname
);
231 for (r
= 0; r
< cfgPtr
->numRow
; r
++) {
232 for (c
= 0; c
< cfgPtr
->numCol
; c
++) {
233 if (rf_get_next_nonblank_line(
234 &cfgPtr
->devnames
[r
][c
][0], 50, fp
, NULL
)) {
235 RF_ERRORMSG2("Config file error: unable to get device file for disk at row %d col %d\n", r
, c
);
242 /* "spare" section is optional */
244 if (rf_search_file_for_start_of("spare", buf
, 256, fp
))
245 cfgPtr
->numSpare
= 0;
246 for (c
= 0; c
< cfgPtr
->numSpare
; c
++) {
247 if (rf_get_next_nonblank_line(&cfgPtr
->spare_names
[c
][0],
249 RF_ERRORMSG1("Config file error: unable to get device file for spare disk %d\n", c
);
255 /* scan the file for the block related to layout */
257 if (rf_search_file_for_start_of("layout", buf
, 256, fp
)) {
258 RF_ERRORMSG1("Can't find \"layout\" section in configuration file %s\n", configname
);
262 if (rf_get_next_nonblank_line(buf
, 256, fp
, NULL
)) {
263 RF_ERRORMSG("Config file error (\"layout\" section): unable to find common layout param line\n");
267 c
= sscanf(buf
, "%d %d %d %c", &aa
, &bb
, &cc
, &cfgPtr
->parityConfig
);
268 cfgPtr
->sectPerSU
= (RF_SectorNum_t
) aa
;
269 cfgPtr
->SUsPerPU
= (RF_StripeNum_t
) bb
;
270 cfgPtr
->SUsPerRU
= (RF_StripeNum_t
) cc
;
272 RF_ERRORMSG("Unable to scan common layout line\n");
276 lp
= rf_GetLayout(cfgPtr
->parityConfig
);
278 RF_ERRORMSG1("Unknown parity config '%c'\n",
279 cfgPtr
->parityConfig
);
284 retcode
= lp
->MakeLayoutSpecific(fp
, cfgPtr
, lp
->makeLayoutSpecificArg
);
288 retcode
= errno
= EINVAL
;
295 /* used in architectures such as RAID0 where there is no layout-specific
296 * information to be passed into the configuration code.
299 rf_MakeLayoutSpecificNULL(FILE *fp
, RF_Config_t
*cfgPtr
, void *ignored
)
301 cfgPtr
->layoutSpecificSize
= 0;
302 cfgPtr
->layoutSpecific
= NULL
;
307 rf_MakeLayoutSpecificDeclustered(FILE *configfp
, RF_Config_t
*cfgPtr
, void *arg
)
309 int b
, v
, k
, r
, lambda
, norotate
, i
, val
, distSpare
;
310 char *cfgBuf
, *bdfile
, *p
, *smname
;
311 char buf
[256], smbuf
[256];
314 distSpare
= *((int *) arg
);
316 /* get the block design file name */
317 if (rf_get_next_nonblank_line(buf
, 256, configfp
,
318 "Can't find block design file name in config file\n"))
320 bdfile
= rf_find_non_white(buf
);
321 if (bdfile
[strlen(bdfile
) - 1] == '\n') {
322 /* strip newline char */
323 bdfile
[strlen(bdfile
) - 1] = '\0';
325 /* open bd file, check validity of configuration */
326 if ((fp
= fopen(bdfile
, "r")) == NULL
) {
327 RF_ERRORMSG1("RAID: config error: Can't open layout table file %s\n", bdfile
);
330 if (fgets(buf
, 256, fp
) == NULL
) {
331 RF_ERRORMSG1("RAID: config error: Can't read layout from layout table file %s\n", bdfile
);
335 i
= sscanf(buf
, "%u %u %u %u %u %u", &b
, &v
, &k
, &r
, &lambda
, &norotate
);
337 norotate
= 0; /* no-rotate flag is optional */
339 RF_ERRORMSG("Unable to parse header line in block design file\n");
343 /* set the sparemap directory. In the in-kernel version, there's a
344 * daemon that's responsible for finding the sparemaps */
346 if (rf_get_next_nonblank_line(smbuf
, 256, configfp
,
347 "Can't find sparemap file name in config file\n")) {
351 smname
= rf_find_non_white(smbuf
);
352 if (smname
[strlen(smname
) - 1] == '\n') {
353 /* strip newline char */
354 smname
[strlen(smname
) - 1] = '\0';
361 /* allocate a buffer to hold the configuration info */
362 cfgPtr
->layoutSpecificSize
= RF_SPAREMAP_NAME_LEN
+
363 6 * sizeof(int) + b
* k
;
365 cfgBuf
= (char *) malloc(cfgPtr
->layoutSpecificSize
);
366 if (cfgBuf
== NULL
) {
370 cfgPtr
->layoutSpecific
= (void *) cfgBuf
;
373 /* install name of sparemap file */
374 for (i
= 0; smname
[i
]; i
++)
377 while (i
< RF_SPAREMAP_NAME_LEN
) {
383 * fill in the buffer with the block design parameters
384 * and then the block design itself
394 *((int *) p
) = lambda
;
396 *((int *) p
) = norotate
;
399 while (fscanf(fp
, "%d", &val
) == 1)
402 if ((unsigned int)(p
- cfgBuf
) != cfgPtr
->layoutSpecificSize
) {
403 RF_ERRORMSG2("Size mismatch creating layout specific data: is %d sb %d bytes\n", (int) (p
- cfgBuf
), (int) (6 * sizeof(int) + b
* k
));
409 /****************************************************************************
413 ***************************************************************************/
415 /* finds a non-white character in the line */
417 rf_find_non_white(char *p
)
419 for (; *p
!= '\0' && (*p
== ' ' || *p
== '\t'); p
++);
423 /* finds a white character in the line */
425 rf_find_white(char *p
)
427 for (; *p
!= '\0' && (*p
!= ' ' && *p
!= '\t'); p
++);
432 * searches a file for a line that says "START string", where string is
433 * specified as a parameter
436 rf_search_file_for_start_of(const char *string
, char *buf
, int len
, FILE *fp
)
441 if (fgets(buf
, len
, fp
) == NULL
)
443 p
= rf_find_non_white(buf
);
444 if (!strncmp(p
, "START", strlen("START"))) {
445 p
= rf_find_white(p
);
446 p
= rf_find_non_white(p
);
447 if (!strncmp(p
, string
, strlen(string
)))
453 /* reads from file fp into buf until it finds an interesting line */
455 rf_get_next_nonblank_line(char *buf
, int len
, FILE *fp
, const char *errmsg
)
460 while (fgets(buf
, len
, fp
) != NULL
) {
461 p
= rf_find_non_white(buf
);
462 if (*p
== '\n' || *p
== '\0' || *p
== '#')
465 while (l
>=0 && (buf
[l
]==' ' || buf
[l
]=='\n')) {
472 RF_ERRORMSG1("%s", errmsg
);
477 * Allocates an array for the spare table, and initializes it from a file.
478 * In the user-level version, this is called when recon is initiated.
479 * When/if I move recon into the kernel, there'll be a daemon that does
480 * an ioctl into raidframe which will block until a spare table is needed.
481 * When it returns, it will read a spare table from the file system,
482 * pass it into the kernel via a different ioctl, and then block again
483 * on the original ioctl.
485 * This is specific to the declustered layout, but doesn't belong in
486 * rf_decluster.c because it uses stuff that can't be compiled into
487 * the kernel, and it needs to be compiled into the user-level sparemap daemon.
491 rf_ReadSpareTable(RF_SparetWait_t
*req
, char *fname
)
493 int i
, j
, numFound
, linecount
, tableNum
, tupleNum
,
494 spareDisk
, spareBlkOffset
;
495 char buf
[1024], targString
[100], errString
[100];
496 RF_SpareTableEntry_t
**table
;
499 /* allocate and initialize the table */
500 table
= malloc(req
->TablesPerSpareRegion
*
501 sizeof(RF_SpareTableEntry_t
*));
504 "rf_ReadSpareTable: Unable to allocate table\n");
507 for (i
= 0; i
< req
->TablesPerSpareRegion
; i
++) {
508 table
[i
] = malloc(req
->BlocksPerTable
*
509 sizeof(RF_SpareTableEntry_t
));
510 if (table
[i
] == NULL
) {
512 "rf_ReadSpareTable: Unable to allocate table\n");
513 return (NULL
); /* XXX should cleanup too! */
515 for (j
= 0; j
< req
->BlocksPerTable
; j
++)
516 table
[i
][j
].spareDisk
=
517 table
[i
][j
].spareBlockOffsetInSUs
= -1;
520 /* 2. open sparemap file, sanity check */
521 if ((fp
= fopen(fname
, "r")) == NULL
) {
523 "rf_ReadSpareTable: Can't open sparemap file %s\n", fname
);
526 if (rf_get_next_nonblank_line(buf
, 1024, fp
,
527 "Invalid sparemap file: can't find header line\n")) {
531 if (buf
[strlen(buf
) - 1] == '\n')
532 buf
[strlen(buf
) - 1] = '\0';
534 snprintf(targString
, sizeof(targString
), "fdisk %d\n", req
->fcol
);
535 snprintf(errString
, sizeof(errString
),
536 "Invalid sparemap file: can't find \"fdisk %d\" line\n",
539 rf_get_next_nonblank_line(buf
, 1024, fp
, errString
);
540 if (!strncmp(buf
, targString
, strlen(targString
)))
544 /* no more blank lines or comments allowed now */
545 linecount
= req
->TablesPerSpareRegion
* req
->TableDepthInPUs
;
546 for (i
= 0; i
< linecount
; i
++) {
547 numFound
= fscanf(fp
, " %d %d %d %d", &tableNum
, &tupleNum
,
548 &spareDisk
, &spareBlkOffset
);
550 fprintf(stderr
, "Sparemap file prematurely exhausted after %d of %d lines\n", i
, linecount
);
555 table
[tableNum
][tupleNum
].spareDisk
= spareDisk
;
556 table
[tableNum
][tupleNum
].spareBlockOffsetInSUs
=
557 spareBlkOffset
* req
->SUsPerPU
;
561 return ((void *) table
);