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