1 /* $NetBSD: rf_configure.c,v 1.15 2001/10/04 16:02:08 oster 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 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, "%s %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