Logo Search packages:      
Sourcecode: dmraid version File versions  Download package

asr.c

/*
 * Adaptec HostRAID ASR metadata format handler.
 *
 * Copyright (C) 2005-2006 IBM, All rights reserved.
 * Written by Darrick Wong <djwong@us.ibm.com>,
 * James Simshaw <simshawj@us.ibm.com>, and
 * Adam DiCarlo <bikko@us.ibm.com>
 *
 * Copyright (C) 2006  Heinz Mauelshagen, Red Hat GmbH
 *                 All rights reserved.
 *
 * See file LICENSE at the top of this source tree for license information.
 */

#include <netinet/in.h>
#include <time.h>

#define     HANDLER     "asr"

#include "internal.h"
#define     FORMAT_HANDLER
#include "asr.h"

#if   BYTE_ORDER == LITTLE_ENDIAN
#  define   DM_BYTEORDER_SWAB
#  include  <datastruct/byteorder.h>
#endif

static const char *handler = HANDLER;
static const char *spare_array = ".asr_spares";

/* Map ASR disk status to dmraid status */
static enum status disk_status(struct asr_raid_configline *disk) {
      static struct states states[] = {
            { LSU_COMPONENT_STATE_OPTIMAL, s_ok },
            { LSU_COMPONENT_STATE_DEGRADED, s_broken },
            { LSU_COMPONENT_STATE_FAILED, s_broken },
            { LSU_COMPONENT_STATE_UNINITIALIZED, s_inconsistent },
            { LSU_COMPONENT_STATE_UNCONFIGURED, s_inconsistent },
            { LSU_COMPONENT_SUBSTATE_BUILDING, s_nosync },
            { LSU_COMPONENT_SUBSTATE_REBUILDING, s_nosync },
            { LSU_COMPONENT_STATE_REPLACED, s_nosync },
            { 0, s_undef },
      };

      return rd_status(states, disk->raidstate, EQUAL);
}
            
/* Extract config line from metadata */
static struct asr_raid_configline *get_config(struct asr *asr, uint32_t magic)
{
      unsigned int i = asr->rt->elmcnt;
      
      while (i--) {
            if (asr->rt->ent[i].raidmagic == magic)
                  return asr->rt->ent + i;
      }

      return NULL;
}

/* Get this disk's configuration */
static struct asr_raid_configline *this_disk(struct asr *asr)
{
      return get_config(asr, asr->rb.drivemagic);
}

/* Make up RAID device name. */
static size_t _name(struct lib_context *lc, struct asr *asr, char *str,
                size_t len)
{
      struct asr_raid_configline *cl = this_disk(asr);

      if (cl)
            return snprintf(str, len, "%s_%s", HANDLER, cl->name);

      LOG_ERR(lc, 0, "%s: Could not find device in config table!", handler);
}

/* Figure out a name for the RAID device. */
static char *name(struct lib_context *lc, struct asr *asr)
{
      size_t len;
      char *ret;

      if ((ret = dbg_malloc((len = _name(lc, asr, NULL, 0) + 1))))
            _name(lc, asr, ret, len);
      else
            log_alloc_err(lc, handler);

      return ret;
}

/* Stride size */
static inline unsigned int stride(struct asr_raid_configline *cl)
{
      return cl ? cl->strpsize: 0;
}

/*
 * FIXME: This needs more examination.  Does HostRAID do linear
 * combination?  The BIOS implies that it only does RAID 0, 1 and 10.
 * The emd driver implied support for RAID3/4/5, but dm doesn't
 * do any of those right now (RAID4 and RAID5 are in the works).
 */
/* Map the ASR raid type codes into dmraid type codes. */
static enum type type(struct asr_raid_configline *cl)
{
      /* Mapping of template types to generic types */
      static struct types types[] = {
            { ASR_RAID0,   t_raid0 },
            { ASR_RAID1,   t_raid1 },
            { ASR_RAIDSPR, t_spare },
            { 0, t_undef},
      };

printf("cl->raidtype=%d rd_type=%d\n", cl->raidtype, rd_type(types, (unsigned int) cl->raidtype));
      return cl ? rd_type(types, (unsigned int) cl->raidtype) : t_undef;
}

/*
 * Read an ASR RAID device.  Fields are big endian, so
 * need to convert them if we're on a LE machine (i386, etc).
 */
enum { ASR_BLOCK = 0x01, ASR_TABLE = 0x02, ASR_EXTTABLE  = 0x04 };

#if   BYTE_ORDER == LITTLE_ENDIAN
static void cvt_configline(struct asr_raid_configline *cl)
{
      CVT16(cl->raidcnt);
      CVT16(cl->raidseq);
      CVT32(cl->raidmagic);
      CVT32(cl->raidid);
      CVT32(cl->loffset);
      CVT32(cl->lcapcty);
      CVT16(cl->strpsize);
      CVT16(cl->biosInfo);
      CVT32(cl->lsu);
      CVT16(cl->blockStorageTid);
      CVT32(cl->curAppBlock);
      CVT32(cl->appBurstCount);
}

