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