]> git.ipfire.org Git - thirdparty/mdadm.git/blobdiff - super-intel.c
Remove st->text_version in favour of info->text_version
[thirdparty/mdadm.git] / super-intel.c
index fd1c3dcd8df5954ed69c6d1a3f88a8fc33cd40d4..69aef65301d06f288f3ad7717f126f1c69a328f5 100644 (file)
@@ -18,6 +18,7 @@
  */
 
 #include "mdadm.h"
+#include "mdmon.h"
 #include <values.h>
 #include <scsi/sg.h>
 #include <ctype.h>
@@ -124,6 +125,7 @@ struct intel_super {
                struct imsm_super *mpb;
                void *buf;
        };
+       int updates_pending;
        struct dl {
                struct dl *next;
                int index;
@@ -238,7 +240,6 @@ static __u32 get_imsm_disk_idx(struct imsm_map *map, int slot)
        return __le32_to_cpu(*ord_tbl & ~(0xff << 24));
 }
 
-#ifndef MDASSEMBLE
 static int get_imsm_raid_level(struct imsm_map *map)
 {
        if (map->raid_level == 1) {
@@ -251,6 +252,7 @@ static int get_imsm_raid_level(struct imsm_map *map)
        return map->raid_level;
 }
 
+#ifndef MDASSEMBLE
 static void print_imsm_dev(struct imsm_dev *dev, int index)
 {
        __u64 sz;
@@ -370,12 +372,9 @@ static void uuid_from_super_imsm(struct supertype *st, int uuid[4])
        printf("%s\n", __FUNCTION__);
 }
 
-static void getinfo_super_imsm(struct supertype *st, struct mdinfo *info)
+static void
+get_imsm_numerical_version(struct imsm_super *mpb, int *m, int *p)
 {
-       struct intel_super *super = st->sb;
-       struct imsm_super *mpb = super->mpb;
-       struct imsm_disk *disk;
-       __u32 s;
        __u8 *v = get_imsm_version(mpb);
        __u8 *end = mpb->sig + MAX_SIGNATURE_LENGTH;
        char major[] = { 0, 0, 0 };
@@ -395,9 +394,21 @@ static void getinfo_super_imsm(struct supertype *st, struct mdinfo *info)
                v++;
        }
 
+       *m = strtol(minor, NULL, 0);
+       *p = strtol(patch, NULL, 0);
+}
+
+static void getinfo_super_imsm(struct supertype *st, struct mdinfo *info)
+{
+       struct intel_super *super = st->sb;
+       struct imsm_super *mpb = super->mpb;
+       struct imsm_disk *disk;
+       __u32 s;
+       int i, j;
+
        info->array.major_version = 2000;
-       info->array.minor_version = strtol(minor, NULL, 0);
-       info->array.patch_version = strtol(patch, NULL, 0);
+       get_imsm_numerical_version(mpb, &info->array.minor_version,
+                                  &info->array.patch_version);
        info->array.raid_disks    = mpb->num_disks;
        info->array.level         = LEVEL_CONTAINER;
        info->array.layout        = 0;
@@ -432,11 +443,17 @@ static void getinfo_super_imsm(struct supertype *st, struct mdinfo *info)
        info->disk.state |= s & FAILED_DISK ? (1 << MD_DISK_FAULTY) : 0;
        info->disk.state |= s & USABLE_DISK ? (1 << MD_DISK_SYNC) : 0;
        info->reshape_active = 0;
+
+       strcpy(info->text_version, "imsm");
 }
 
 static void getinfo_super_imsm_raid(struct supertype *st, struct mdinfo *info)
 {
        printf("%s\n", __FUNCTION__);
+
+       sprintf(info->text_version, "/%s/%d",
+               devnum2devname(st->container_dev),
+               info->container_member); // FIXME is this even set here?
 }
 
 static int update_super_imsm(struct supertype *st, struct mdinfo *info,
@@ -856,7 +873,7 @@ static int load_super_imsm(struct supertype *st, int fd, char *devname)
        int rv;
 
 #ifndef MDASSEMBLE
-       if (load_super_imsm_all(st, fd, &st->sb, devname, 0) == 0)
+       if (load_super_imsm_all(st, fd, &st->sb, devname, 1) == 0)
                return 0;
 #endif
 
@@ -990,13 +1007,332 @@ static int validate_geometry_imsm_raid(struct supertype *st, int level,
        return 0;
 }
 
+static struct mdinfo *container_content_imsm(struct supertype *st)
+{
+       /* Given a container loaded by load_super_imsm_all,
+        * extract information about all the arrays into
+        * an mdinfo tree.
+        *
+        * For each imsm_dev create an mdinfo, fill it in,
+        *  then look for matching devices in super->disks
+        *  and create appropriate device mdinfo.
+        */
+       struct intel_super *super = st->sb;
+       struct imsm_super *mpb = super->mpb;
+       struct mdinfo *rest = NULL;
+       int i;
 
+       for (i = 0; i < mpb->num_raid_devs; i++) {
+               struct imsm_dev *dev = get_imsm_dev(mpb, i);
+               struct imsm_vol *vol = &dev->vol;
+               struct imsm_map *map = vol->map;
+               struct mdinfo *this;
+               __u64 sz;
+               int slot;
+
+               this = malloc(sizeof(*this));
+               memset(this, 0, sizeof(*this));
+               this->next = rest;
+               rest = this;
+
+               this->array.major_version = 2000;
+               get_imsm_numerical_version(mpb, &this->array.minor_version,
+                                          &this->array.patch_version);
+               this->array.level = get_imsm_raid_level(map);
+               this->array.raid_disks = map->num_members;
+               switch(this->array.level) {
+               case 0:
+               case 1:
+                       this->array.layout = 0;
+                       break;
+               case 5:
+               case 6:
+                       this->array.layout = ALGORITHM_LEFT_SYMMETRIC;
+                       break;
+               case 10:
+                       this->array.layout = 0x102; //FIXME is this correct?
+                       break;
+               default:
+                       this->array.layout = -1; // FIXME
+               }
+               this->array.md_minor = -1;
+               this->array.ctime = 0;
+               this->array.utime = 0;
+               this->array.chunk_size = __le16_to_cpu(map->blocks_per_strip) << 9;
+               this->array.state = !vol->dirty;
+               this->container_member = i;
+               if (map->map_state == IMSM_T_STATE_UNINITIALIZED || dev->vol.dirty)
+                       this->resync_start = 0;
+               else
+                       this->resync_start = ~0ULL;
+
+               strncpy(this->name, (char *) dev->volume, MAX_RAID_SERIAL_LEN);
+               this->name[MAX_RAID_SERIAL_LEN] = 0;
+
+               sprintf(this->text_version, "/%s/%d",
+                       devnum2devname(st->container_dev),
+                       this->container_member);
+
+               memset(this->uuid, 0, sizeof(this->uuid));
+
+               sz = __le32_to_cpu(dev->size_high);
+               sz <<= 32;
+               sz += __le32_to_cpu(dev->size_low);
+               this->component_size = sz;
+               this->array.size = this->component_size / 2;
+
+               for (slot = 0 ; slot <  map->num_members; slot++) {
+                       struct imsm_disk *disk;
+                       struct mdinfo *info_d;
+                       struct dl *d;
+                       int idx;
+                       __u32 s;
+
+                       idx = __le32_to_cpu(map->disk_ord_tbl[slot] & ~(0xff << 24));
+                       for (d = super->disks; d ; d = d->next)
+                               if (d->index == idx)
+                                        break;
+
+                       if (d == NULL)
+                               break; /* shouldn't this be continue ?? */
+
+                       info_d = malloc(sizeof(*info_d));
+                       if (!info_d)
+                               break; /* ditto ?? */
+                       memset(info_d, 0, sizeof(*info_d));
+                       info_d->next = this->devs;
+                       this->devs = info_d;
+
+                       disk = get_imsm_disk(mpb, idx);
+                       s = __le32_to_cpu(disk->status);
+
+                       info_d->disk.number = d->index;
+                       info_d->disk.major = d->major;
+                       info_d->disk.minor = d->minor;
+                       info_d->disk.raid_disk = slot;
+                       info_d->disk.state  = s & CONFIGURED_DISK ? (1 << MD_DISK_ACTIVE) : 0;
+                       info_d->disk.state |= s & FAILED_DISK ? (1 << MD_DISK_FAULTY) : 0;
+                       info_d->disk.state |= s & USABLE_DISK ? (1 << MD_DISK_SYNC) : 0;
+
+                       this->array.working_disks++;
+
+                       info_d->events = __le32_to_cpu(mpb->generation_num);
+                       info_d->data_offset = __le32_to_cpu(map->pba_of_lba0);
+                       info_d->component_size = __le32_to_cpu(map->blocks_per_member);
+                       if (d->devname)
+                               strcpy(info_d->name, d->devname);
+               }
+       }
 
-static struct mdinfo *container_content_imsm(struct supertype *st)
+       return rest;
+}
+
+
+static int imsm_open_new(struct supertype *c, struct active_array *a, int inst)
 {
-       printf("%s\n", __FUNCTION__);
+       fprintf(stderr, "imsm: open_new %d\n", inst);
+       return 0;
+}
 
-       return NULL;
+static void imsm_set_array_state(struct active_array *a, int consistent)
+{
+       int inst = a->info.container_member;
+       struct intel_super *super = a->container->sb;
+       struct imsm_dev *dev = get_imsm_dev(super->mpb, inst);
+       int dirty = !consistent || (a->resync_start != ~0ULL);
+
+       if (dev->vol.dirty != dirty) {
+               fprintf(stderr, "imsm: mark '%s' (%llu)\n",
+                       dirty?"dirty":"clean", a->resync_start);
+
+               dev->vol.dirty = dirty;
+               super->updates_pending++;
+       }
+}
+
+static __u8 imsm_check_degraded(struct imsm_super *mpb, int n, int failed)
+{
+       struct imsm_dev *dev = get_imsm_dev(mpb, n);
+       struct imsm_map *map = dev->vol.map;
+
+       if (!failed)
+               return map->map_state;
+
+       switch (get_imsm_raid_level(map)) {
+       case 0:
+               return IMSM_T_STATE_FAILED;
+               break;
+       case 1:
+               if (failed < map->num_members)
+                       return IMSM_T_STATE_DEGRADED;
+               else
+                       return IMSM_T_STATE_FAILED;
+               break;
+       case 10:
+       {
+               /**
+                * check to see if any mirrors have failed,
+                * otherwise we are degraded
+                */
+               int device_per_mirror = 2; /* FIXME is this always the case?
+                                           * and are they always adjacent?
+                                           */
+               int failed = 0;
+               int i;
+
+               for (i = 0; i < map->num_members; i++) {
+                       int idx = get_imsm_disk_idx(map, i);
+                       struct imsm_disk *disk = get_imsm_disk(mpb, idx);
+
+                       if (__le32_to_cpu(disk->status) & FAILED_DISK)
+                               failed++;
+
+                       if (failed >= device_per_mirror)
+                               return IMSM_T_STATE_FAILED;
+
+                       /* reset 'failed' for next mirror set */
+                       if (!((i + 1) % device_per_mirror))
+                               failed = 0;
+               }
+
+               return IMSM_T_STATE_DEGRADED;
+       }
+       case 5:
+               if (failed < 2)
+                       return IMSM_T_STATE_DEGRADED;
+               else
+                       return IMSM_T_STATE_FAILED;
+               break;
+       default:
+               break;
+       }
+
+       return map->map_state;
+}
+
+static int imsm_count_failed(struct imsm_super *mpb, struct imsm_map *map)
+{
+       int i;
+       int failed = 0;
+       struct imsm_disk *disk;
+
+       for (i = 0; i < map->num_members; i++) {
+               int idx = get_imsm_disk_idx(map, i);
+
+               disk = get_imsm_disk(mpb, idx);
+               if (__le32_to_cpu(disk->status) & FAILED_DISK)
+                       failed++;
+       }
+
+       return failed;
+}
+
+static void imsm_set_disk(struct active_array *a, int n, int state)
+{
+       int inst = a->info.container_member;
+       struct intel_super *super = a->container->sb;
+       struct imsm_dev *dev = get_imsm_dev(super->mpb, inst);
+       struct imsm_map *map = dev->vol.map;
+       struct imsm_disk *disk;
+       __u32 status;
+       int failed = 0;
+       int new_failure = 0;
+
+       if (n > map->num_members)
+               fprintf(stderr, "imsm: set_disk %d out of range 0..%d\n",
+                       n, map->num_members - 1);
+
+       if (n < 0)
+               return;
+
+       fprintf(stderr, "imsm: set_disk %d:%x\n", n, state);
+
+       disk = get_imsm_disk(super->mpb, get_imsm_disk_idx(map, n));
+
+       /* check if we have seen this failure before */
+       status = __le32_to_cpu(disk->status);
+       if ((state & DS_FAULTY) && !(status & FAILED_DISK)) {
+               status |= FAILED_DISK;
+               disk->status = __cpu_to_le32(status);
+               new_failure = 1;
+       }
+
+       /**
+        * the number of failures have changed, count up 'failed' to determine
+        * degraded / failed status
+        */
+       if (new_failure && map->map_state != IMSM_T_STATE_FAILED)
+               failed = imsm_count_failed(super->mpb, map);
+
+       if (failed)
+               map->map_state = imsm_check_degraded(super->mpb, inst, failed);
+
+       if (new_failure)
+               super->updates_pending++;
+}
+
+static int store_imsm_mpb(int fd, struct intel_super *super)
+{
+       struct imsm_super *mpb = super->mpb;
+       __u32 mpb_size = __le32_to_cpu(mpb->mpb_size);
+       unsigned long long dsize;
+       unsigned long long sectors;
+
+       get_dev_size(fd, NULL, &dsize);
+
+       /* first block is stored on second to last sector of the disk */
+       if (lseek64(fd, dsize - (512 * 2), SEEK_SET) < 0)
+               return 1;
+
+       if (write(fd, super->buf, 512) != 512)
+               return 1;
+
+       if (mpb_size <= 512)
+               return 0;
+
+       /* -1 because we already wrote a sector */
+       sectors = mpb_sectors(mpb) - 1;
+
+       /* write the extended mpb to the sectors preceeding the anchor */
+       if (lseek64(fd, dsize - (512 * (2 + sectors)), SEEK_SET) < 0)
+               return 1;
+
+       if (write(fd, super->buf + 512, mpb_size - 512) != mpb_size - 512)
+               return 1;
+
+       fsync(fd);
+
+       return 0;
+}
+
+static void imsm_sync_metadata(struct active_array *a)
+{
+       struct intel_super *super = a->container->sb;
+       struct imsm_super *mpb = super->mpb;
+       struct dl *d;
+       __u32 generation;
+       __u32 sum;
+
+       if (!super->updates_pending)
+               return;
+
+       fprintf(stderr, "imsm: sync_metadata\n");
+
+       /* 'generation' is incremented everytime the metadata is written */
+       generation = __le32_to_cpu(mpb->generation_num);
+       generation++;
+       mpb->generation_num = __cpu_to_le32(generation);
+
+       /* recalculate checksum */
+       sum = gen_imsm_checksum(mpb);
+       mpb->check_sum = __cpu_to_le32(sum);
+
+       for (d = super->disks; d ; d = d->next)
+               if (store_imsm_mpb(d->fd, super))
+                       fprintf(stderr, "%s: failed for device %d:%d %s\n",
+                               __func__, d->major, d->minor, strerror(errno));
+
+       super->updates_pending = 0;
 }
 
 struct superswitch super_imsm = {
@@ -1026,7 +1362,13 @@ struct superswitch super_imsm = {
        .major          = 2000,
        .swapuuid       = 0,
        .external       = 1,
-       .text_version   = "imsm",
+
+/* for mdmon */
+       .open_new       = imsm_open_new,
+       .load_super     = load_super_imsm,
+       .set_array_state= imsm_set_array_state,
+       .set_disk       = imsm_set_disk,
+       .sync_metadata  = imsm_sync_metadata,
 };
 
 /* super_imsm_container is set by validate_geometry_imsm when given a
@@ -1056,7 +1398,6 @@ struct superswitch super_imsm_container = {
        .major          = 2000,
        .swapuuid       = 0,
        .external       = 1,
-       .text_version   = "imsm",
 };
 
 struct superswitch super_imsm_raid = {
@@ -1076,5 +1417,4 @@ struct superswitch super_imsm_raid = {
        .major          = 2001,
        .swapuuid       = 0,
        .external       = 2,
-       .text_version   = "imsm",
 };