static void to_cpu(void *meta, unsigned int cvt)
{
      struct asr *asr = meta;
      unsigned int i, elmcnt = asr->rt->elmcnt,
                 use_old_elmcnt = (asr->rt->ridcode == RVALID2);

      if (cvt & ASR_BLOCK) {
            CVT32(asr->rb.b0idcode);
            CVT16(asr->rb.biosInfo);
            CVT32(asr->rb.fstrsvrb);
            CVT16(asr->rb.svBlockStorageTid);
            CVT16(asr->rb.svtid);
            CVT32(asr->rb.drivemagic);
            CVT32(asr->rb.fwTestMagic);
            CVT32(asr->rb.fwTestSeqNum);
            CVT32(asr->rb.smagic);
            CVT32(asr->rb.raidtbl);
      }

      if (cvt & ASR_TABLE) {
            CVT32(asr->rt->ridcode);
            CVT32(asr->rt->rversion);
            CVT16(asr->rt->maxelm);
            CVT16(asr->rt->elmcnt);
            if (!use_old_elmcnt)
                  elmcnt = asr->rt->elmcnt;
            CVT16(asr->rt->elmsize);
            CVT32(asr->rt->raidFlags);
            CVT32(asr->rt->timestamp);
            CVT16(asr->rt->rchksum);
            CVT32(asr->rt->sparedrivemagic);
            CVT32(asr->rt->raidmagic);
            CVT32(asr->rt->verifyDate);
            CVT32(asr->rt->recreateDate);

            /* Convert the first seven config lines */
            for (i = 0; i < (min(elmcnt, ASR_TBLELMCNT)); i++) 
                  cvt_configline(asr->rt->ent + i);
      }

      if (cvt & ASR_EXTTABLE) {
            for (i = ASR_TBLELMCNT; i < elmcnt; i++)
                  cvt_configline(asr->rt->ent + i);
      }
}

#else
# define to_cpu(x, y)
#endif

/* Compute the checksum of RAID metadata */
static unsigned int compute_checksum(struct asr *asr)
{
      uint8_t *ptr = (uint8_t*) asr->rt->ent;
      unsigned int checksum = 0,
                 end = sizeof(*asr->rt->ent) * asr->rt->elmcnt;

      /* Compute checksum. */
      while (end--)
            checksum += *(ptr++);

      return checksum & 0xFFFF;
}

/* (Un)truncate white space at the end of a name */
enum truncate { TRUNCATE, UNTRUNCATE };
static void handle_white_space(uint8_t *p, enum truncate truncate)
{
      unsigned int j = ASR_NAMELEN;
      uint8_t c = truncate == TRUNCATE ? 0 : ' ';

      while (j-- && (truncate == TRUNCATE ? isspace(p[j]) : !p[j]))
            p[j] = c;
}

/* Read extended metadata areas */
static int read_extended(struct lib_context *lc, struct dev_info *di,
                   struct asr *asr)
{
      unsigned int remaining, i, chk;

      log_notice(lc, "%s: reading extended data on %s", handler, di->path);
      
      /* Read the RAID table. */
      if (!read_file(lc, handler, di->path, asr->rt, ASR_DISK_BLOCK_SIZE,
                   (uint64_t) asr->rb.raidtbl * ASR_DISK_BLOCK_SIZE))
            LOG_ERR(lc, 0, "%s: Could not read metadata off %s",
                  handler, di->path);

      /* Convert it */
      to_cpu(asr, ASR_TABLE);
      
      /* Is this ok? */
      if (asr->rt->ridcode != RVALID2)
            LOG_ERR(lc, 0, "%s: Invalid magic number in RAID table; "
                  "saw 0x%X, expected 0x%X on %s",
                  handler, asr->rt->ridcode, RVALID2, di->path);

      /* Have we a valid element count? */
      if (asr->rt->elmcnt >= asr->rt->maxelm || asr->rt->elmcnt == 0)
            LOG_ERR(lc, 0, "%s: Invalid RAID config table count on %s",
                  handler, di->path);

      /* Is each element the right size? */
      if (asr->rt->elmsize != sizeof(*asr->rt->ent))
            LOG_ERR(lc, 0, "%s: Wrong RAID config line size on %s",
                  handler, di->path);

      /* Figure out how much else we need to read. */
      if (asr->rt->elmcnt > ASR_TBLELMCNT) {
            remaining = asr->rt->elmsize * (asr->rt->elmcnt - 7);
            if (!read_file(lc, handler, di->path, asr->rt->ent + 7,
                         remaining, (uint64_t)(asr->rb.raidtbl + 1) *
                         ASR_DISK_BLOCK_SIZE))
                  return 0;

            to_cpu(asr, ASR_EXTTABLE);
      }

      chk = compute_checksum(asr);
      if (chk != asr->rt->rchksum)
            LOG_ERR(lc, 0,"%s: Invalid RAID config table checksum "
                         "(0x%X vs. 0x%X) on %s",
                  handler, chk, asr->rt->rchksum, di->path);
      
