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