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

via.c

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

#define     HANDLER     "via"

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

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


static const char *handler = HANDLER;

static int _subset(struct via *via)
{
      return VIA_T_RAID01_MIRROR(via);
}

/* Make up VIA RAID device name suffix from the serial_checksum array. */
static uint32_t sum_serial(struct via *via)
{
      unsigned int i = VIA_MAX_DISKS;
      uint32_t ret = via->array.disk_array_ex; /* FIXME: correct ? */

      while (i--)
            ret += via->serial_checksum[i];

      return ret;
}

static char *_name_suffix(struct via *via)
{
      size_t len;
      uint32_t sum = sum_serial(via);
      char *ret;

      if ((ret = dbg_malloc((len = snprintf(NULL, 0, "%u", sum) + 1))))
            snprintf(ret, len, "%u", sum);

      return ret;
}

/* Make up RAID device name. */
static size_t _name(struct lib_context *lc, struct via *via, char *str,
                 size_t len, char *suffix, unsigned int subset)
{
      return snprintf(str, len,
                  subset ? "via_%s-%u" : "via_%s", suffix, _subset(via));
}

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

      if (!(suffix = _name_suffix(via)))
            return NULL;

      subset = subset && (VIA_RAID_TYPE(via) == VIA_T_RAID01);
      if ((ret = dbg_malloc((len = _name(lc, via, NULL, 0,
                                 suffix, subset) + 1)))) {
            _name(lc, via, ret, len, suffix, subset);
            mk_alpha(lc, ret + HANDLER_LEN, len - HANDLER_LEN -
                   (subset ? 3 : 1));
      } else
            log_alloc_err(lc, handler);

      dbg_free(suffix);

      return ret;
}

/*
 * Retrieve status of device.
 * FIXME: is this sufficient to cover all state ?
 */
static enum status status(struct via *via)
{
      if (via->array.disk.tolerance)
            return s_broken;

      return via->array.disk.in_disk_array ? s_ok : s_undef;
}

/* Neutralize disk type using generic metadata type mapping function */
static enum type type(struct via *via)
{
      /* Mapping of via types to generic types */
      static struct types types[] = {
            { VIA_T_SPAN,  t_linear },
            { VIA_T_RAID0, t_raid0 },
            { VIA_T_RAID1, t_raid1 },
            { VIA_T_RAID01, t_raid0 },
              { 0, t_undef}
      };

      return rd_type(types, (unsigned int) VIA_RAID_TYPE(via));
}

/*
 * Read a VIA RAID device
 */
/* Endianess conversion */
#if   BYTE_ORDER == LITTLE_ENDIAN
#  define   to_cpu      NULL
#else
static void to_cpu(void *meta)
{
      struct via *via = meta;
      unsigned int i = VIA_MAX_DISKS;

      CVT16(via->signature);
      CVT16(via->array.disk.bootable);
      CVT32(via->array.capacity_low);
      CVT32(via->array.capacity_high);
      CVT32(via->array.serial_checksum);

      while (i--)
            CVT32(via->serial_checksum[i]);

}
#endif

/* 8 bit checksum on first 50 bytes of metadata. */
static uint8_t checksum(struct via *via)
{
      uint8_t i = 50, sum = 0;
      
      while (i--)
            sum += ((uint8_t*) via)[i];

      return sum == via->checksum;
}

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

      if (via->signature != VIA_SIGNATURE)
            return 0;

      if (!checksum(via))
            LOG_ERR(lc, 0, "%s: invalid checksum on %s", handler, di->path);

      if (via->version_number > 1)
            log_info(lc, "%s: version %u; format handler specified for "
                   "version 0+1 only", handler, via->version_number);
      
      return 1;
}

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 *via_read(struct lib_context *lc, struct dev_info *di)
{
      return read_raid_dev(lc, di, NULL,
                       sizeof(struct via), VIA_CONFIGOFFSET,
                       to_cpu, is_via, NULL, setup_rd, handler);
}

/* Decide about ordering sequence of RAID device. */
static int dev_sort(struct list_head *pos, struct list_head *new)
{
      struct via *p = META(RD(pos), via);
      struct via *n = META(RD(new), via);

      switch (VIA_RAID_TYPE(p)) {
      case VIA_T_RAID1:
            return VIA_T_RAID1_SOURCE(n);

      default: /* span, RAID0 + RAID01 */
            return VIA_T_RAID_INDEX(n) < VIA_T_RAID_INDEX(p);
      }
}

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

static void super_created(struct raid_set *ss, void *private)
{
      ss->type = t_raid1;
      ss->stride = VIA_STRIDE(META(private, via));
}

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

      if (!init_raid_set(lc, rs, rd, VIA_STRIDE(via),
                     VIA_RAID_TYPE(via), handler))
            return 0;

      list_add_sorted(lc, &rs->devs, &rd->devs, dev_sort);

      switch (VIA_RAID_TYPE(via)) {
      case VIA_T_SPAN:
      case VIA_T_RAID0:
      case VIA_T_RAID1:
            if (!find_set(lc, NULL, rs->name, FIND_TOP))
                  list_add_tail(&rs->list, LC_RS(lc));

            break;

      case VIA_T_RAID01:
            /* Sort RAID disk into appropriate subset. */
            if (!(*ss = join_superset(lc, name, super_created,
                                set_sort, rs, rd)))
                  return 0;
      }

      return 1;
}