      /* Process the name of each line of the config line. */
      for (i = 0; i < asr->rt->elmcnt; i++) {
            /* 
             * Weird quirks of the name field of the config line:
             *
             * - SATA HostRAID w/ ICH5 on IBM x226: The name field is null
             *   in the drive config lines.  The zeroeth item does have a
             *   name, however.
             * - Spares on SCSI HostRAID on IBM x226: The name field for
             *   all config lines is null.
             * 
             * So, we'll assume that we can copy the name from the zeroeth
             * element in the array.  The twisted logic doesn't seem to
             * have a problem with either of the above cases, though
             * attaching spares is going to be a tad tricky (primarily
             * because there doesn't seem to be a way to attach a spare to
             * a particular array; presumably the binary driver knows how
             * or just grabs a disk out of the spare pool.
             *
             * (Yes, the binary driver _does_ just grab a disk from the
             * global spare pool.  We must teach dm about this...?)
             *
             * This is nuts.
             */
            if (!asr->rt->ent[i].name)
                  memcpy(asr->rt->ent[i].name, asr->rt->ent[0].name, 16);

            /* Now truncate trailing whitespace in the name. */
            handle_white_space(asr->rt->ent[i].name, TRUNCATE);
      }

      return 1;
}

static int is_asr(struct lib_context *lc, struct dev_info *di, void *meta)
{
      struct asr *asr = meta;

      /*
       * Check our magic numbers and that the version == v8.
       * We don't support anything other than that right now.
       */
      if (asr->rb.b0idcode == B0RESRVD &&
          asr->rb.smagic == SVALID) {
            if (asr->rb.resver == RBLOCK_VER)
                  return 1;
            
            log_err(lc, "%s: ASR v%d detected, but we only support v8",
                  handler, asr->rb.resver);
      }

      return 0;
}

/*
 * Attempt to interpret ASR metadata from a block device.  This function
 * returns either NULL (not an ASR) or a pointer to a descriptor struct.
 * Note that the struct should be fully converted to the correct endianness
 * by the time this function returns.
 *
 * WARNING: If you take disks out of an ASR HostRAID array and plug them in
 * to a normal SCSI controller, the array will still show up!  Even if you
 * scribble over the disks!  I assume that the a320raid binary driver only
 * does its HostRAID magic if your controller is in RAID mode... but dmraid
 * lacks this sort of visibility as to where its block devices come from.
 * This is EXTREMELY DANGEROUS if you aren't careful!
 */
static void *read_metadata_areas(struct lib_context *lc, struct dev_info *di,
                         size_t *sz, uint64_t *offset,
                         union read_info *info)
{
      size_t size = ASR_DISK_BLOCK_SIZE;
      uint64_t asr_sboffset = ASR_CONFIGOFFSET;
      struct asr *asr;
      struct asr_raid_configline *cl;

      /*
       * Read the ASR reserved block on each disk.  This is the very
       * last sector of the disk, and we're really only interested in
       * the two magic numbers, the version, and the pointer to the
       * RAID table.  Everything else appears to be unused in v8.
       */
      if (!(asr = alloc_private(lc, handler, sizeof(*asr))))
            goto bad0;
      
      if (!(asr->rt = alloc_private(lc, handler, sizeof(*asr->rt))))
            goto bad1;

      if (!read_file(lc, handler, di->path, &asr->rb, size, asr_sboffset))
            goto bad2;

      /*
       * Convert metadata and read in 
       */
      to_cpu(asr, ASR_BLOCK);

      /* Check Signature and read optional extended metadata. */
      if (!is_asr(lc, di, asr) ||
          !read_extended(lc, di, asr))
            goto bad2;

      /*
       * Now that we made sure that we have all the metadata, we exit.
       */
      cl = this_disk(asr);
      if (cl->raidstate == LSU_COMPONENT_STATE_FAILED)
            goto bad2;

      goto out;

   bad2:
      dbg_free(asr->rt);
   bad1:
      asr->rt = NULL;
      dbg_free(asr);
   bad0:
      asr = NULL;

   out:
      return asr;
}

/* Read the whole metadata chunk at once */
static uint8_t *read_metadata_chunk(struct lib_context *lc, struct dev_info *di,
                            uint64_t start)
{
      uint8_t *ret;
      size_t size = (di->sectors - start) * ASR_DISK_BLOCK_SIZE;

      if (!(ret = dbg_malloc(size)))
            LOG_ERR(lc, ret, "%s: unable to allocate memory for %s",
                  handler, di->path);

      if (!read_file(lc, handler, di->path, ret, size,
                   start * ASR_DISK_BLOCK_SIZE)) {
            dbg_free(ret);
            LOG_ERR(lc, NULL, "%s: unable to read metadata on %s",
                  handler, di->path);
      }

      return ret;
}

/*
 * "File the metadata areas" -- I think this function is supposed to declare
 * which parts of the drive are metadata and thus off-limits to dmraid.
 */
static void file_metadata_areas(struct lib_context *lc, struct dev_info *di,
                        void *meta)
{
      uint8_t *buf;
      struct asr *asr = meta;
      uint64_t start = asr->rb.raidtbl;

      if (!(buf = read_metadata_chunk(lc, di, start)))
            return;

      /* Register the raid tables. */
      file_metadata(lc, handler, di->path, buf,
                  ASR_DISK_BLOCK_SIZE * 17,
                  start * ASR_DISK_BLOCK_SIZE);
      
      dbg_free(buf);
      
      /* Record the device size if -D was specified. */
      file_dev_size(lc, handler, di);
}

