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