Logo Search packages:      
Sourcecode: dmraid version File versions

hpt37x.c

/*
 * Highpoint 37X ATARAID series metadata format handler.
 *
 * Copyright (C) 2004,2005  Heinz Mauelshagen, Red Hat GmbH.
 *                          All rights reserved.
 *
 * See file LICENSE at the top of this source tree for license information.
 */

/*
 * hpt37x_read(), hpt37x_group() and group_rd() profited from
 * Carl-Daniel Hailfinger's raiddetect code.
 */
#define     HANDLER "hpt37x"

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

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

static const char *handler = HANDLER;

/* Make up RAID set name from magic_[01] numbers */
/* FIXME: better name ? */
static size_t _name(struct hpt37x *hpt, char *str, size_t len,
                unsigned int subset)
{
      const char *fmt;

      if (hpt->magic_0)
            fmt = (subset &&
                   (hpt->type == HPT37X_T_RAID01_RAID0 ||
                    hpt->type == HPT37X_T_RAID01_RAID1)) ?
                   "hpt37x_%u-%u" : "hpt37x_%u";
      else
            fmt = "hpt37x_SPARE";

      /* FIXME: hpt->order not zero-based. */
      return snprintf(str, len, fmt,
                  hpt->magic_1 ? hpt->magic_1 : hpt->magic_0, hpt->order);
}

static char *name(struct lib_context *lc, struct raid_dev *rd,
              unsigned int subset)
{
      size_t len;
      char *ret;
      struct hpt37x *hpt = META(rd, hpt37x);

      if ((ret = dbg_malloc((len = _name(hpt, NULL, 0, subset) + 1)))) {
            _name(hpt, ret, len, subset);
            mk_alpha(lc, ret + HANDLER_LEN, len - HANDLER_LEN -
                   (strrchr(ret, '-') ? 3 : 1));
      } else
            log_alloc_err(lc, handler);

      return ret;
}

/*
 * Retrieve status of device.
 * FIXME: is this sufficient to cover all state ?
 */
static enum status status(struct hpt37x *hpt)
{
      return hpt->magic == HPT37X_MAGIC_BAD ? s_broken : s_ok;
}

/* Neutralize disk type. */
static enum type type(struct hpt37x *hpt)
{
      /* Mapping of HPT 37X types to generic types. */
      static struct types types[] = {
            { HPT37X_T_SINGLEDISK, t_linear},
            { HPT37X_T_SPAN, t_linear},
            { HPT37X_T_RAID0, t_raid0},
            { HPT37X_T_RAID1, t_raid1},
            { HPT37X_T_RAID01_RAID0, t_raid0},
            { HPT37X_T_RAID01_RAID1, t_raid1},
            /* FIXME: support RAID 3+5 */
            { 0, t_undef},
      };

      return hpt->magic_0 ?
             rd_type(types, (unsigned int) hpt->type) : t_spare;
}

/* Decide about ordering sequence of RAID device. */
static int dev_sort(struct list_head *pos, struct list_head *new)
{
      return (META(RD(new), hpt37x))->disk_number <
             (META(RD(pos), hpt37x))->disk_number;
}

/* Decide about ordering sequence of RAID subset. */
static int set_sort(struct list_head *pos, struct list_head *new)
{
      return (META(RD_RS(RS(new)), hpt37x))->order <
             (META(RD_RS(RS(pos)), hpt37x))->order;
}

/* Magic check. */
static int check_magic(void *meta)
{
      struct hpt37x *hpt = meta;

      return (hpt->magic == HPT37X_MAGIC_OK ||
            hpt->magic == HPT37X_MAGIC_BAD) &&
            hpt->disk_number < 8;
}

/*
 * Read a Highpoint 37X RAID device.
 */
/* Endianess conversion. */
#if   BYTE_ORDER == LITTLE_ENDIAN
#  define   to_cpu      NULL
#else
static void to_cpu(void *meta)
{
      struct hpt37x *hpt = meta;

      CVT32(hpt->magic);
      CVT32(hpt->magic_0);
      CVT32(hpt->magic_1);
      CVT32(hpt->order);
      CVT32(hpt->total_secs);
      CVT32(hpt->disk_mode);
      CVT32(hpt->boot_mode);

      /* Only convert error log entries in case we discover proper magic */
      if (check_magic(meta)) {
            struct hpt37x_errorlog *l;

            for (l = hpt->errorlog;
                 l < hpt->errorlog +
                   min(hpt->error_log_entries, HPT37X_MAX_ERRORLOG);
                 l++) {
                  CVT32(l->timestamp);
                  CVT32(l->lba);
            }
      }
}
#endif