static int setup_rd(struct lib_context *lc, struct raid_dev *rd,
                struct dev_info *di, void *meta, union read_info *info);
static struct raid_dev *asr_read(struct lib_context *lc,
                              struct dev_info *di)
{
      /*
       * NOTE: Everything called after read_metadata_areas assumes that
       * the reserved block, raid table and config table have been
       * converted to the appropriate endianness.
       */
      return read_raid_dev(lc, di, read_metadata_areas, 0, 0, NULL, NULL,
                       file_metadata_areas, setup_rd, handler);
}

static int set_sort(struct list_head *dont, struct list_head *care)
{
      return 0;
}

/*
 * Compose a 64-bit ID for device sorting.
 * Is hba:ch:lun:id ok?
 * It seems to be the way the binary driver does it...
 */
static inline uint64_t compose_id(struct asr_raid_configline *cl)
{
      return    ((uint64_t) cl->raidhba  << 48)
            | ((uint64_t) cl->raidchnl << 40)
            | ((uint64_t) cl->raidlun  << 32)
            | (uint64_t) cl->raidid;
}

/* Sort ASR devices by for a RAID set. */
static int dev_sort(struct list_head *pos, struct list_head *new)
{
      return compose_id(this_disk(META(RD(new), asr))) <
             compose_id(this_disk(META(RD(pos), asr)));
}

/*
 * Find the top-level RAID set for an ASR context.
 */
static int find_toplevel(struct lib_context *lc, struct asr *asr)
{
      int i, toplevel = -1;

      for (i = 0; i < asr->rt->elmcnt; i++) {
            if (asr->rt->ent[i].raidlevel == FWL)
                  toplevel = i;
            else if (asr->rt->ent[i].raidlevel == FWL_2) {
                  toplevel = i;
                  break;
            }
      }
      
      return toplevel;
}

/*
 * Find the logical drive configuration that goes with this
 * physical disk configuration.
 */
static struct asr_raid_configline *find_logical(struct asr *asr)
{
      int i, j;

      /* This MUST be done backwards! */
      for (i = asr->rt->elmcnt - 1; i > -1; i--) {
            if (asr->rt->ent[i].raidmagic == asr->rb.drivemagic) {
                  for (j = i - 1; j > -1; j--) {
                        if (asr->rt->ent[j].raidlevel == FWL)
                              return asr->rt->ent + j;
                  }
            }
      }

      return NULL;
}

static struct raid_dev *find_spare(struct lib_context *lc) {
      struct raid_dev *spare;
      
      list_for_each_entry(spare, LC_RD(lc), list) {
            if (spare->type == t_spare)  
                  return spare;
      }

      return NULL;
}

/* Wrapper for name() */
static char *js_name(struct lib_context *lc, struct raid_dev *rd,
                 unsigned int subset)
{
      return name(lc, META(rd, asr));
}

/*
 * IO error event handler.
 */
static int event_io(struct lib_context *lc, struct event_io *e_io)
{
      struct raid_dev *rd = e_io->rd;
      struct asr *asr = META(rd, asr);
      struct asr_raid_configline *cl = this_disk(asr);
      struct asr_raid_configline *fwl = find_logical(asr);

      /* Ignore if we've already marked this disk broken(?) */
      if (rd->status & s_broken)
            return 0;
      
      log_err(lc, "%s: I/O error on device %s at sector %lu",
            handler, e_io->rd->di->path, e_io->sector);

      /* Mark the array as degraded and the disk as failed. */
      rd->status = s_broken;
      cl->raidstate = LSU_COMPONENT_STATE_FAILED;
      fwl->raidstate = LSU_COMPONENT_STATE_DEGRADED;
      /* FIXME: Do we have to mark a parent too? */

      return 1; /* Indicate that this is indeed a failure. */
}

/*
 * Helper routines for asr_group()
 */
static struct raid_set *do_spare(struct lib_context *lc, struct raid_dev *rd)
{
      struct raid_set *rs;

      /*
       * If this drive really _is_ attached to a specific
       * RAID set, then just attach it.  Really old HostRAID cards
       * do this... but I don't have any hardware to test this.
       */
      /*
       * FIXME: dmraid ignores spares attached to RAID arrays.
       * For now, we'll let it get sucked into the ASR spare pool. 
       * If we need it, we'll reconfigure it; if not, nobody touches
       * it.
       *
       * rs = find_set(lc, name(lc, asr), FIND_TOP, rd, LC_RS(lc),
       *           NO_CREATE, NO_CREATE_ARG);
       */

      /* Otherwise, make a global spare pool. */
      rs = find_or_alloc_raid_set(lc, (char*)spare_array, FIND_TOP, rd,
                            LC_RS(lc), NO_CREATE, NO_CREATE_ARG);

      /*
       * Setting the type to t_spare guarantees that dmraid won't
       * try to set up a real device-mapper mapping.
       */
      rs->type = t_spare;

      /* Add the disk to the set. */
      list_add_sorted(lc, &rs->devs, &rd->devs, dev_sort);
      return rs;
}

