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