Home | History | Annotate | Line # | Download | only in raidctl
rf_configure.c revision 1.4
      1 /*
      2  * Copyright (c) 1995 Carnegie-Mellon University.
      3  * All rights reserved.
      4  *
      5  * Author: Mark Holland
      6  *
      7  * Permission to use, copy, modify and distribute this software and
      8  * its documentation is hereby granted, provided that both the copyright
      9  * notice and this permission notice appear in all copies of the
     10  * software, derivative works or modified versions, and any portions
     11  * thereof, and that both notices appear in supporting documentation.
     12  *
     13  * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
     14  * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND
     15  * FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
     16  *
     17  * Carnegie Mellon requests users of this software to return to
     18  *
     19  *  Software Distribution Coordinator  or  Software.Distribution (at) CS.CMU.EDU
     20  *  School of Computer Science
     21  *  Carnegie Mellon University
     22  *  Pittsburgh PA 15213-3890
     23  *
     24  * any improvements or extensions that they make and grant Carnegie the
     25  * rights to redistribute these changes.
     26  */
     27 
     28 /***************************************************************
     29  *
     30  * rf_configure.c -- code related to configuring the raidframe system
     31  *
     32  * configuration is complicated by the fact that we want the same
     33  * driver to work both in the kernel and at user level.  In the
     34  * kernel, we can't read the configuration file, so we configure
     35  * by running a user-level program that reads the config file,
     36  * creates a data structure describing the configuration and
     37  * passes it into the kernel via an ioctl.  Since we want the config
     38  * code to be common between the two versions of the driver, we
     39  * configure using the same two-step process when running at
     40  * user level.  Of course, at user level, the config structure is
     41  * passed directly to the config routine, rather than via ioctl.
     42  *
     43  * This file is not compiled into the kernel, so we have no
     44  * need for KERNEL ifdefs.
     45  *
     46  **************************************************************/
     47 
     48 #include <stdio.h>
     49 #include <stdlib.h>
     50 #include <errno.h>
     51 #include <strings.h>
     52 #include <sys/types.h>
     53 #include <sys/stat.h>
     54 #include "rf_raid.h"
     55 #include "rf_raidframe.h"
     56 #include "rf_utils.h"
     57 #include "rf_general.h"
     58 #include "rf_decluster.h"
     59 #include "rf_configure.h"
     60 #include "rf_sys.h"
     61 
     62 /*
     63 
     64    XXX we include this here so we don't need to drag rf_debugMem.c into
     65    the picture...  This is userland, afterall...
     66 
     67  */
     68 
     69 /* XXX sucky hack to override the defn. of RF_Malloc as given in
     70 rf_debugMem.c...  but I *really* don't want (nor need) to link with
     71 that file here in userland..  GO
     72 */
     73 
     74 #undef RF_Malloc
     75 #define RF_Malloc(_p_, _size_, _cast_) \
     76   { \
     77      _p_ = _cast_ malloc((u_long)_size_); \
     78      bzero((char *)_p_, _size_); \
     79   }
     80 
     81 
     82 
     83 #ifndef SIMULATE
     84 static unsigned int dev_name2num(char *s);
     85 static unsigned int osf_dev_name2num(char *s);
     86 #endif
     87 static int rf_search_file_for_start_of(char *string, char *buf, int len,
     88 	FILE *fp);
     89 static int rf_get_next_nonblank_line(char *buf, int len, FILE *fp,
     90 	char *errmsg);
     91 
     92 /* called from user level to read the configuration file and create
     93  * a configuration control structure.  This is used in the user-level
     94  * version of the driver, and in the user-level program that configures
     95  * the system via ioctl.
     96  */
     97 int rf_MakeConfig(configname, cfgPtr)
     98   char         *configname;
     99   RF_Config_t  *cfgPtr;
    100 {
    101   int numscanned, val, r, c, retcode, aa, bb, cc;
    102   char buf[256], buf1[256], *cp;
    103   RF_LayoutSW_t *lp;
    104   FILE *fp;
    105 
    106   bzero((char *)cfgPtr, sizeof(RF_Config_t));
    107 
    108   fp = fopen(configname, "r");
    109   if (!fp) {
    110     RF_ERRORMSG1("Can't open config file %s\n",configname);
    111     return(-1);
    112   }
    113 
    114   rewind(fp);
    115   if (rf_search_file_for_start_of("array", buf, 256, fp)) {
    116     RF_ERRORMSG1("Unable to find start of \"array\" params in config file %s\n",configname);
    117     retcode = -1; goto out;
    118   }
    119   rf_get_next_nonblank_line(buf, 256, fp, "Config file error (\"array\" section):  unable to get numRow and numCol\n");
    120   /*
    121    * wackiness with aa, bb, cc to get around size problems on 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; goto out;
    127   }
    128   cfgPtr->numRow = (RF_RowCol_t)aa;
    129   cfgPtr->numCol = (RF_RowCol_t)bb;
    130   cfgPtr->numSpare = (RF_RowCol_t)cc;
    131 
    132   /* debug section is optional */
    133   for (c=0; c<RF_MAXDBGV; c++)
    134     cfgPtr->debugVars[c][0] = '\0';
    135   rewind(fp);
    136   if (!rf_search_file_for_start_of("debug", buf, 256, fp)) {
    137     for (c=0; c < RF_MAXDBGV; c++) {
    138       if (rf_get_next_nonblank_line(buf, 256, fp, NULL)) break;
    139       cp = rf_find_non_white(buf);
    140       if (!strncmp(cp, "START", strlen("START"))) break;
    141       (void) strcpy(&cfgPtr->debugVars[c][0], cp);
    142     }
    143   }
    144 
    145   rewind(fp);
    146   strcpy(cfgPtr->diskQueueType,"fifo");
    147   cfgPtr->maxOutstandingDiskReqs = 1;
    148   /* scan the file for the block related to disk queues */
    149   if (rf_search_file_for_start_of("queue",buf,256,fp)) {
    150     RF_ERRORMSG2("[No disk queue discipline specified in config file %s.  Using %s.]\n",configname, cfgPtr->diskQueueType);
    151   } else {
    152     if (rf_get_next_nonblank_line(buf, 256, fp, NULL)) {
    153       RF_ERRORMSG2("[No disk queue discipline specified in config file %s.  Using %s.]\n",configname, cfgPtr->diskQueueType);
    154     }
    155   }
    156 
    157   /* the queue specifier line contains two entries:
    158    * 1st char of first word specifies queue to be used
    159    * 2nd word specifies max num reqs that can be outstanding on the disk itself (typically 1)
    160    */
    161   if (sscanf(buf,"%s %d",buf1,&val)!=2) {
    162     RF_ERRORMSG1("Can't determine queue type and/or max outstanding reqs from line: %s",buf);
    163     RF_ERRORMSG2("Using %s-%d\n", cfgPtr->diskQueueType, cfgPtr->maxOutstandingDiskReqs);
    164   } else {
    165     char *c;
    166     bcopy(buf1, cfgPtr->diskQueueType, RF_MIN(sizeof(cfgPtr->diskQueueType), strlen(buf1)+1));
    167     for(c=buf1;*c;c++) {
    168       if (*c == ' ') {
    169         *c = '\0';
    170         break;
    171       }
    172     }
    173     cfgPtr->maxOutstandingDiskReqs = val;
    174   }
    175 
    176   rewind(fp);
    177 
    178 
    179   if (rf_search_file_for_start_of("disks",buf,256,fp)) {
    180     RF_ERRORMSG1("Can't find \"disks\" section in config file %s\n",configname);
    181     retcode = -1; goto out;
    182   }
    183 
    184   for (r=0; r<cfgPtr->numRow; r++) {
    185     for (c=0; c<cfgPtr->numCol; c++) {
    186       if (rf_get_next_nonblank_line(&cfgPtr->devnames[r][c][0], 50, fp, NULL)) {
    187 	RF_ERRORMSG2("Config file error: unable to get device file for disk at row %d col %d\n",r,c);
    188 	retcode = -1; goto out;
    189       }
    190 #ifndef SIMULATE
    191       val = dev_name2num(&cfgPtr->devnames[r][c][0]);
    192 
    193       if (val < 0) {
    194 	RF_ERRORMSG3("Config file error: can't get dev num (dev file '%s') for disk at row %d c %d\n",
    195 		  &cfgPtr->devnames[r][c][0],r,c);
    196 	retcode = -1; goto out;
    197       } else cfgPtr->devs[r][c] = val;
    198 #endif /* !SIMULATE */
    199     }
    200   }
    201 
    202   /* "spare" section is optional */
    203   rewind(fp);
    204   if (rf_search_file_for_start_of("spare",buf,256,fp)) cfgPtr->numSpare =0;
    205   for (c = 0; c < cfgPtr->numSpare; c++) {
    206     if (rf_get_next_nonblank_line(&cfgPtr->spare_names[c][0], 256, fp, NULL)) {
    207       RF_ERRORMSG1("Config file error: unable to get device file for spare disk %d\n",c);
    208       retcode = -1; goto out;
    209     }
    210 #ifndef SIMULATE
    211     val = dev_name2num(&cfgPtr->spare_names[c][0]);
    212     if (val < 0) {
    213       RF_ERRORMSG2("Config file error: can't get dev num (dev file '%s') for spare disk %d\n",
    214 		&cfgPtr->spare_names[c][0],c);
    215       retcode = -1; goto out;
    216     } else cfgPtr->spare_devs[c] = val;
    217 #endif /* !SIMULATE */
    218   }
    219 
    220   /* scan the file for the block related to layout */
    221   rewind(fp);
    222   if (rf_search_file_for_start_of("layout",buf,256,fp)) {
    223     RF_ERRORMSG1("Can't find \"layout\" section in configuration file %s\n",configname);
    224     retcode = -1; goto out;
    225   }
    226   if (rf_get_next_nonblank_line(buf, 256, fp, NULL)) {
    227     RF_ERRORMSG("Config file error (\"layout\" section): unable to find common layout param line\n");
    228     retcode = -1; goto out;
    229   }
    230   c = sscanf(buf,"%d %d %d %c", &aa, &bb, &cc, &cfgPtr->parityConfig);
    231   cfgPtr->sectPerSU = (RF_SectorNum_t)aa;
    232   cfgPtr->SUsPerPU = (RF_StripeNum_t)bb;
    233   cfgPtr->SUsPerRU = (RF_StripeNum_t)cc;
    234   if (c != 4) {
    235     RF_ERRORMSG("Unable to scan common layout line\n");
    236     retcode = -1; goto out;
    237   }
    238   lp = rf_GetLayout(cfgPtr->parityConfig);
    239   if (lp == NULL) {
    240     RF_ERRORMSG1("Unknown parity config '%c'\n", cfgPtr->parityConfig);
    241     retcode = -1;
    242     goto out;
    243   }
    244 
    245   /* XXX who cares.. it's not going into the kernel, so we should ignore this... */
    246 #ifndef KERNEL
    247   retcode = lp->MakeLayoutSpecific(fp, cfgPtr, lp->makeLayoutSpecificArg);
    248 #endif
    249 out:
    250   fclose(fp);
    251   if (retcode < 0)
    252     retcode = errno = EINVAL;
    253   else
    254     errno = retcode;
    255   return(retcode);
    256 }
    257 
    258 
    259 /* used in architectures such as RAID0 where there is no layout-specific
    260  * information to be passed into the configuration code.
    261  */
    262 int rf_MakeLayoutSpecificNULL(fp, cfgPtr, ignored)
    263   FILE         *fp;
    264   RF_Config_t  *cfgPtr;
    265   void         *ignored;
    266 {
    267   cfgPtr->layoutSpecificSize = 0;
    268   cfgPtr->layoutSpecific     = NULL;
    269   return(0);
    270 }
    271 
    272 int rf_MakeLayoutSpecificDeclustered(configfp, cfgPtr, arg)
    273   FILE         *configfp;
    274   RF_Config_t  *cfgPtr;
    275   void         *arg;
    276 {
    277   int b, v, k, r, lambda, norotate, i, val, distSpare;
    278   char *cfgBuf, *bdfile, *p, *smname;
    279   char buf[256], smbuf[256];
    280   FILE *fp;
    281 
    282   distSpare = *((int *)arg);
    283 
    284   /* get the block design file name */
    285   if (rf_get_next_nonblank_line(buf,256,configfp,"Can't find block design file name in config file\n"))
    286     return(EINVAL);
    287   bdfile = rf_find_non_white(buf);
    288   if (bdfile[strlen(bdfile)-1] == '\n') {
    289     /* strip newline char */
    290     bdfile[strlen(bdfile)-1] = '\0';
    291   }
    292 
    293   /* open bd file, check validity of configuration */
    294   if ((fp = fopen(bdfile,"r"))==NULL) {
    295     RF_ERRORMSG1("RAID: config error: Can't open layout table file %s\n",bdfile);
    296     return(EINVAL);
    297   }
    298 
    299   fgets(buf,256,fp);
    300   i = sscanf(buf,"%u %u %u %u %u %u",&b,&v,&k,&r,&lambda,&norotate);
    301   if (i == 5)
    302     norotate = 0; /* no-rotate flag is optional */
    303   else if (i != 6) {
    304     RF_ERRORMSG("Unable to parse header line in block design file\n");
    305     return(EINVAL);
    306   }
    307 
    308   /* set the sparemap directory.  In the in-kernel version, there's a daemon
    309    * that's responsible for finding the sparemaps
    310    */
    311   if (distSpare) {
    312     if (rf_get_next_nonblank_line(smbuf,256,configfp,"Can't find sparemap file name in config file\n"))
    313       return(EINVAL);
    314     smname = rf_find_non_white(smbuf);
    315     if (smname[strlen(smname)-1] == '\n') {
    316       /* strip newline char */
    317       smname[strlen(smname)-1] = '\0';
    318     }
    319   }
    320   else {
    321     smbuf[0] = '\0';
    322     smname = smbuf;
    323   }
    324 
    325   /* allocate a buffer to hold the configuration info */
    326   cfgPtr->layoutSpecificSize = RF_SPAREMAP_NAME_LEN + 6 * sizeof(int) + b * k;
    327   /* can't use RF_Malloc here b/c debugMem module not yet init'd */
    328   cfgBuf = (char *) malloc(cfgPtr->layoutSpecificSize);
    329   cfgPtr->layoutSpecific = (void *) cfgBuf;
    330   p = cfgBuf;
    331 
    332   /* install name of sparemap file */
    333   for (i=0; smname[i]; i++)
    334     *p++ = smname[i];
    335   /* pad with zeros */
    336   while (i<RF_SPAREMAP_NAME_LEN) {
    337     *p++ = '\0';
    338     i++;
    339   }
    340 
    341   /*
    342    * fill in the buffer with the block design parameters
    343    * and then the block design itself
    344    */
    345   *( (int *) p) = b;        p += sizeof(int);
    346   *( (int *) p) = v;        p += sizeof(int);
    347   *( (int *) p) = k;        p += sizeof(int);
    348   *( (int *) p) = r;        p += sizeof(int);
    349   *( (int *) p) = lambda;   p += sizeof(int);
    350   *( (int *) p) = norotate; p += sizeof(int);
    351 
    352   while (fscanf(fp,"%d",&val) == 1)
    353     *p++ = (char) val;
    354   fclose(fp);
    355   if (p - cfgBuf != cfgPtr->layoutSpecificSize) {
    356       RF_ERRORMSG2("Size mismatch creating layout specific data: is %d sb %d bytes\n",(int)(p-cfgBuf),(int)(6*sizeof(int)+b*k));
    357       return(EINVAL);
    358   }
    359   return(0);
    360 }
    361 
    362 
    363 /****************************************************************************
    364  *
    365  * utilities
    366  *
    367  ***************************************************************************/
    368 #ifndef SIMULATE
    369 /* convert a device file name to a device number */
    370 static unsigned int dev_name2num(s)
    371   char  *s;
    372 {
    373   struct stat buf;
    374 
    375   if (stat(s, &buf) < 0) return(osf_dev_name2num(s));
    376   else return(buf.st_rdev);
    377 }
    378 
    379 /* converts an osf/1 style device name to a device number.  We use this
    380  * only if the stat of the device file fails.
    381  */
    382 static unsigned int osf_dev_name2num(s)
    383   char  *s;
    384 {
    385   int num;
    386   char part_ch, lun_ch;
    387   unsigned int bus, target, lun, part, dev_major;
    388 
    389   dev_major = RF_SCSI_DISK_MAJOR;
    390   if (sscanf(s,"/dev/rrz%d%c", &num, &part_ch) == 2) {
    391     bus = num>>3;
    392     target = num & 0x7;
    393     part = part_ch - 'a';
    394     lun = 0;
    395   } else if (sscanf(s,"/dev/rrz%c%d%c", &lun_ch, &num, &part_ch) == 3) {
    396     bus = num>>3;
    397     target = num & 0x7;
    398     part = part_ch - 'a';
    399     lun = lun_ch - 'a' + 1;
    400   } else {
    401     RF_ERRORMSG1("Unable to parse disk dev file name %s\n",s);
    402     return(-1);
    403   }
    404 
    405   return( (dev_major<<20) | (bus<<14) | (target<<10) | (lun<<6) | part );
    406 }
    407 #endif
    408 
    409 /* searches a file for a line that says "START string", where string is
    410  * specified as a parameter
    411  */
    412 static int rf_search_file_for_start_of(string, buf, len, fp)
    413   char  *string;
    414   char  *buf;
    415   int    len;
    416   FILE  *fp;
    417 {
    418   char *p;
    419 
    420   while (1) {
    421     if (fgets(buf, len, fp) == NULL) return(-1);
    422     p = rf_find_non_white(buf);
    423     if (!strncmp(p, "START", strlen("START"))) {
    424       p = rf_find_white(p);
    425       p = rf_find_non_white(p);
    426       if (!strncmp(p, string, strlen(string))) return(0);
    427     }
    428   }
    429 }
    430 
    431 /* reads from file fp into buf until it finds an interesting line */
    432 int rf_get_next_nonblank_line(buf, len, fp, errmsg)
    433   char  *buf;
    434   int    len;
    435   FILE  *fp;
    436   char  *errmsg;
    437 {
    438   char *p;
    439 
    440   while (fgets(buf,256,fp) != NULL) {
    441     p = rf_find_non_white(buf);
    442     if (*p == '\n' || *p == '\0' || *p == '#') continue;
    443     return(0);
    444   }
    445   if (errmsg) RF_ERRORMSG(errmsg);
    446   return(1);
    447 }
    448 
    449 /* Allocates an array for the spare table, and initializes it from a file.
    450  * In the user-level version, this is called when recon is initiated.
    451  * When/if I move recon into the kernel, there'll be a daemon that does
    452  * an ioctl into raidframe which will block until a spare table is needed.
    453  * When it returns, it will read a spare table from the file system,
    454  * pass it into the kernel via a different ioctl, and then block again
    455  * on the original ioctl.
    456  *
    457  * This is specific to the declustered layout, but doesn't belong in
    458  * rf_decluster.c because it uses stuff that can't be compiled into
    459  * the kernel, and it needs to be compiled into the user-level sparemap daemon.
    460  *
    461  */
    462 void *rf_ReadSpareTable(req, fname)
    463   RF_SparetWait_t  *req;
    464   char             *fname;
    465 {
    466   int i, j, numFound, linecount, tableNum, tupleNum, spareDisk, spareBlkOffset;
    467   char buf[1024], targString[100], errString[100];
    468   RF_SpareTableEntry_t **table;
    469   FILE *fp;
    470 
    471   /* allocate and initialize the table */
    472   RF_Malloc(table, req->TablesPerSpareRegion * sizeof(RF_SpareTableEntry_t *), (RF_SpareTableEntry_t **));
    473   for (i=0; i<req->TablesPerSpareRegion; i++) {
    474     RF_Malloc(table[i], req->BlocksPerTable * sizeof(RF_SpareTableEntry_t), (RF_SpareTableEntry_t *));
    475     for (j=0; j<req->BlocksPerTable; j++) table[i][j].spareDisk = table[i][j].spareBlockOffsetInSUs = -1;
    476   }
    477 
    478   /* 2.  open sparemap file, sanity check */
    479   if ((fp = fopen(fname,"r"))==NULL) {
    480     fprintf(stderr,"rf_ReadSpareTable:  Can't open sparemap file %s\n",fname);  return(NULL);
    481   }
    482   if (rf_get_next_nonblank_line(buf,1024,fp,"Invalid sparemap file:  can't find header line\n"))
    483     return(NULL);
    484   if (buf[strlen(buf)-1] == '\n')
    485     buf[strlen(buf)-1] = '\0';
    486 
    487   sprintf(targString, "fdisk %d\n", req->fcol);
    488   sprintf(errString, "Invalid sparemap file:  can't find \"fdisk %d\" line\n",req->fcol);
    489   while (1) {
    490     rf_get_next_nonblank_line(buf,1024,fp,errString);
    491     if (!strncmp(buf,targString,strlen(targString))) break;
    492   }
    493 
    494   /* no more blank lines or comments allowed now */
    495   linecount = req->TablesPerSpareRegion * req->TableDepthInPUs;
    496   for (i=0; i<linecount; i++) {
    497     numFound = fscanf(fp," %d %d %d %d",&tableNum, &tupleNum, &spareDisk, &spareBlkOffset);
    498     if (numFound != 4) {
    499       fprintf(stderr,"Sparemap file prematurely exhausted after %d of %d lines\n",i,linecount); return(NULL);
    500     }
    501 
    502     RF_ASSERT(tableNum >= 0 && tableNum < req->TablesPerSpareRegion);
    503     RF_ASSERT(tupleNum >= 0 && tupleNum < req->BlocksPerTable);
    504     RF_ASSERT(spareDisk >= 0 && spareDisk < req->C);
    505     RF_ASSERT(spareBlkOffset >= 0 && spareBlkOffset < req->SpareSpaceDepthPerRegionInSUs / req->SUsPerPU);
    506 
    507     table[tableNum][tupleNum].spareDisk = spareDisk;
    508     table[tableNum][tupleNum].spareBlockOffsetInSUs = spareBlkOffset * req->SUsPerPU;
    509   }
    510 
    511   fclose(fp);
    512   return((void *) table);
    513 }
    514