#define BUFSIZE 128
static struct raid_set *do_stacked(struct lib_context *lc, struct raid_dev *rd,
                           struct asr_raid_configline *cl)
{
      char buf[BUFSIZE], *path = rd->di->path;
      struct raid_set *rs, *ss;
      struct asr_raid_configline *fwl;
      struct asr *asr = META(rd, asr);

      /* First compute the name of the disk's direct parent. */
      fwl = find_logical(asr);
      if (!fwl)
            LOG_ERR(lc, NULL, "%s: Failed to find RAID configuration "
                          "line on %s",
                  handler, path);

      snprintf(buf, BUFSIZE, ".asr_%s_%x_donotuse",
             fwl->name, fwl->raidmagic);
      
      /* Now find said parent. */
      rs = find_or_alloc_raid_set(lc, buf, FIND_ALL, rd, NO_LIST,
                            NO_CREATE, NO_CREATE_ARG);
      if (!rs)
            LOG_ERR(lc, NULL, "%s: Error creating RAID set for %s",
                  handler, path);

      rs->stride = stride(cl);
      rs->status = s_ok;
      rs->type = type(fwl);

      /* Add the disk to the set. */
      list_add_sorted(lc, &rs->devs, &rd->devs, dev_sort);
      
      /* Find the top level set. */
      ss = join_superset(lc, js_name, NO_CREATE, set_sort, rs, rd);
      if (!ss)
            LOG_ERR(lc, NULL, "%s: Error creating top RAID set for %s",
                  handler, path);

      ss->stride = stride(cl);
      ss->status = s_ok;
      /* FIXME: correct type (this crashed in stacked set code) */
      ss->type = t_raid1; // type(&asr->rt->ent[top_idx]);
      return ss;
}

/* 
 * Add an ASR device to a RAID set.  This involves finding the raid set to
 * which this disk belongs, and then attaching it.  Note that there are other
 * complications, such as two-layer arrays (RAID10).
 */
static struct raid_set *asr_group(struct lib_context *lc, struct raid_dev *rd)
{
      int top_idx;
      struct asr *asr = META(rd, asr);
      struct asr_raid_configline *cl = this_disk(asr);
      struct raid_set *rs;

      if (T_SPARE(rd))
            return do_spare(lc, rd);

      /* Find the top level FWL/FWL2 for this device. */
      top_idx = find_toplevel(lc, asr);
      if (top_idx < 0)
            LOG_ERR(lc, NULL, "%s: Can't find a logical array config "
                  "for disk %x",
                  handler, asr->rb.drivemagic);

      /* This is a simple RAID0/1 array.  Find the set. */
      if (asr->rt->ent[top_idx].raidlevel == FWL) {
            rs = find_or_alloc_raid_set(lc, name(lc, asr), FIND_TOP,
                                  rd, LC_RS(lc), NO_CREATE,
                                  NO_CREATE_ARG);
            rs->stride = stride(cl);
            rs->status = s_ok;
            rs->type = type(find_logical(asr));

            /* Add the disk to the set. */
            list_add_sorted(lc, &rs->devs, &rd->devs, dev_sort);
            return rs;
      }

      /*
       * This is a two-level RAID array.  Attach the disk to the disk's
       * parent set; create it if necessary.  Then, find the top-level set
       * and use join_superset to attach the parent set to the top set.
       */
      if (asr->rt->ent[top_idx].raidlevel == FWL_2)
            return do_stacked(lc, rd, cl);


      /* If we land here, something's seriously wrong. */
      LOG_ERR(lc, NULL, "%s: Top level array config is not FWL/FWL2?",
            handler);
}

/* deletes configline from metadata of given asr, by index. */
static void delete_configline(struct asr *asr, int index)
{
      struct asr_raid_configline *cl, *end;

      asr->rt->elmcnt--;
      cl = asr->rt->ent + index;
      end = asr->rt->ent + asr->rt->elmcnt;
      while (cl < end) {
            memcpy(cl, cl + 1, sizeof(*cl));
            ++cl;
      }
}

/* Find the newest configline entry in raid set and return a pointer to it. */
static struct raid_dev *find_newest_drive(struct raid_set *rs)
{
      struct asr *asr;
      struct raid_dev *device, *newest = NULL;
      uint16_t newest_raidseq = 0;
        int i;
      
      list_for_each_entry(device, &rs->devs, devs) {
            asr = META(device, asr);
            // FIXME: We should be able to assume each configline
            // in a single drive has the same raidseq as the rest
            // in that drive. We're doing too much work here.
            for (i = 0; i < asr->rt->elmcnt; ++i) {
                  if (asr->rt->ent[i].raidseq >= newest_raidseq) {
                        newest_raidseq = asr->rt->ent[i].raidseq;
                        newest = device;
                  }
            }
      }
      
      return newest;
}

/* Creates a random integer for a drive magic section */
static uint32_t create_drivemagic() {

      srand(time(NULL));
      return rand() + rand();
}

static int spare(struct lib_context *lc, struct raid_dev *rd,
             struct asr *asr)
{
      struct asr_raid_configline *cl;