/* Use magic check to tell, if this is Highpoint 37x */
static int is_hpt37x(struct lib_context *lc, struct dev_info *di, void *meta)
{
      return check_magic(meta);
}

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 *hpt37x_read(struct lib_context *lc, struct dev_info *di)
{
      return read_raid_dev(lc, di, NULL,
                       sizeof(struct hpt37x), HPT37X_CONFIGOFFSET,
                       to_cpu, is_hpt37x, NULL, setup_rd, handler);
}

/*
 * Write a Highpoint 37X RAID device.
 */
static int hpt37x_write(struct lib_context *lc,
                  struct raid_dev *rd, int erase)
{
      int ret;
#if   BYTE_ORDER != LITTLE_ENDIAN
      struct hpt37x *hpt = META(rd, hpt37x);

      to_disk(hpt);
#endif
      ret = write_metadata(lc, handler, rd, -1, erase);
#if   BYTE_ORDER != LITTLE_ENDIAN
      to_cpu(hpt);
#endif

      return ret;
}

/*
 * Group the RAID disk into a set.
 *
 * Check device hierarchy and create sub sets appropriately.
 *
 */
static unsigned int stride(struct hpt37x *hpt)
{
      return hpt->raid0_shift ? 1 << hpt->raid0_shift : 0;
}

static int mismatch(struct lib_context *lc, struct raid_dev *rd, char magic)
{
      LOG_ERR(lc, 0, "%s: magic_%c mismatch on %s",
            handler, magic, rd->di->path);
}

static void super_created(struct raid_set *ss, void *private)
{
      struct hpt37x *hpt = META(private, hpt37x);

      ss->type   = hpt->type == HPT37X_T_RAID01_RAID0 ? t_raid1 : t_raid0;
      ss->stride = stride(hpt);
}

/* FIXME: handle spares in mirrors and check that types are correct. */
static int group_rd(struct lib_context *lc, struct raid_set *rs,
                struct raid_set **ss, struct raid_dev *rd)
{
      struct hpt37x *h, *hpt = META(rd, hpt37x);

      if (!init_raid_set(lc, rs, rd, stride(hpt), hpt->type, handler))
            return 0;

      list_add_sorted(lc, &rs->devs, &rd->devs, dev_sort);
      h = DEVS(rs) ? META(RD_RS(rs), hpt37x) : NULL;

      switch (hpt->type) {
      case HPT37X_T_SINGLEDISK:
      case HPT37X_T_SPAN:
      case HPT37X_T_RAID0:
      case HPT37X_T_RAID1:
            if (h && h->magic_0 != hpt->magic_0)
                  return mismatch(lc, rd, '0');

            if (!find_set(lc, NULL, rs->name, FIND_TOP))
                  list_add_tail(&rs->list, LC_RS(lc));

            break;

      case HPT37X_T_RAID01_RAID0:
      case HPT37X_T_RAID01_RAID1:
            if (h && h->magic_1 != hpt->magic_1)
                  return mismatch(lc, rd, '1');

            if (!(*ss = join_superset(lc, name, super_created,
                                set_sort, rs, rd)))
                  return 0;
      }

      return 1;
}

/*
 * Add a Highpoint RAID device to a set.
 */
static struct raid_set *hpt37x_group(struct lib_context *lc,
                               struct raid_dev *rd)
{
      struct raid_set *rs, *ss = NULL;

      if (T_SPARE(rd))
            return NULL;

      if ((rs = find_or_alloc_raid_set(lc, rd->name, FIND_ALL, rd,
                               NO_LIST, NO_CREATE, NO_CREATE_ARG)))
            return group_rd(lc, rs, &ss, rd) ? (ss ? ss : rs) : NULL;

      return NULL;
}

/*
 * Check a Highpoint 37X RAID set.
 *
 * FIXME: more sanity checks.
 */
static unsigned int devices(struct raid_dev *rd, void *context)
{
      return (META(rd, hpt37x))->raid_disks;
}

static int check_rd(struct lib_context *lc, struct raid_set *rs,
                struct raid_dev *rd, void *context)
{
      /*
       * FIXME: raid_disks member wrong ?
       *      (eg, Peter Jonas RAID1 metadata, 2 disks and raid_disks = 1)
       */
      return T_RAID1(rd);
}