/* Add a VIA RAID device to a set */
static struct raid_set *via_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;
}

static int via_write(struct lib_context *lc, struct raid_dev *rd, int erase)
{
      int ret;
#if   BYTE_ORDER != LITTLE_ENDIAN
      struct via *via = META(rd, via);

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

      return ret;
}

/*
 * Check a VIA RAID set.
 *
 * FIXME: more sanity checks.
 */
/* Figure total number of disks depending on RAID type. */
static unsigned int devices(struct raid_dev *rd, void *context)
{
      struct via *via = META(rd, via);

      return VIA_RAID_TYPE(via) == VIA_T_RAID1 ? 2 : VIA_RAID_DISKS(via);
}

static int check_rd(struct lib_context *lc, struct raid_set *rs,
                struct raid_dev *rd, void *context)
{
      struct via *via = META(rd, via);

      log_dbg(lc, "checking %s device \"%s\"", handler, rd->di->path);
      if (VIA_ARRAY_INDEX(via) != VIA_ARRAY_INDEX(via))
            LOG_ERR(lc, 0, "%s: array index wrong on %s for set \"%s\"",
                  handler, rd->di->path, rs->name);

      if (VIA_RAID_TYPE(via) != VIA_RAID_TYPE(via))
            LOG_ERR(lc, 0, "%s: RAID type wrong on %s for set \"%s\"",
                  handler, rd->di->path, rs->name);

      return 1;
}

static int via_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 via *via = META(rd, via);

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

      via->array.disk.tolerance = 1;

      return 1;
}

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


#ifdef DMRAID_NATIVE_LOG
/*
 * Log native information about the RAID device.
 */
static void via_log(struct lib_context *lc, struct raid_dev *rd)
{
      unsigned int i;
      struct via *via = META(rd, via);

      log_print(lc, "%s (%s):", rd->di->path, handler);
      DP("signature: 0x%x", via, via->signature);
      DP("version_number: %u", via, via->version_number);
      P("array.disk.bootable: %u", via, via->array.disk,
        via->array.disk.bootable);
      P("array.disk.enable_enhanced: %u", via, via->array.disk,
        via->array.disk.enable_enhanced);
      P("array.disk.in_disk_array: %u", via, via->array.disk,
        via->array.disk.in_disk_array);
      P("array.disk.raid_type: %u", via, via->array.disk,
        VIA_RAID_TYPE(via));
      P("array.disk.array_index: %u", via, via->array.disk,
        VIA_ARRAY_INDEX(via));

      P("array.disk.raid_type_info: %u", via, via->array.disk,
        via->array.disk.raid_type_info);
      P("array.disk.raid_type_info(INDEX): %u", via, via->array.disk,
        VIA_T_RAID_INDEX(via));
      P("array.disk.raid_type_info(MIRROR): %u", via, via->array.disk,
        VIA_T_RAID01_MIRROR(via));
      P("array.disk.raid_type_info(DIRTY): %u", via, via->array.disk,
        VIA_T_RAID01_DIRTY(via));

      P("array.disk.tolerance: %u", via, via->array.disk,
        via->array.disk.tolerance);
      DP("array.disk_array_ex: 0x%x", via, via->array.disk_array_ex);
      DP("array.capacity_low: %u", via, via->array.capacity_low);
      DP("array.capacity_high: %u", via, via->array.capacity_high);
      DP("array.serial_checksum: %u", via, via->array.serial_checksum);

      for (i = 0; i < VIA_MAX_DISKS; i++)
            P2("serial_checksum[%u]: %u", via, i, via->serial_checksum[i]);

      DP("checksum: %u", via, via->checksum);
}
#endif

static struct dmraid_format via_format = {
      .name = HANDLER,
      .descr      = "VIA Software RAID",
      .caps = "S,0,1,10",
      .format = FMT_RAID,
      .read = via_read,
      .write      = via_write,
      .group      = via_group,
      .check      = via_check,
      .events     = &via_event_handlers,
#ifdef DMRAID_NATIVE_LOG
      .log  = via_log,
#endif
};

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

static int setup_rd(struct lib_context *lc, struct raid_dev *rd,
                struct dev_info *di, void *meta, union read_info *info)
{
      struct via *via = meta;

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

      rd->meta_areas->offset = VIA_CONFIGOFFSET >> 9;
      rd->meta_areas->size = sizeof(*via);
      rd->meta_areas->area = (void*) via;

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

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

      rd->offset = VIA_DATAOFFSET;
      if (!(rd->sectors = rd->meta_areas->offset))
            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