      /* If the magic is already 0xFFFFFFFF, exit */
      if (asr->rt->raidmagic == 0xFFFFFFFF)
            return 1;

      /* Otherwise, set the magic */
      asr->rt->raidmagic = 0xFFFFFFFF;

      /* Erase all the CLs, create the two defaults and exit */
      /* FIXME: How to set blockstoragetid? */
      asr->rt->elmcnt = 2;

      /* Note the presence of an array of spares in first config
       * line entry. */
      cl = asr->rt->ent;
      cl->raidmagic = 0xFFFFFFFF;
      cl->raidseq = 0;
      cl->name[0] = 0;
      cl->raidcnt = 1;
      cl->raidtype = ASR_RAIDSPR;
      cl->lcapcty = rd->di->sectors;
      cl->raidlevel = FWL;
      cl++;

      /* Actually describe the disk: it's a spare. */
      cl->raidmagic = asr->rb.drivemagic;
      cl->raidseq = 0;
      cl->name[0] = 0;
      cl->raidcnt = 0;
      cl->raidtype = ASR_RAIDSPR;
      cl->lcapcty = rd->di->sectors;
      cl->raidlevel = FWP;

      return 1;
}

/* Returns (boolean) whether or not the drive described by the given configline
 * is in the given raid_set. */
static int in_raid_set(struct asr_raid_configline *cl, struct raid_set *rs)
{
      struct asr *asr;
      struct raid_dev *d;

      list_for_each_entry(d, &rs->devs, devs) {
            asr = META(d, asr);
            if (cl->raidmagic == asr->rb.drivemagic)
                  return 1;
      }
      return 0;
}

/* Delete extra configlines which would otherwise trip us up. */
static int cleanup_configlines(struct raid_dev *rd, struct raid_set *rs)
{
      struct asr *a;
      struct raid_dev *d;
      struct asr_raid_configline *cl;
      int clcnt;

      list_for_each_entry(d, &rs->devs, devs) {
            a = META(d, asr);
      
            cl = a->rt->ent;
            for (clcnt = 0; clcnt < a->rt->elmcnt; /* done in loop */ ) {
                  /* If it's in the seen list, or is a logical drive, 
                   * end iteration. The idea: get rid of configlines
                   * which describe devices which are no longer in the
                   * array.
                   * FIXME: If our topmost level is FWL2, we could have
                   * FWL entries which need to be removed, right? We need
                   * to check for this condition, too. */
                  if (cl->raidlevel != FWP || in_raid_set(cl, rs)) {
                        cl++;
                        clcnt++;
                  } else {
                        /* Delete entry. After deleting, a new entry is
                         * found at *cl (a->rt->ent[clcnt]), so don't
                         * increment counter/pointer; otherwise we'd
                         * skip an entry.
                         */
                        delete_configline(a, clcnt);
                  }
            }
      }
      return 1;
}

/* Add a CL entry */
static int create_configline(struct raid_set *rs, struct asr *asr,
                         struct asr *a, struct raid_dev* newest)
{
      if (asr->rt->elmcnt >= RCTBL_MAX_ENTRIES) {
            return 0;
      }

      struct asr *newest_asr;
      struct asr_raid_configline *cl;
      
      newest_asr = META(newest, asr);
      
      cl = asr->rt->ent + asr->rt->elmcnt;
      asr->rt->elmcnt++;

      /* Use first raidseq, below: FIXME - don't assume all CLS are
       * consistent */
      cl->raidmagic = a->rb.drivemagic;
      cl->raidseq = newest_asr->rt->ent[0].raidseq;
      cl->strpsize = newest_asr->rt->ent[0].strpsize;
      strcpy((char*) cl->name, &rs->name[4]); /* starts after "asr_" */
      cl->raidcnt = 0;

      /* Convert rs->type to an ASR_RAID type for the CL */
      switch (rs->type) {
      case t_raid0:
            cl->raidtype = ASR_RAID0;
            break;
      case t_raid1:
            cl->raidtype = ASR_RAID1;
            break;
      default:
            return 0;
      }
      cl->lcapcty = newest_asr->rt->ent[0].lcapcty;
      cl->raidlevel = FWP;
      return 1;
}

/* Update metadata to reflect the current raid set configuration.
 * Returns boolean success. */
static int update_metadata(struct lib_context *lc,  struct raid_dev *rd,
                     struct asr *asr)
{
      struct raid_set *rs;
      struct asr_raid_configline *cl;
      struct raid_dev *d, *newest;
      struct asr *a;

      /* Find the raid set */
      rs = get_raid_set(lc, rd);
      if (!rs) {
            /* Array-less disks ... have no CLs ? */
            asr->rt->elmcnt = 0;
            return 1;
      }
      
      /* If this is the spare array... */
      if (!strcmp(spare_array, rs->name))
            return spare(lc, rd, asr);

      /* Find newest drive for use below */
      if (!(newest = find_newest_drive(rs)))
            return 0;

      /* If the drive magic is 0xFFFFFFFF, assign a random one. */
      if (asr->rb.drivemagic == 0xFFFFFFFF)
            asr->rb.drivemagic = create_drivemagic();