static int hpt37x_check(struct lib_context *lc, struct raid_set *rs)
{
      return check_raid_set(lc, rs, devices, NULL, check_rd, NULL, handler);
}

/*
 * 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 hpt37x *hpt = META(rd, hpt37x);

      /* Avoid write trashing. */
      if (status(hpt) & s_broken)
            return 0;

      hpt->magic = HPT37X_MAGIC_BAD;

      return 1;
}

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

#ifdef DMRAID_NATIVE_LOG
/*
 * Log native information about an HPT37X RAID device.
 */
static void hpt37x_log(struct lib_context *lc, struct raid_dev *rd)
{
      struct hpt37x *hpt = META(rd, hpt37x);
      struct hpt37x_errorlog *el;

      log_print(lc, "%s (%s):", rd->di->path, handler);
      DP("magic: 0x%x", hpt, hpt->magic);
      DP("magic_0: 0x%x", hpt, hpt->magic_0);
      DP("magic_1: 0x%x", hpt, hpt->magic_1);
      DP("order: %u", hpt, hpt->order);
      DP("raid_disks: %u", hpt, hpt->raid_disks);
      DP("raid0_shift: %u", hpt, hpt->raid0_shift);
      DP("type: %u", hpt, hpt->type);
      DP("disk_number: %u", hpt, hpt->disk_number);
      DP("total_secs: %u", hpt, hpt->total_secs);
      DP("disk_mode: 0x%x", hpt, hpt->disk_mode);
      DP("boot_mode: 0x%x", hpt, hpt->boot_mode);
      DP("boot_disk: %u", hpt, hpt->boot_disk);
      DP("boot_protect: %u", hpt, hpt->boot_protect);
      DP("error_log_entries: %u", hpt, hpt->error_log_entries);
      DP("error_log_index: %u", hpt, hpt->error_log_index);
      if (hpt->error_log_entries)
            log_print(lc, "error_log:");

      for (el = hpt->errorlog; el < hpt->errorlog + 32; el++) {
            if (!el->timestamp)
                  break;

            DP("timestamp: %u", hpt, el->timestamp);
            DP("reason: %u", hpt, el->reason);
            DP("disk: %u", hpt, el->disk);
            DP("status: %u", hpt, el->status);
            DP("sectors: %u", hpt, el->sectors);
            DP("lba: %u", hpt, el->lba);
       };
}
#endif

static struct dmraid_format hpt37x_format = {
      .name = HANDLER,
      .descr      = "Highpoint HPT37X",
      .caps = "S,0,1,10,01",
      .format = FMT_RAID,
      .read = hpt37x_read,
      .write      = hpt37x_write,
      .group      = hpt37x_group,
      .check      = hpt37x_check,
      .events     = &hpt37x_event_handlers,
#ifdef DMRAID_NATIVE_LOG
      .log  = hpt37x_log,
#endif
};

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

/* Calculate RAID device size in sectors depending on RAID type. */
static uint64_t sectors(struct raid_dev *rd, struct hpt37x *hpt)
{
      uint64_t ret = 0;
      struct dev_info *di = rd->di;

      switch (rd->type) {
      case t_raid0:
            ret = hpt->total_secs / (hpt->raid_disks ? hpt->raid_disks : 1);
            break;

      case t_raid1:
            ret = hpt->total_secs;
            break;

      default:
            ret = di->sectors;
      }

      /* Subtract offset sectors on drives > 0. */
      return ret - rd->offset;
}

/* Derive the RAID device contents from the Highpoint ones. */
static int setup_rd(struct lib_context *lc, struct raid_dev *rd,
                struct dev_info *di, void *meta, union read_info *info)
{
      struct hpt37x *hpt = meta;

      if (!(rd->meta_areas = alloc_meta_areas(lc, rd, handler, 1)))
            return 0;

      rd->meta_areas->offset = HPT37X_CONFIGOFFSET >> 9;
      rd->meta_areas->size = sizeof(*hpt);
      rd->meta_areas->area = (void*) hpt;

      rd->di  = di;
      rd->fmt = &hpt37x_format;

      rd->status = status(hpt);
      rd->type   = type(hpt);

      /* Data offset from start of device; first device is special */
      rd->offset  = hpt->disk_number ? HPT37X_DATAOFFSET : 0;
        if (!(rd->sectors = sectors(rd, hpt)))
            return log_zero_sectors(lc, di->path, handler);

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

Generated by  Doxygen 1.6.0   Back to index