+ if (d == NULL)
+ skip = 1;
+
+ s = d ? d->disk.status : 0;
+ if (s & FAILED_DISK)
+ skip = 1;
+ if (!(s & USABLE_DISK))
+ skip = 1;
+ if (ord & IMSM_ORD_REBUILD)
+ skip = 1;
+
+ /*
+ * if we skip some disks the array will be assmebled degraded;
+ * reset resync start to avoid a dirty-degraded situation
+ *
+ * FIXME handle dirty degraded
+ */
+ if (skip && !dev->vol.dirty)
+ this->resync_start = ~0ULL;
+ if (skip)
+ continue;
+
+ info_d = malloc(sizeof(*info_d));
+ if (!info_d) {
+ fprintf(stderr, Name ": failed to allocate disk"
+ " for volume %.16s\n", dev->volume);
+ free(this);
+ this = rest;
+ break;
+ }
+ memset(info_d, 0, sizeof(*info_d));
+ info_d->next = this->devs;
+ this->devs = info_d;
+
+ info_d->disk.number = d->index;
+ info_d->disk.major = d->major;
+ info_d->disk.minor = d->minor;
+ info_d->disk.raid_disk = slot;
+
+ 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);
+ }
+ rest = this;
+ }
+
+ return rest;
+}
+
+
+#ifndef MDASSEMBLE
+static int imsm_open_new(struct supertype *c, struct active_array *a,
+ char *inst)
+{
+ struct intel_super *super = c->sb;
+ struct imsm_super *mpb = super->anchor;
+
+ if (atoi(inst) >= mpb->num_raid_devs) {
+ fprintf(stderr, "%s: subarry index %d, out of range\n",
+ __func__, atoi(inst));
+ return -ENODEV;
+ }
+
+ dprintf("imsm: open_new %s\n", inst);
+ a->info.container_member = atoi(inst);
+ return 0;
+}
+
+static __u8 imsm_check_degraded(struct intel_super *super, struct imsm_dev *dev, int failed)
+{
+ struct imsm_map *map = get_imsm_map(dev, 0);
+
+ if (!failed)
+ return map->map_state == IMSM_T_STATE_UNINITIALIZED ?
+ IMSM_T_STATE_UNINITIALIZED : IMSM_T_STATE_NORMAL;
+
+ 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. Even numbered slots are mirrored on
+ * slot+1
+ */
+ int i;
+ /* gcc -Os complains that this is unused */
+ int insync = insync;
+
+ for (i = 0; i < map->num_members; i++) {
+ __u32 ord = get_imsm_ord_tbl_ent(dev, i);
+ int idx = ord_to_idx(ord);
+ struct imsm_disk *disk;
+
+ /* reset the potential in-sync count on even-numbered
+ * slots. num_copies is always 2 for imsm raid10
+ */
+ if ((i & 1) == 0)
+ insync = 2;
+
+ disk = get_imsm_disk(super, idx);
+ if (!disk || disk->status & FAILED_DISK ||
+ ord & IMSM_ORD_REBUILD)
+ insync--;
+
+ /* no in-sync disks left in this mirror the
+ * array has failed
+ */
+ if (insync == 0)
+ return IMSM_T_STATE_FAILED;
+ }
+
+ 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 intel_super *super, struct imsm_dev *dev)
+{
+ int i;
+ int failed = 0;
+ struct imsm_disk *disk;
+ struct imsm_map *map = get_imsm_map(dev, 0);
+ struct imsm_map *prev = get_imsm_map(dev, dev->vol.migr_state);
+ __u32 ord;
+ int idx;
+
+ /* at the beginning of migration we set IMSM_ORD_REBUILD on
+ * disks that are being rebuilt. New failures are recorded to
+ * map[0]. So we look through all the disks we started with and
+ * see if any failures are still present, or if any new ones
+ * have arrived
+ *
+ * FIXME add support for online capacity expansion and
+ * raid-level-migration
+ */
+ for (i = 0; i < prev->num_members; i++) {
+ ord = __le32_to_cpu(prev->disk_ord_tbl[i]);
+ ord |= __le32_to_cpu(map->disk_ord_tbl[i]);
+ idx = ord_to_idx(ord);
+
+ disk = get_imsm_disk(super, idx);
+ if (!disk || disk->status & FAILED_DISK ||
+ ord & IMSM_ORD_REBUILD)
+ failed++;
+ }
+
+ return failed;
+}
+
+static int is_resyncing(struct imsm_dev *dev)
+{
+ struct imsm_map *migr_map;
+
+ if (!dev->vol.migr_state)
+ return 0;
+
+ if (migr_type(dev) == MIGR_INIT ||
+ migr_type(dev) == MIGR_REPAIR)
+ return 1;
+
+ migr_map = get_imsm_map(dev, 1);
+
+ if (migr_map->map_state == IMSM_T_STATE_NORMAL)
+ return 1;
+ else
+ return 0;
+}
+
+static int is_rebuilding(struct imsm_dev *dev)
+{
+ struct imsm_map *migr_map;
+
+ if (!dev->vol.migr_state)
+ return 0;
+
+ if (migr_type(dev) != MIGR_REBUILD)
+ return 0;
+
+ migr_map = get_imsm_map(dev, 1);
+
+ if (migr_map->map_state == IMSM_T_STATE_DEGRADED)
+ return 1;
+ else
+ return 0;
+}
+
+/* return true if we recorded new information */
+static int mark_failure(struct imsm_dev *dev, struct imsm_disk *disk, int idx)
+{
+ __u32 ord;
+ int slot;
+ struct imsm_map *map;
+
+ /* new failures are always set in map[0] */
+ map = get_imsm_map(dev, 0);
+
+ slot = get_imsm_disk_slot(map, idx);
+ if (slot < 0)
+ return 0;
+
+ ord = __le32_to_cpu(map->disk_ord_tbl[slot]);
+ if ((disk->status & FAILED_DISK) && (ord & IMSM_ORD_REBUILD))
+ return 0;
+
+ disk->status |= FAILED_DISK;
+ set_imsm_ord_tbl_ent(map, slot, idx | IMSM_ORD_REBUILD);
+ if (~map->failed_disk_num == 0)
+ map->failed_disk_num = slot;
+ return 1;
+}
+
+static void mark_missing(struct imsm_dev *dev, struct imsm_disk *disk, int idx)
+{
+ mark_failure(dev, disk, idx);
+
+ if (disk->scsi_id == __cpu_to_le32(~(__u32)0))
+ return;
+
+ disk->scsi_id = __cpu_to_le32(~(__u32)0);
+ memmove(&disk->serial[0], &disk->serial[1], MAX_RAID_SERIAL_LEN - 1);
+}
+
+/* Handle dirty -> clean transititions and resync. Degraded and rebuild
+ * states are handled in imsm_set_disk() with one exception, when a
+ * resync is stopped due to a new failure this routine will set the
+ * 'degraded' state for the array.
+ */
+static int 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, inst);
+ struct imsm_map *map = get_imsm_map(dev, 0);
+ int failed = imsm_count_failed(super, dev);
+ __u8 map_state = imsm_check_degraded(super, dev, failed);
+
+ /* before we activate this array handle any missing disks */
+ if (consistent == 2 && super->missing) {
+ struct dl *dl;
+
+ dprintf("imsm: mark missing\n");
+ end_migration(dev, map_state);
+ for (dl = super->missing; dl; dl = dl->next)
+ mark_missing(dev, &dl->disk, dl->index);
+ super->updates_pending++;
+ }
+
+ if (consistent == 2 &&
+ (!is_resync_complete(a) ||
+ map_state != IMSM_T_STATE_NORMAL ||
+ dev->vol.migr_state))
+ consistent = 0;
+
+ if (is_resync_complete(a)) {
+ /* complete intialization / resync,
+ * recovery and interrupted recovery is completed in
+ * ->set_disk
+ */
+ if (is_resyncing(dev)) {
+ dprintf("imsm: mark resync done\n");
+ end_migration(dev, map_state);
+ super->updates_pending++;
+ }
+ } else if (!is_resyncing(dev) && !failed) {
+ /* mark the start of the init process if nothing is failed */
+ dprintf("imsm: mark resync start (%llu)\n", a->resync_start);
+ if (map->map_state == IMSM_T_STATE_UNINITIALIZED)
+ migrate(dev, IMSM_T_STATE_NORMAL, MIGR_INIT);
+ else
+ migrate(dev, IMSM_T_STATE_NORMAL, MIGR_REPAIR);
+ super->updates_pending++;
+ }
+
+ /* FIXME check if we can update curr_migr_unit from resync_start */
+
+ /* mark dirty / clean */
+ if (dev->vol.dirty != !consistent) {
+ dprintf("imsm: mark '%s' (%llu)\n",
+ consistent ? "clean" : "dirty", a->resync_start);
+ if (consistent)
+ dev->vol.dirty = 0;
+ else
+ dev->vol.dirty = 1;
+ super->updates_pending++;
+ }
+ return consistent;
+}
+
+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, inst);
+ struct imsm_map *map = get_imsm_map(dev, 0);
+ struct imsm_disk *disk;
+ int failed;
+ __u32 ord;
+ __u8 map_state;
+
+ 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;
+
+ dprintf("imsm: set_disk %d:%x\n", n, state);
+
+ ord = get_imsm_ord_tbl_ent(dev, n);
+ disk = get_imsm_disk(super, ord_to_idx(ord));
+
+ /* check for new failures */
+ if (state & DS_FAULTY) {
+ if (mark_failure(dev, disk, ord_to_idx(ord)))
+ super->updates_pending++;
+ }
+
+ /* check if in_sync */
+ if (state & DS_INSYNC && ord & IMSM_ORD_REBUILD && is_rebuilding(dev)) {
+ struct imsm_map *migr_map = get_imsm_map(dev, 1);
+
+ set_imsm_ord_tbl_ent(migr_map, n, ord_to_idx(ord));
+ super->updates_pending++;
+ }
+
+ failed = imsm_count_failed(super, dev);
+ map_state = imsm_check_degraded(super, dev, failed);
+
+ /* check if recovery complete, newly degraded, or failed */
+ if (map_state == IMSM_T_STATE_NORMAL && is_rebuilding(dev)) {
+ end_migration(dev, map_state);
+ map = get_imsm_map(dev, 0);
+ map->failed_disk_num = ~0;
+ super->updates_pending++;
+ } else if (map_state == IMSM_T_STATE_DEGRADED &&
+ map->map_state != map_state &&
+ !dev->vol.migr_state) {
+ dprintf("imsm: mark degraded\n");
+ map->map_state = map_state;
+ super->updates_pending++;
+ } else if (map_state == IMSM_T_STATE_FAILED &&
+ map->map_state != map_state) {
+ dprintf("imsm: mark failed\n");
+ end_migration(dev, map_state);
+ super->updates_pending++;
+ }
+}
+
+static int store_imsm_mpb(int fd, struct intel_super *super)
+{
+ struct imsm_super *mpb = super->anchor;
+ __u32 mpb_size = __le32_to_cpu(mpb->mpb_size);
+ unsigned long long dsize;
+ unsigned long long sectors;
+
+ get_dev_size(fd, NULL, &dsize);
+
+ if (mpb_size > 512) {
+ /* -1 to account for anchor */
+ 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, 512 * sectors) != 512 * sectors)
+ return 1;
+ }
+
+ /* 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;
+
+ return 0;
+}
+
+static void imsm_sync_metadata(struct supertype *container)
+{
+ struct intel_super *super = container->sb;
+
+ if (!super->updates_pending)
+ return;
+
+ write_super_imsm(super, 0);
+
+ super->updates_pending = 0;
+}
+
+static struct dl *imsm_readd(struct intel_super *super, int idx, struct active_array *a)
+{
+ struct imsm_dev *dev = get_imsm_dev(super, a->info.container_member);
+ int i = get_imsm_disk_idx(dev, idx);
+ struct dl *dl;
+
+ for (dl = super->disks; dl; dl = dl->next)
+ if (dl->index == i)
+ break;
+
+ if (dl && dl->disk.status & FAILED_DISK)
+ dl = NULL;
+
+ if (dl)
+ dprintf("%s: found %x:%x\n", __func__, dl->major, dl->minor);
+
+ return dl;
+}
+
+static struct dl *imsm_add_spare(struct intel_super *super, int slot,
+ struct active_array *a, int activate_new)
+{
+ struct imsm_dev *dev = get_imsm_dev(super, a->info.container_member);
+ int idx = get_imsm_disk_idx(dev, slot);
+ struct imsm_super *mpb = super->anchor;
+ struct imsm_map *map;
+ unsigned long long esize;
+ unsigned long long pos;
+ struct mdinfo *d;
+ struct extent *ex;
+ int i, j;
+ int found;
+ __u32 array_start;
+ __u32 blocks;
+ struct dl *dl;
+
+ for (dl = super->disks; dl; dl = dl->next) {
+ /* If in this array, skip */
+ for (d = a->info.devs ; d ; d = d->next)
+ if (d->state_fd >= 0 &&
+ d->disk.major == dl->major &&
+ d->disk.minor == dl->minor) {
+ dprintf("%x:%x already in array\n", dl->major, dl->minor);
+ break;
+ }
+ if (d)
+ continue;