      /* Make sure the raid type agrees with the metadata */
      if (type(this_disk(asr)) == t_spare) {
            struct asr *newest_asr = META(newest, asr);
      
            /* copy entire table from newest drive */ 
            asr->rt->elmcnt = newest_asr->rt->elmcnt;
            memcpy(asr->rt->ent, newest_asr->rt->ent,
                  asr->rt->elmcnt * sizeof(*asr->rt->ent));
      }

      /* Increment the top level CL's raid count */
      /* Fixme: What about the the FWLs in a FWL2 setting? */
      cl = asr->rt->ent + find_toplevel(lc, asr);
      cl->raidseq++;

      /* For each disk in the rs */
      list_for_each_entry(d, &rs->devs, devs) {
            a = META(d, asr);

            /* If it's in the CL already... */
            if ((cl = get_config(asr, a->rb.drivemagic))) {
                  /* Increment seq number */
                  cl->raidseq++;
                  continue;
            }

            /* If the magic is 0xFFFFFFFF, assign a random one */
            if (a->rb.drivemagic == 0xFFFFFFFF) {
                  a->rb.drivemagic = create_drivemagic();
            }
            
            if (!(newest = find_newest_drive(rs)))
                  return 0;
                        
            create_configline(rs, asr, a, newest);
      }

      cleanup_configlines(rd, rs);

      return 1;
}


/* Write metadata. */
static int asr_write(struct lib_context *lc,  struct raid_dev *rd, int erase)
{
        struct asr *asr = META(rd, asr);
      int elmcnt = asr->rt->elmcnt, i, ret;

      /* Update the metadata if we're not erasing it. */
      if (!erase)
            update_metadata(lc, rd, asr);

      /* Untruncate trailing whitespace in the name. */
      for (i = 0; i < elmcnt; i++)
            handle_white_space(asr->rt->ent[i].name, UNTRUNCATE);

      /* Compute checksum */
      asr->rt->rchksum = compute_checksum(asr);

      /* Convert back to disk format */
        to_disk(asr, ASR_BLOCK | ASR_TABLE | ASR_EXTTABLE);

      /* Write data */
        ret = write_metadata(lc, handler, rd, -1, erase);
      
      /* Go back to CPU format */
        to_cpu(asr, ASR_BLOCK | ASR_TABLE | ASR_EXTTABLE);
 
      /* Truncate trailing whitespace in the name. */
      for (i = 0; i < elmcnt; i++)
            handle_white_space(asr->rt->ent[i].name, TRUNCATE);

        return ret;
}

/*
 * Check integrity of a RAID set.
 */

/* Retrieve the number of devices that should be in this set. */
static unsigned int device_count(struct raid_dev *rd, void *context)
{
      /* Get the logical drive */
      struct asr_raid_configline *cl = find_logical(META(rd, asr));

      return cl ? cl->raidcnt : 0;
}

/* Check a RAID device */
static int check_rd(struct lib_context *lc, struct raid_set *rs,
                struct raid_dev *rd, void *context)
{
      /* FIXME: Assume non-broken means ok. */
      return rd->type != s_broken;
}

/* Start the recursive RAID set check. */
static int asr_check(struct lib_context *lc, struct raid_set *rs)
{
      return check_raid_set(lc, rs, device_count, NULL, check_rd,
                        NULL, handler);
}

static struct event_handlers asr_event_handlers = {
      .io = event_io,
      .rd = NULL, /* FIXME: no device add/remove event handler yet. */
};

/* Dump a reserved block */
static void dump_rb(struct lib_context *lc, struct asr_reservedblock *rb)
{
      DP("block magic:\t\t0x%X", rb, rb->b0idcode);
      DP("sb0flags:\t\t\t0x%X", rb, rb->sb0flags);
      DP("jbodEnable:\t\t%d", rb, rb->jbodEnable);
      DP("biosInfo:\t\t\t0x%X", rb, rb->biosInfo);
      DP("drivemagic:\t\t0x%X", rb, rb->drivemagic);
      DP("svBlockStorageTid:\t0x%X", rb, rb->svBlockStorageTid);
      DP("svtid:\t\t\t0x%X", rb, rb->svtid);
      DP("resver:\t\t\t%d", rb, rb->resver);
      DP("smagic:\t\t\t0x%X", rb, rb->smagic);
      DP("raidtbl @ sector:\t\t%d", rb, rb->raidtbl);
}

/* Dump a raid config line */
static void dump_cl(struct lib_context *lc, struct asr_raid_configline *cl)
{
      DP("config ID:\t\t0x%X", cl, cl->raidmagic);
      DP("  name:\t\t\t\"%s\"", cl, cl->name);
      DP("  raidcount:\t\t%d", cl, cl->raidcnt);
      DP("  sequence #:\t\t%d", cl, cl->raidseq);
      DP("  level:\t\t\t%d", cl, cl->raidlevel);
      DP("  type:\t\t\t%d", cl, cl->raidtype);
      DP("  state:\t\t\t%d", cl, cl->raidstate);
      DP("  flags:\t\t\t0x%X", cl, cl->flags);
      DP("  refcount:\t\t%d", cl, cl->refcnt);
      DP("  hba:\t\t\t%d", cl, cl->raidhba);
      DP("  channel:\t\t%d", cl, cl->raidchnl);
      DP("  lun:\t\t\t%d", cl, cl->raidlun);
      DP("  id:\t\t\t%d", cl, cl->raidid);
      DP("  offset:\t\t\t%d", cl, cl->loffset);
      DP("  capacity:\t\t%d", cl, cl->lcapcty);
      P("  stripe size:\t\t%d KB",
        cl, cl->strpsize, cl->strpsize * ASR_DISK_BLOCK_SIZE / 1024);
      DP("  BIOS info:\t\t%d", cl, cl->biosInfo);
      DP("  phys/log lun:\t\t%d", cl, cl->lsu);
      DP("  addedDrives:\t\t%d", cl, cl->addedDrives);
      DP("  appSleepRate:\t\t%d", cl, cl->appSleepRate);
      DP("  blockStorageTid:\t%d", cl, cl->blockStorageTid);
      DP("  curAppBlock:\t\t%d", cl, cl->curAppBlock);
      DP("  appBurstCount:\t\t%d", cl, cl->appBurstCount);
}

/* Dump a raid config table */
static void dump_rt(struct lib_context *lc, struct asr_raidtable *rt)
{
      unsigned int i;

      DP("ridcode:\t\t\t0x%X", rt, rt->ridcode);
      DP("table ver:\t\t%d", rt, rt->rversion);
      DP("max configs:\t\t%d", rt, rt->maxelm);
      DP("configs:\t\t\t%d", rt, rt->elmcnt);
      DP("config sz:\t\t%d", rt, rt->elmsize);
      DP("checksum:\t\t\t0x%X", rt, rt->rchksum);
      DP("raid flags:\t\t0x%X", rt, rt->raidFlags);
      DP("timestamp:\t\t0x%X", rt, rt->timestamp);
      P("irocFlags:\t\t%X%s", rt, rt->irocFlags, rt->irocFlags,
        rt->irocFlags & ASR_IF_BOOTABLE ? " (bootable)" : "");
      DP("dirt, rty:\t\t%d", rt, rt->dirty);
      DP("action prio:\t\t%d", rt, rt->actionPriority);
      DP("spareid:\t\t\t%d", rt, rt->spareid);
      DP("sparedrivemagic:\t\t0x%X", rt, rt->sparedrivemagic);
      DP("raidmagic:\t\t0x%X", rt, rt->raidmagic);
      DP("verifydate:\t\t0x%X", rt, rt->verifyDate);
      DP("recreatedate:\t\t0x%X", rt, rt->recreateDate);

      log_print(lc, "\nRAID config table:");
      for (i = 0; i < rt->elmcnt; i++)
            dump_cl(lc, &rt->ent[i]);
}

#ifdef DMRAID_NATIVE_LOG
/*
 * Log native information about the RAID device.
 */
static void asr_log(struct lib_context *lc, struct raid_dev *rd)
{
      struct asr *asr = META(rd, asr);

      log_print(lc, "%s (%s):", rd->di->path, handler);
      dump_rb(lc, &asr->rb);
      dump_rt(lc, asr->rt);
}
#endif

static struct dmraid_format asr_format = {
      .name = HANDLER,
      .descr      = "Adaptec HostRAID ASR",
      .caps = "0,1,10",
      .format = FMT_RAID,
      .read = asr_read,
      .write      = asr_write,
      .group      = asr_group,
      .check      = asr_check,
      .events     = &asr_event_handlers,
#ifdef DMRAID_NATIVE_LOG
      .log  = asr_log,
#endif
};

/* Register this format handler with the format core */
int register_asr(struct lib_context *lc)
{
      return register_format_handler(lc, &asr_format);
}

/*
 * Set up a RAID device from what we've assembled out of the metadata.
 */
static int setup_rd(struct lib_context *lc, struct raid_dev *rd,
                struct dev_info *di, void *meta, union read_info *info)
{
      struct asr *asr = meta;
      struct meta_areas *ma;
      struct asr_raid_configline *cl = this_disk(asr);

      if (!cl)
            LOG_ERR(lc, 0, "%s: Could not find current disk!", handler);            

      /* We need two metadata areas */
      if (!(ma = rd->meta_areas = alloc_meta_areas(lc, rd, handler, 2)))
            return 0;

      /* First area: raid reserved block. */
      ma->offset = ASR_CONFIGOFFSET >> 9;
      ma->size = ASR_DISK_BLOCK_SIZE;
      (ma++)->area = asr;

      /* Second area: raid table. */
      ma->offset = asr->rb.raidtbl;
      ma->size = ASR_DISK_BLOCK_SIZE * 16;
      ma->area = asr->rt;

      /* Now set up the rest of the metadata info */
        rd->di = di;
      rd->fmt = &asr_format;
      rd->status = disk_status(cl);
      rd->type   = type(cl);
      rd->offset = ASR_DATAOFFSET;
      if (!(rd->sectors = cl->lcapcty))
            return log_zero_sectors(lc, di->path, handler);

      return (rd->name = name(lc, asr)) ? 1 : 0;
}

Generated by  Doxygen 1.6.0   Back to index