]> git.ipfire.org Git - thirdparty/mdadm.git/blobdiff - super-intel.c
imsm: FIX: Add array map state transition for failures during initialization
[thirdparty/mdadm.git] / super-intel.c
index e9d9e35c0e8222174d940a33dec4a26dfdf42484..217a1963eec7f489b2b50814d9772c9611472132 100644 (file)
 
 /* Define all supported attributes that have to be accepted by mdadm
  */
-#define MPB_ATTRIB_SUPPORTED           MPB_ATTRIB_CHECKSUM_VERIFY | \
+#define MPB_ATTRIB_SUPPORTED          (MPB_ATTRIB_CHECKSUM_VERIFY | \
                                        MPB_ATTRIB_2TB             | \
                                        MPB_ATTRIB_2TB_DISK        | \
                                        MPB_ATTRIB_RAID0           | \
                                        MPB_ATTRIB_RAID1           | \
                                        MPB_ATTRIB_RAID10          | \
                                        MPB_ATTRIB_RAID5           | \
-                                       MPB_ATTRIB_EXP_STRIPE_SIZE
+                                       MPB_ATTRIB_EXP_STRIPE_SIZE)
+
+/* Define attributes that are unused but not harmful */
+#define MPB_ATTRIB_IGNORED             (MPB_ATTRIB_NEVER_USE)
 
 #define MPB_SECTOR_CNT 2210
 #define IMSM_RESERVED_SECTORS 4096
+#define NUM_BLOCKS_DIRTY_STRIPE_REGION 2056
 #define SECT_PER_MB_SHIFT 11
 
 /* Disk configuration info. */
@@ -102,6 +106,11 @@ struct imsm_disk {
        __u32 filler[IMSM_DISK_FILLERS]; /* 0xF4 - 0x107 MPB_DISK_FILLERS for future expansion */
 };
 
+/* map selector for map managment
+ */
+#define MAP_0          2
+#define MAP_1          4
+
 /* RAID map configuration infos. */
 struct imsm_map {
        __u32 pba_of_lba0;      /* start address of partition */
@@ -341,7 +350,7 @@ struct intel_super {
                struct extent *e; /* for determining freespace @ create */
                int raiddisk; /* slot to fill in autolayout */
                enum action action;
-       } *disks;
+       } *disks, *current_disk;
        struct dl *disk_mgmt_list; /* list of disks to add/remove while mdmon
                                      active */
        struct dl *missing; /* disks removed while we weren't looking */
@@ -658,21 +667,32 @@ struct imsm_map *get_imsm_map(struct imsm_dev *dev, int second_map)
 {
        /* A device can have 2 maps if it is in the middle of a migration.
         * If second_map is:
-        *    0   - we return the first map
-        *    1   - we return the second map if it exists, else NULL
+        *    MAP_0 or 0   - we return the first map
+        *    MAP_1 or 1   - we return the second map if it exists, else NULL
         *   -1   - we return the second map if it exists, else the first
         */
        struct imsm_map *map = &dev->vol.map[0];
+       struct imsm_map *map2 = NULL;
 
-       if (second_map == 1 && !dev->vol.migr_state)
-               return NULL;
-       else if (second_map == 1 ||
-                (second_map < 0 && dev->vol.migr_state)) {
-               void *ptr = map;
+       if (dev->vol.migr_state)
+               map2 = (void *)map + sizeof_imsm_map(map);
 
-               return ptr + sizeof_imsm_map(map);
-       } else
-               return map;
+       switch (second_map) {
+       case MAP_0:
+       case 0:
+               break;
+       case MAP_1:
+       case 1:
+               map = map2;
+               break;
+       case -1:
+               if (map2)
+                       map = map2;
+               break;
+       default:
+               map = NULL;
+       }
+       return map;
 
 }
 
@@ -824,13 +844,24 @@ static int count_memberships(struct dl *dl, struct intel_super *super)
        return memberships;
 }
 
+static __u32 imsm_min_reserved_sectors(struct intel_super *super);
+
 static struct extent *get_extents(struct intel_super *super, struct dl *dl)
 {
        /* find a list of used extents on the given physical device */
        struct extent *rv, *e;
        int i;
        int memberships = count_memberships(dl, super);
-       __u32 reservation = MPB_SECTOR_CNT + IMSM_RESERVED_SECTORS;
+       __u32 reservation;
+
+       /* trim the reserved area for spares, so they can join any array
+        * regardless of whether the OROM has assigned sectors from the
+        * IMSM_RESERVED_SECTORS region
+        */
+       if (dl->index == -1)
+               reservation = imsm_min_reserved_sectors(super);
+       else
+               reservation = MPB_SECTOR_CNT + IMSM_RESERVED_SECTORS;
 
        rv = malloc(sizeof(struct extent) * (memberships + 1));
        if (!rv)
@@ -921,6 +952,51 @@ static int is_failed(struct imsm_disk *disk)
        return (disk->status & FAILED_DISK) == FAILED_DISK;
 }
 
+/* try to determine how much space is reserved for metadata from
+ * the last get_extents() entry on the smallest active disk,
+ * otherwise fallback to the default
+ */
+static __u32 imsm_min_reserved_sectors(struct intel_super *super)
+{
+       struct extent *e;
+       int i;
+       __u32 min_active, remainder;
+       __u32 rv = MPB_SECTOR_CNT + IMSM_RESERVED_SECTORS;
+       struct dl *dl, *dl_min = NULL;
+
+       if (!super)
+               return rv;
+
+       min_active = 0;
+       for (dl = super->disks; dl; dl = dl->next) {
+               if (dl->index < 0)
+                       continue;
+               if (dl->disk.total_blocks < min_active || min_active == 0) {
+                       dl_min = dl;
+                       min_active = dl->disk.total_blocks;
+               }
+       }
+       if (!dl_min)
+               return rv;
+
+       /* find last lba used by subarrays on the smallest active disk */
+       e = get_extents(super, dl_min);
+       if (!e)
+               return rv;
+       for (i = 0; e[i].size; i++)
+               continue;
+
+       remainder = min_active - e[i].start;
+       free(e);
+
+       /* to give priority to recovery we should not require full
+          IMSM_RESERVED_SECTORS from the spare */
+       rv = MPB_SECTOR_CNT + NUM_BLOCKS_DIRTY_STRIPE_REGION;
+
+       /* if real reservation is smaller use that value */
+       return  (remainder < rv) ? remainder : rv;
+}
+
 /* Return minimum size of a spare that can be used in this array*/
 static unsigned long long min_acceptable_spare_size_imsm(struct supertype *st)
 {
@@ -947,11 +1023,15 @@ static unsigned long long min_acceptable_spare_size_imsm(struct supertype *st)
        if (i > 0)
                rv = e[i-1].start + e[i-1].size;
        free(e);
+
        /* add the amount of space needed for metadata */
-       rv = rv + MPB_SECTOR_CNT + IMSM_RESERVED_SECTORS;
+       rv = rv + imsm_min_reserved_sectors(super);
+
        return rv * 512;
 }
 
+static int is_gen_migration(struct imsm_dev *dev);
+
 #ifndef MDASSEMBLE
 static __u64 blocks_per_migr_unit(struct intel_super *super,
                                  struct imsm_dev *dev);
@@ -1048,26 +1128,32 @@ static void print_imsm_dev(struct intel_super *super,
                struct imsm_map *map = get_imsm_map(dev, 1);
 
                printf(" <-- %s", map_state_str[map->map_state]);
-               printf("\n     Checkpoint : %u (%llu)",
-                      __le32_to_cpu(dev->vol.curr_migr_unit),
-                      (unsigned long long)blocks_per_migr_unit(super, dev));
+               printf("\n     Checkpoint : %u ",
+                          __le32_to_cpu(dev->vol.curr_migr_unit));
+               if ((is_gen_migration(dev)) && (super->disks->index > 1))
+                       printf("(N/A)");
+               else
+                       printf("(%llu)", (unsigned long long)
+                                  blocks_per_migr_unit(super, dev));
        }
        printf("\n");
        printf("    Dirty State : %s\n", dev->vol.dirty ? "dirty" : "clean");
 }
 
-static void print_imsm_disk(struct imsm_super *mpb, int index, __u32 reserved)
+static void print_imsm_disk(struct imsm_disk *disk, int index, __u32 reserved)
 {
-       struct imsm_disk *disk = __get_imsm_disk(mpb, index);
        char str[MAX_RAID_SERIAL_LEN + 1];
        __u64 sz;
 
-       if (index < 0 || !disk)
+       if (index < -1 || !disk)
                return;
 
        printf("\n");
        snprintf(str, MAX_RAID_SERIAL_LEN + 1, "%s", disk->serial);
-       printf("  Disk%02d Serial : %s\n", index, str);
+       if (index >= 0)
+               printf("  Disk%02d Serial : %s\n", index, str);
+       else
+               printf("    Disk Serial : %s\n", str);
        printf("          State :%s%s%s\n", is_spare(disk) ? " spare" : "",
                                            is_configured(disk) ? " active" : "",
                                            is_failed(disk) ? " failed" : "");
@@ -1077,8 +1163,6 @@ static void print_imsm_disk(struct imsm_super *mpb, int index, __u32 reserved)
               human_size(sz * 512));
 }
 
-static int is_gen_migration(struct imsm_dev *dev);
-
 void examine_migr_rec_imsm(struct intel_super *super)
 {
        struct migr_record *migr_rec = super->migr_rec;
@@ -1141,11 +1225,14 @@ void examine_migr_rec_imsm(struct intel_super *super)
 static int imsm_check_attributes(__u32 attributes)
 {
        int ret_val = 1;
-       __u32 not_supported = (MPB_ATTRIB_SUPPORTED)^0xffffffff;
+       __u32 not_supported = MPB_ATTRIB_SUPPORTED^0xffffffff;
+
+       not_supported &= ~MPB_ATTRIB_IGNORED;
 
        not_supported &= attributes;
        if (not_supported) {
-               fprintf(stderr, Name "(IMSM): Unsupported attributes : %x\n", not_supported);
+               fprintf(stderr, Name "(IMSM): Unsupported attributes : %x\n",
+                       (unsigned)__le32_to_cpu(not_supported));
                if (not_supported & MPB_ATTRIB_CHECKSUM_VERIFY) {
                        dprintf("\t\tMPB_ATTRIB_CHECKSUM_VERIFY \n");
                        not_supported ^= MPB_ATTRIB_CHECKSUM_VERIFY;
@@ -1248,7 +1335,7 @@ static void examine_super_imsm(struct supertype *st, char *homehost)
        printf("    MPB Sectors : %d\n", mpb_sectors(mpb));
        printf("          Disks : %d\n", mpb->num_disks);
        printf("   RAID Devices : %d\n", mpb->num_raid_devs);
-       print_imsm_disk(mpb, super->disks->index, reserved);
+       print_imsm_disk(__get_imsm_disk(mpb, super->disks->index), super->disks->index, reserved);
        if (super->bbm_log) {
                struct bbm_log *log = super->bbm_log;
 
@@ -1273,28 +1360,12 @@ static void examine_super_imsm(struct supertype *st, char *homehost)
        for (i = 0; i < mpb->num_disks; i++) {
                if (i == super->disks->index)
                        continue;
-               print_imsm_disk(mpb, i, reserved);
+               print_imsm_disk(__get_imsm_disk(mpb, i), i, reserved);
        }
-       for (dl = super->disks ; dl; dl = dl->next) {
-               struct imsm_disk *disk;
-               char str[MAX_RAID_SERIAL_LEN + 1];
-               __u64 sz;
-
-               if (dl->index >= 0)
-                       continue;
 
-               disk = &dl->disk;
-               printf("\n");
-               snprintf(str, MAX_RAID_SERIAL_LEN + 1, "%s", disk->serial);
-               printf("    Disk Serial : %s\n", str);
-               printf("          State :%s%s%s\n", is_spare(disk) ? " spare" : "",
-                      is_configured(disk) ? " active" : "",
-                      is_failed(disk) ? " failed" : "");
-               printf("             Id : %08x\n", __le32_to_cpu(disk->scsi_id));
-               sz = __le32_to_cpu(disk->total_blocks) - reserved;
-               printf("    Usable Size : %llu%s\n", (unsigned long long)sz,
-                      human_size(sz * 512));
-       }
+       for (dl = super->disks; dl; dl = dl->next)
+               if (dl->index == -1)
+                       print_imsm_disk(&dl->disk, -1, reserved);
 
        examine_migr_rec_imsm(super);
 }
@@ -1518,11 +1589,11 @@ static int ahci_enumerate_ports(const char *hba_path, int port_count, int host_b
                        fd2devname(fd, buf);
                        printf("          Port%d : %s", port, buf);
                        if (imsm_read_serial(fd, NULL, (__u8 *) buf) == 0)
-                               printf(" (%s)\n", buf);
+                               printf(" (%.*s)\n", MAX_RAID_SERIAL_LEN, buf);
                        else
-                               printf("()\n");
+                               printf(" ()\n");
+                       close(fd);
                }
-               close(fd);
                free(path);
                path = NULL;
        }
@@ -2184,6 +2255,30 @@ static int write_imsm_migr_rec(struct supertype *st)
 }
 #endif /* MDASSEMBLE */
 
+/* spare/missing disks activations are not allowe when
+ * array/container performs reshape operation, because
+ * all arrays in container works on the same disks set
+ */
+int imsm_reshape_blocks_arrays_changes(struct intel_super *super)
+{
+       int rv = 0;
+       struct intel_dev *i_dev;
+       struct imsm_dev *dev;
+
+       /* check whole container
+        */
+       for (i_dev = super->devlist; i_dev; i_dev = i_dev->next) {
+               dev = i_dev->dev;
+               if (is_gen_migration(dev)) {
+                       /* No repair during any migration in container
+                        */
+                       rv = 1;
+                       break;
+               }
+       }
+       return rv;
+}
+
 static void getinfo_super_imsm_volume(struct supertype *st, struct mdinfo *info, char *dmap)
 {
        struct intel_super *super = st->sb;
@@ -2201,7 +2296,7 @@ static void getinfo_super_imsm_volume(struct supertype *st, struct mdinfo *info,
        if (prev_map)
                map_to_analyse = prev_map;
 
-       dl = super->disks;
+       dl = super->current_disk;
 
        info->container_member    = super->current_vol;
        info->array.raid_disks    = map->num_members;
@@ -2216,7 +2311,9 @@ static void getinfo_super_imsm_volume(struct supertype *st, struct mdinfo *info,
        info->custom_array_size   = __le32_to_cpu(dev->size_high);
        info->custom_array_size   <<= 32;
        info->custom_array_size   |= __le32_to_cpu(dev->size_low);
-       if (prev_map && map->map_state == prev_map->map_state) {
+       info->recovery_blocked = imsm_reshape_blocks_arrays_changes(st->sb);
+
+       if (is_gen_migration(dev)) {
                info->reshape_active = 1;
                info->new_level = get_imsm_raid_level(map);
                info->new_layout = imsm_level_to_layout(info->new_level);
@@ -2226,7 +2323,7 @@ static void getinfo_super_imsm_volume(struct supertype *st, struct mdinfo *info,
                        /* this needs to be applied to every array
                         * in the container.
                         */
-                       info->reshape_active = 2;
+                       info->reshape_active = CONTAINER_RESHAPE;
                }
                /* We shape information that we give to md might have to be
                 * modify to cope with md's requirement for reshaping arrays.
@@ -2263,11 +2360,13 @@ static void getinfo_super_imsm_volume(struct supertype *st, struct mdinfo *info,
                info->new_chunk = info->array.chunk_size;
                info->delta_disks = 0;
        }
-       info->disk.major = 0;
-       info->disk.minor = 0;
+
        if (dl) {
                info->disk.major = dl->major;
                info->disk.minor = dl->minor;
+               info->disk.number = dl->index;
+               info->disk.raid_disk = get_imsm_disk_slot(map_to_analyse,
+                                                         dl->index);
        }
 
        info->data_offset         = __le32_to_cpu(map_to_analyse->pba_of_lba0);
@@ -2293,8 +2392,9 @@ static void getinfo_super_imsm_volume(struct supertype *st, struct mdinfo *info,
 
        info->reshape_progress = 0;
        info->resync_start = MaxSector;
-       if (map_to_analyse->map_state == IMSM_T_STATE_UNINITIALIZED ||
-           dev->vol.dirty) {
+       if ((map_to_analyse->map_state == IMSM_T_STATE_UNINITIALIZED ||
+           dev->vol.dirty) &&
+           imsm_reshape_blocks_arrays_changes(super) == 0) {
                info->resync_start = 0;
        }
        if (dev->vol.migr_state) {
@@ -2326,7 +2426,9 @@ static void getinfo_super_imsm_volume(struct supertype *st, struct mdinfo *info,
 
                        dprintf("IMSM: General Migration checkpoint : %llu "
                               "(%llu) -> read reshape progress : %llu\n",
-                               units, blocks_per_unit, info->reshape_progress);
+                               (unsigned long long)units,
+                               (unsigned long long)blocks_per_unit,
+                               info->reshape_progress);
 
                        used_disks = imsm_num_data_members(dev, 1);
                        if (used_disks > 0) {
@@ -2382,8 +2484,28 @@ static void getinfo_super_imsm_volume(struct supertype *st, struct mdinfo *info,
        }
 }
 
-static __u8 imsm_check_degraded(struct intel_super *super, struct imsm_dev *dev, int failed);
-static int imsm_count_failed(struct intel_super *super, struct imsm_dev *dev);
+static __u8 imsm_check_degraded(struct intel_super *super, struct imsm_dev *dev,
+                               int failed, int look_in_map);
+
+static int imsm_count_failed(struct intel_super *super, struct imsm_dev *dev,
+                            int look_in_map);
+
+static void manage_second_map(struct intel_super *super, struct imsm_dev *dev)
+{
+       if (is_gen_migration(dev)) {
+               int failed;
+               __u8 map_state;
+               struct imsm_map *map2 = get_imsm_map(dev, MAP_1);
+
+               failed = imsm_count_failed(super, dev, MAP_1);
+               map_state = imsm_check_degraded(super, dev, failed,
+                                               MAP_1);
+               if (map2->map_state != map_state) {
+                       map2->map_state = map_state;
+                       super->updates_pending++;
+               }
+       }
+}
 
 static struct imsm_disk *get_imsm_missing(struct intel_super *super, __u8 index)
 {
@@ -2433,6 +2555,7 @@ static void getinfo_super_imsm(struct supertype *st, struct mdinfo *info, char *
        info->disk.state = 0;
        info->name[0] = 0;
        info->recovery_start = MaxSector;
+       info->recovery_blocked = imsm_reshape_blocks_arrays_changes(st->sb);
 
        /* do we have the all the insync disks that we expect? */
        mpb = super->anchor;
@@ -2443,15 +2566,15 @@ static void getinfo_super_imsm(struct supertype *st, struct mdinfo *info, char *
                struct imsm_map *map;
                __u8 state;
 
-               failed = imsm_count_failed(super, dev);
-               state = imsm_check_degraded(super, dev, failed);
-               map = get_imsm_map(dev, dev->vol.migr_state);
+               failed = imsm_count_failed(super, dev, MAP_0);
+               state = imsm_check_degraded(super, dev, failed, MAP_0);
+               map = get_imsm_map(dev, 0);
 
                /* any newly missing disks?
                 * (catches single-degraded vs double-degraded)
                 */
                for (j = 0; j < map->num_members; j++) {
-                       __u32 ord = get_imsm_ord_tbl_ent(dev, i, -1);
+                       __u32 ord = get_imsm_ord_tbl_ent(dev, j, 0);
                        __u32 idx = ord_to_idx(ord);
 
                        if (!(ord & IMSM_ORD_REBUILD) &&
@@ -2778,14 +2901,16 @@ static void fd2devname(int fd, char *name)
        sprintf(path, "/sys/dev/block/%d:%d",
                major(st.st_rdev), minor(st.st_rdev));
 
-       rv = readlink(path, dname, sizeof(dname));
+       rv = readlink(path, dname, sizeof(dname)-1);
        if (rv <= 0)
                return;
        
        dname[rv] = '\0';
        nm = strrchr(dname, '/');
-       nm++;
-       snprintf(name, MAX_RAID_SERIAL_LEN, "/dev/%s", nm);
+       if (nm) {
+               nm++;
+               snprintf(name, MAX_RAID_SERIAL_LEN, "/dev/%s", nm);
+       }
 }
 
 extern int scsi_get_serial(int fd, void *buf, size_t buf_len);
@@ -2870,7 +2995,6 @@ static void serialcpy(__u8 *dest, __u8 *src)
        strncpy((char *) dest, (char *) src, MAX_RAID_SERIAL_LEN);
 }
 
-#ifndef MDASSEMBLE
 static struct dl *serial_to_dl(__u8 *serial, struct intel_super *super)
 {
        struct dl *dl;
@@ -2881,7 +3005,6 @@ static struct dl *serial_to_dl(__u8 *serial, struct intel_super *super)
 
        return dl;
 }
-#endif
 
 static struct imsm_disk *
 __serial_to_disk(__u8 *serial, struct imsm_super *mpb, int *idx)
@@ -3005,7 +3128,8 @@ static void migrate(struct imsm_dev *dev, struct intel_super *super,
        src->map_state = to_state;
 }
 
-static void end_migration(struct imsm_dev *dev, __u8 map_state)
+static void end_migration(struct imsm_dev *dev, struct intel_super *super,
+                         __u8 map_state)
 {
        struct imsm_map *map = get_imsm_map(dev, 0);
        struct imsm_map *prev = get_imsm_map(dev, dev->vol.migr_state);
@@ -3016,19 +3140,31 @@ static void end_migration(struct imsm_dev *dev, __u8 map_state)
         *
         * FIXME add support for raid-level-migration
         */
-       for (i = 0; i < prev->num_members; i++)
-               for (j = 0; j < map->num_members; j++)
-                       /* during online capacity expansion
-                        * disks position can be changed if takeover is used
-                        */
-                       if (ord_to_idx(map->disk_ord_tbl[j]) ==
-                           ord_to_idx(prev->disk_ord_tbl[i])) {
-                               map->disk_ord_tbl[j] |= prev->disk_ord_tbl[i];
-                               break;
-                       }
+       if ((map_state != map->map_state) && (is_gen_migration(dev) == 0) &&
+               (prev->map_state != IMSM_T_STATE_UNINITIALIZED)) {
+               /* when final map state is other than expected
+                * merge maps (not for migration)
+                */
+               int failed;
+
+               for (i = 0; i < prev->num_members; i++)
+                       for (j = 0; j < map->num_members; j++)
+                               /* during online capacity expansion
+                                * disks position can be changed
+                                * if takeover is used
+                                */
+                               if (ord_to_idx(map->disk_ord_tbl[j]) ==
+                                   ord_to_idx(prev->disk_ord_tbl[i])) {
+                                       map->disk_ord_tbl[j] |=
+                                               prev->disk_ord_tbl[i];
+                                       break;
+                               }
+               failed = imsm_count_failed(super, dev, MAP_0);
+               map_state = imsm_check_degraded(super, dev, failed, MAP_0);
+       }
 
        dev->vol.migr_state = 0;
-       dev->vol.migr_type = 0;
+       set_migr_type(dev, 0);
        dev->vol.curr_migr_unit = 0;
        map->map_state = map_state;
 }
@@ -3439,7 +3575,6 @@ static int find_intel_hba_capability(int fd, struct intel_super *super, char *de
        return 0;
 }
 
-#ifndef MDASSEMBLE
 /* find_missing - helper routine for load_super_imsm_all that identifies
  * disks that have disappeared from the system.  This routine relies on
  * the mpb being uptodate, which it is at load time.
@@ -3475,6 +3610,7 @@ static int find_missing(struct intel_super *super)
        return 0;
 }
 
+#ifndef MDASSEMBLE
 static struct intel_disk *disk_list_get(__u8 *serial, struct intel_disk *disk_list)
 {
        struct intel_disk *idisk = disk_list;
@@ -4118,6 +4254,7 @@ static int init_super_imsm_volume(struct supertype *st, mdu_array_info_t *info,
                                __func__);
                        free(super->buf);
                        free(super);
+                       free(mpb_new);
                        return 0;
                }
                memcpy(mpb_new, mpb, size_old);
@@ -4128,12 +4265,40 @@ static int init_super_imsm_volume(struct supertype *st, mdu_array_info_t *info,
                memset(mpb_new + size_old, 0, size_round - size_old);
        }
        super->current_vol = idx;
-       /* when creating the first raid device in this container set num_disks
-        * to zero, i.e. delete this spare and add raid member devices in
-        * add_to_super_imsm_volume()
+
+       /* handle 'failed_disks' by either:
+        * a) create dummy disk entries in the table if this the first
+        *    volume in the array.  We add them here as this is the only
+        *    opportunity to add them. add_to_super_imsm_volume()
+        *    handles the non-failed disks and continues incrementing
+        *    mpb->num_disks.
+        * b) validate that 'failed_disks' matches the current number
+        *    of missing disks if the container is populated
         */
-       if (super->current_vol == 0)
+       if (super->current_vol == 0) {
                mpb->num_disks = 0;
+               for (i = 0; i < info->failed_disks; i++) {
+                       struct imsm_disk *disk;
+
+                       mpb->num_disks++;
+                       disk = __get_imsm_disk(mpb, i);
+                       disk->status = CONFIGURED_DISK | FAILED_DISK;
+                       disk->scsi_id = __cpu_to_le32(~(__u32)0);
+                       snprintf((char *) disk->serial, MAX_RAID_SERIAL_LEN,
+                                "missing:%d", i);
+               }
+               find_missing(super);
+       } else {
+               int missing = 0;
+               struct dl *d;
+
+               for (d = super->missing; d; d = d->next)
+                       missing++;
+               if (info->failed_disks > missing) {
+                       fprintf(stderr, Name": unable to add 'missing' disk to container\n");
+                       return 0;
+               }
+       }
 
        if (!check_name(super, name, 0))
                return 0;
@@ -4165,15 +4330,18 @@ static int init_super_imsm_volume(struct supertype *st, mdu_array_info_t *info,
        vol = &dev->vol;
        vol->migr_state = 0;
        set_migr_type(dev, MIGR_INIT);
-       vol->dirty = 0;
+       vol->dirty = !info->state;
        vol->curr_migr_unit = 0;
        map = get_imsm_map(dev, 0);
        map->pba_of_lba0 = __cpu_to_le32(super->create_offset);
        map->blocks_per_member = __cpu_to_le32(info_to_blocks_per_member(info));
        map->blocks_per_strip = __cpu_to_le16(info_to_blocks_per_strip(info));
        map->failed_disk_num = ~0;
-       map->map_state = info->level ? IMSM_T_STATE_UNINITIALIZED :
-                                      IMSM_T_STATE_NORMAL;
+       if (info->level > 0)
+               map->map_state = IMSM_T_STATE_UNINITIALIZED;
+       else
+               map->map_state = info->failed_disks ? IMSM_T_STATE_FAILED :
+                                                     IMSM_T_STATE_NORMAL;
        map->ddf = 1;
 
        if (info->level == 1 && info->raid_disks > 2) {
@@ -4281,9 +4449,10 @@ static int add_to_super_imsm_volume(struct supertype *st, mdu_disk_info_t *dk,
 {
        struct intel_super *super = st->sb;
        struct imsm_super *mpb = super->anchor;
-       struct dl *dl;
+       struct imsm_disk *_disk;
        struct imsm_dev *dev;
        struct imsm_map *map;
+       struct dl *dl, *df;
        int slot;
 
        dev = get_imsm_dev(super, super->current_vol);
@@ -4327,15 +4496,54 @@ static int add_to_super_imsm_volume(struct supertype *st, mdu_disk_info_t *dk,
                        devname);
                return 1;
        }
-       set_imsm_ord_tbl_ent(map, dk->number, dl->index);
+       set_imsm_ord_tbl_ent(map, dk->raid_disk, dl->index);
        dl->disk.status = CONFIGURED_DISK;
 
+       /* update size of 'missing' disks to be at least as large as the
+        * largest acitve member (we only have dummy missing disks when
+        * creating the first volume)
+        */
+       if (super->current_vol == 0) {
+               for (df = super->missing; df; df = df->next) {
+                       if (dl->disk.total_blocks > df->disk.total_blocks)
+                               df->disk.total_blocks = dl->disk.total_blocks;
+                       _disk = __get_imsm_disk(mpb, df->index);
+                       *_disk = df->disk;
+               }
+       }
+
+       /* refresh unset/failed slots to point to valid 'missing' entries */
+       for (df = super->missing; df; df = df->next)
+               for (slot = 0; slot < mpb->num_disks; slot++) {
+                       __u32 ord = get_imsm_ord_tbl_ent(dev, slot, -1);
+
+                       if ((ord & IMSM_ORD_REBUILD) == 0)
+                               continue;
+                       set_imsm_ord_tbl_ent(map, slot, df->index | IMSM_ORD_REBUILD);
+                       if (is_gen_migration(dev)) {
+                               struct imsm_map *map2 = get_imsm_map(dev, 1);
+                               if (slot < map2->num_members) {
+                                       __u32 ord2 = get_imsm_ord_tbl_ent(dev,
+                                                                         slot,
+                                                                         1);
+                                       if ((unsigned)df->index ==
+                                                              ord_to_idx(ord2))
+                                               set_imsm_ord_tbl_ent(map2,
+                                                       slot,
+                                                       df->index |
+                                                       IMSM_ORD_REBUILD);
+                               }
+                       }
+                       dprintf("set slot:%d to missing disk:%d\n", slot, df->index);
+                       break;
+               }
+
        /* if we are creating the first raid device update the family number */
        if (super->current_vol == 0) {
                __u32 sum;
                struct imsm_dev *_dev = __get_imsm_dev(mpb, 0);
-               struct imsm_disk *_disk = __get_imsm_disk(mpb, dl->index);
 
+               _disk = __get_imsm_disk(mpb, dl->index);
                if (!_dev || !_disk) {
                        fprintf(stderr, Name ": BUG mpb setup error\n");
                        return 1;
@@ -4347,10 +4555,41 @@ static int add_to_super_imsm_volume(struct supertype *st, mdu_disk_info_t *dk,
                mpb->family_num = __cpu_to_le32(sum);
                mpb->orig_family_num = mpb->family_num;
        }
-
+       super->current_disk = dl;
        return 0;
 }
 
+/* mark_spare()
+ *   Function marks disk as spare and restores disk serial
+ *   in case it was previously marked as failed by takeover operation
+ * reruns:
+ *   -1 : critical error
+ *    0 : disk is marked as spare but serial is not set
+ *    1 : success
+ */
+int mark_spare(struct dl *disk)
+{
+       __u8 serial[MAX_RAID_SERIAL_LEN];
+       int ret_val = -1;
+
+       if (!disk)
+               return ret_val;
+
+       ret_val = 0;
+       if (!imsm_read_serial(disk->fd, NULL, serial)) {
+               /* Restore disk serial number, because takeover marks disk
+                * as failed and adds to serial ':0' before it becomes
+                * a spare disk.
+                */
+               serialcpy(disk->serial, serial);
+               serialcpy(disk->disk.serial, serial);
+               ret_val = 1;
+       }
+       disk->disk.status = SPARE_DISK;
+       disk->index = -1;
+
+       return ret_val;
+}
 
 static int add_to_super_imsm(struct supertype *st, mdu_disk_info_t *dk,
                             int fd, char *devname)
@@ -4388,7 +4627,6 @@ static int add_to_super_imsm(struct supertype *st, mdu_disk_info_t *dk,
        memset(dd, 0, sizeof(*dd));
        dd->major = major(stb.st_rdev);
        dd->minor = minor(stb.st_rdev);
-       dd->index = -1;
        dd->devname = devname ? strdup(devname) : NULL;
        dd->fd = fd;
        dd->e = NULL;
@@ -4405,7 +4643,7 @@ static int add_to_super_imsm(struct supertype *st, mdu_disk_info_t *dk,
        size /= 512;
        serialcpy(dd->disk.serial, dd->serial);
        dd->disk.total_blocks = __cpu_to_le32(size);
-       dd->disk.status = SPARE_DISK;
+       mark_spare(dd);
        if (sysfs_disk_to_scsi_id(fd, &id) == 0)
                dd->disk.scsi_id = __cpu_to_le32(id);
        else
@@ -4448,9 +4686,8 @@ static int remove_from_super_imsm(struct supertype *st, mdu_disk_info_t *dk)
        memset(dd, 0, sizeof(*dd));
        dd->major = dk->major;
        dd->minor = dk->minor;
-       dd->index = -1;
        dd->fd = -1;
-       dd->disk.status = SPARE_DISK;
+       mark_spare(dd);
        dd->action = DISK_REMOVE;
 
        dd->next = super->disk_mgmt_list;
@@ -4574,7 +4811,7 @@ static int write_super_imsm(struct supertype *st, int doclose)
 
        /* write the mpb for disks that compose raid devices */
        for (d = super->disks; d ; d = d->next) {
-               if (d->index < 0)
+               if (d->index < 0 || is_failed(&d->disk))
                        continue;
                if (store_imsm_mpb(d->fd, mpb))
                        fprintf(stderr, "%s: failed for device %d:%d %s\n",
@@ -4912,43 +5149,44 @@ static int is_raid_level_supported(const struct imsm_orom *orom, int level, int
        return 0;
 }
 
+static int imsm_default_chunk(const struct imsm_orom *orom)
+{
+       /* up to 512 if the plaform supports it, otherwise the platform max.
+        * 128 if no platform detected
+        */
+       int fs = max(7, orom ? fls(orom->sss) : 0);
+
+       return min(512, (1 << fs));
+}
 
 #define pr_vrb(fmt, arg...) (void) (verbose && fprintf(stderr, Name fmt, ##arg))
-/*
- * validate volume parameters with OROM/EFI capabilities
- */
 static int
 validate_geometry_imsm_orom(struct intel_super *super, int level, int layout,
                            int raiddisks, int *chunk, int verbose)
 {
-#if DEBUG
-       verbose = 1;
-#endif
-       /* validate container capabilities */
-       if (super->orom && raiddisks > super->orom->tds) {
-               if (verbose)
-                       fprintf(stderr, Name ": %d exceeds maximum number of"
-                               " platform supported disks: %d\n",
-                               raiddisks, super->orom->tds);
+       /* check/set platform and metadata limits/defaults */
+       if (super->orom && raiddisks > super->orom->dpa) {
+               pr_vrb(": platform supports a maximum of %d disks per array\n",
+                      super->orom->dpa);
                return 0;
        }
 
         /* capabilities of OROM tested - copied from validate_geometry_imsm_volume */
-       if (super->orom && (!is_raid_level_supported(super->orom, level,
-                                                    raiddisks))) {
+       if (!is_raid_level_supported(super->orom, level, raiddisks)) {
                pr_vrb(": platform does not support raid%d with %d disk%s\n",
                        level, raiddisks, raiddisks > 1 ? "s" : "");
                return 0;
        }
-       if (super->orom && level != 1) {
-               if (chunk && (*chunk == 0 || *chunk == UnSet))
-                       *chunk = imsm_orom_default_chunk(super->orom);
-               else if (chunk && !imsm_orom_has_chunk(super->orom, *chunk)) {
-                       pr_vrb(": platform does not support a chunk size of: "
-                              "%d\n", *chunk);
-                       return 0;
-               }
+
+       if (chunk && (*chunk == 0 || *chunk == UnSet))
+               *chunk = imsm_default_chunk(super->orom);
+
+       if (super->orom && chunk && !imsm_orom_has_chunk(super->orom, *chunk)) {
+               pr_vrb(": platform does not support a chunk size of: "
+                      "%d\n", *chunk);
+               return 0;
        }
+
        if (layout != imsm_level_to_layout(level)) {
                if (level == 5)
                        pr_vrb(": imsm raid 5 only supports the left-asymmetric layout\n");
@@ -4973,7 +5211,7 @@ static int validate_geometry_imsm_volume(struct supertype *st, int level,
 {
        struct stat stb;
        struct intel_super *super = st->sb;
-       struct imsm_super *mpb = super->anchor;
+       struct imsm_super *mpb;
        struct dl *dl;
        unsigned long long pos = 0;
        unsigned long long maxsize;
@@ -4984,6 +5222,14 @@ static int validate_geometry_imsm_volume(struct supertype *st, int level,
        if (!super)
                return 0;
 
+       mpb = super->anchor;
+
+       if (mpb->num_raid_devs > 0 && mpb->num_disks != raiddisks) {
+               fprintf(stderr, Name ": the option-rom requires all "
+                       "member disks to be a member of all volumes.\n");
+               return 0;
+       }
+
        if (!validate_geometry_imsm_orom(super, level, layout, raiddisks, chunk, verbose)) {
                fprintf(stderr, Name ": RAID gemetry validation failed. "
                        "Cannot proceed with the action(s).\n");
@@ -5100,6 +5346,15 @@ static int validate_geometry_imsm_volume(struct supertype *st, int level,
                        i += dl->extent_cnt;
 
        maxsize = merge_extents(super, i);
+
+       if (!check_env("IMSM_NO_PLATFORM") &&
+           mpb->num_raid_devs > 0 && size && size != maxsize) {
+               fprintf(stderr, Name ": attempting to create a second "
+                       "volume with size less then remaining space. "
+                       "Aborting...\n");
+               return 0;
+       }
+
        if (maxsize < size || maxsize == 0) {
                if (verbose)
                        fprintf(stderr, Name ": not enough space after merge (%llu < %llu)\n",
@@ -5209,7 +5464,11 @@ static int validate_geometry_imsm(struct supertype *st, int level, int layout,
        }
        
        if (!dev) {
-               if (st->sb && freesize) {
+               if (st->sb) {
+                       if (!validate_geometry_imsm_orom(st->sb, level, layout,
+                                                        raiddisks, chunk,
+                                                        verbose))
+                               return 0;
                        /* we are being asked to automatically layout a
                         * new volume based on the current contents of
                         * the container.  If the the parameters can be
@@ -5218,12 +5477,9 @@ static int validate_geometry_imsm(struct supertype *st, int level, int layout,
                         * created.  add_to_super and getinfo_super
                         * detect when autolayout is in progress.
                         */
-                       if (!validate_geometry_imsm_orom(st->sb, level, layout,
-                                                        raiddisks, chunk,
-                                                        verbose))
-                               return 0;
-                       return reserve_space(st, raiddisks, size,
-                                            chunk?*chunk:0, freesize);
+                       if (freesize)
+                               return reserve_space(st, raiddisks, size,
+                                                    chunk?*chunk:0, freesize);
                }
                return 1;
        }
@@ -5277,7 +5533,8 @@ static int validate_geometry_imsm(struct supertype *st, int level, int layout,
                        return validate_geometry_imsm_volume(st, level, layout,
                                                             raiddisks, chunk,
                                                             size, dev,
-                                                            freesize, verbose);
+                                                            freesize, 1)
+                               ? 1 : -1;
                }
        }
 
@@ -5298,9 +5555,8 @@ static void default_geometry_imsm(struct supertype *st, int *level, int *layout,
        if (level && layout && *layout == UnSet)
                *layout = imsm_level_to_layout(*level);
 
-       if (chunk && (*chunk == UnSet || *chunk == 0) && 
-           super && super->orom)
-               *chunk = imsm_orom_default_chunk(super->orom);
+       if (chunk && (*chunk == UnSet || *chunk == 0))
+               *chunk = imsm_default_chunk(super->orom);
 }
 
 static void handle_missing(struct intel_super *super, struct imsm_dev *dev);
@@ -5368,10 +5624,8 @@ static int kill_subarray_imsm(struct supertype *st)
                struct dl *d;
 
                for (d = super->disks; d; d = d->next)
-                       if (d->index > -2) {
-                               d->index = -1;
-                               d->disk.status = SPARE_DISK;
-                       }
+                       if (d->index > -2)
+                               mark_spare(d);
        }
 
        super->updates_pending++;
@@ -5430,6 +5684,7 @@ static int update_subarray_imsm(struct supertype *st, char *subarray,
 
        return 0;
 }
+#endif /* MDASSEMBLE */
 
 static int is_gen_migration(struct imsm_dev *dev)
 {
@@ -5444,7 +5699,6 @@ static int is_gen_migration(struct imsm_dev *dev)
 
        return 0;
 }
-#endif /* MDASSEMBLE */
 
 static int is_rebuilding(struct imsm_dev *dev)
 {
@@ -5464,6 +5718,25 @@ static int is_rebuilding(struct imsm_dev *dev)
                return 0;
 }
 
+static int is_initializing(struct imsm_dev *dev)
+{
+       struct imsm_map *migr_map;
+
+       if (!dev->vol.migr_state)
+               return 0;
+
+       if (migr_type(dev) != MIGR_INIT)
+               return 0;
+
+       migr_map = get_imsm_map(dev, 1);
+
+       if (migr_map->map_state == IMSM_T_STATE_UNINITIALIZED)
+               return 1;
+
+       return 0;
+
+}
+
 static void update_recovery_start(struct intel_super *super,
                                        struct imsm_dev *dev,
                                        struct mdinfo *array)
@@ -5515,20 +5788,24 @@ static struct mdinfo *container_content_imsm(struct supertype *st, char *subarra
        struct imsm_super *mpb = super->anchor;
        struct mdinfo *rest = NULL;
        unsigned int i;
-       int bbm_errors = 0;
+       int sb_errors = 0;
        struct dl *d;
        int spare_disks = 0;
 
        /* do not assemble arrays when not all attributes are supported */
        if (imsm_check_attributes(mpb->attributes) == 0) {
-               fprintf(stderr, Name ": IMSM metadata loading not allowed "
-                       "due to attributes incompatibility.\n");
-               return NULL;
+               sb_errors = 1;
+               fprintf(stderr, Name ": Unsupported attributes in IMSM metadata."
+                       "Arrays activation is blocked.\n");
        }
 
        /* check for bad blocks */
-       if (imsm_bbm_log_size(super->anchor))
-               bbm_errors = 1;
+       if (imsm_bbm_log_size(super->anchor)) {
+               fprintf(stderr, Name ": BBM log found in IMSM metadata."
+                       "Arrays activation is blocked.\n");
+               sb_errors = 1;
+       }
+
 
        /* count spare devices, not used in maps
         */
@@ -5567,18 +5844,6 @@ static struct mdinfo *container_content_imsm(struct supertype *st, char *subarra
                 */
 
                chunk = __le16_to_cpu(map->blocks_per_strip) >> 1;
-#ifndef MDASSEMBLE
-               if (!validate_geometry_imsm_orom(super,
-                                                get_imsm_raid_level(map), /* RAID level */
-                                                imsm_level_to_layout(get_imsm_raid_level(map)),
-                                                map->num_members, /* raid disks */
-                                                &chunk,
-                                                1 /* verbose */)) {
-                       fprintf(stderr, Name ": RAID gemetry validation failed. "
-                               "Cannot proceed with the action(s).\n");
-                       continue;
-               }
-#endif /* MDASSEMBLE */
                this = malloc(sizeof(*this));
                if (!this) {
                        fprintf(stderr, Name ": failed to allocate %zu bytes\n",
@@ -5589,6 +5854,29 @@ static struct mdinfo *container_content_imsm(struct supertype *st, char *subarra
                super->current_vol = i;
                getinfo_super_imsm_volume(st, this, NULL);
                this->next = rest;
+#ifndef MDASSEMBLE
+               /* mdadm does not support all metadata features- set the bit in all arrays state */
+               if (!validate_geometry_imsm_orom(super,
+                                                get_imsm_raid_level(map), /* RAID level */
+                                                imsm_level_to_layout(get_imsm_raid_level(map)),
+                                                map->num_members, /* raid disks */
+                                                &chunk,
+                                                1 /* verbose */)) {
+                       fprintf(stderr, Name ": IMSM RAID geometry validation"
+                               " failed.  Array %s activation is blocked.\n",
+                               dev->volume);
+                       this->array.state |=
+                         (1<<MD_SB_BLOCK_CONTAINER_RESHAPE) |
+                         (1<<MD_SB_BLOCK_VOLUME);
+               }
+#endif
+
+               /* if array has bad blocks, set suitable bit in all arrays state */
+               if (sb_errors)
+                       this->array.state |=
+                         (1<<MD_SB_BLOCK_CONTAINER_RESHAPE) |
+                         (1<<MD_SB_BLOCK_VOLUME);
+
                for (slot = 0 ; slot <  map->num_members; slot++) {
                        unsigned long long recovery_start;
                        struct mdinfo *info_d;
@@ -5677,17 +5965,16 @@ static struct mdinfo *container_content_imsm(struct supertype *st, char *subarra
                rest = this;
        }
 
-       /* if array has bad blocks, set suitable bit in array status */
-       if (bbm_errors)
-               rest->array.state |= (1<<MD_SB_BBM_ERRORS);
-
        return rest;
 }
 
 
-static __u8 imsm_check_degraded(struct intel_super *super, struct imsm_dev *dev, int failed)
+static __u8 imsm_check_degraded(struct intel_super *super, struct imsm_dev *dev,
+                               int failed, int look_in_map)
 {
-       struct imsm_map *map = get_imsm_map(dev, 0);
+       struct imsm_map *map;
+
+       map = get_imsm_map(dev, look_in_map);
 
        if (!failed)
                return map->map_state == IMSM_T_STATE_UNINITIALIZED ? 
@@ -5751,33 +6038,51 @@ static __u8 imsm_check_degraded(struct intel_super *super, struct imsm_dev *dev,
        return map->map_state;
 }
 
-static int imsm_count_failed(struct intel_super *super, struct imsm_dev *dev)
+static int imsm_count_failed(struct intel_super *super, struct imsm_dev *dev,
+                            int look_in_map)
 {
        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);
+       struct imsm_map *map = get_imsm_map(dev, MAP_0);
+       struct imsm_map *prev = get_imsm_map(dev, MAP_1);
+       struct imsm_map *map_for_loop;
        __u32 ord;
        int idx;
+       int idx_1;
 
        /* 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);
+       map_for_loop = map;
+       if (prev && (map->num_members < prev->num_members))
+               map_for_loop = prev;
+
+       for (i = 0; i < map_for_loop->num_members; i++) {
+               idx_1 = -255;
+               if (prev &&
+                   (look_in_map & MAP_1) && (i < prev->num_members)) {
+                       ord = __le32_to_cpu(prev->disk_ord_tbl[i]);
+                       idx_1 = ord_to_idx(ord);
+
+                       disk = get_imsm_disk(super, idx_1);
+                       if (!disk || is_failed(disk) || ord & IMSM_ORD_REBUILD)
+                               failed++;
+               }
+               if ((look_in_map & MAP_0) && (i < map->num_members)) {
+                       ord = __le32_to_cpu(map->disk_ord_tbl[i]);
+                       idx = ord_to_idx(ord);
 
-               disk = get_imsm_disk(super, idx);
-               if (!disk || is_failed(disk) || ord & IMSM_ORD_REBUILD)
-                       failed++;
+                       if (idx != idx_1) {
+                               disk = get_imsm_disk(super, idx);
+                               if (!disk || is_failed(disk) ||
+                                   ord & IMSM_ORD_REBUILD)
+                                       failed++;
+                       }
+               }
        }
 
        return failed;
@@ -5830,6 +6135,8 @@ static int mark_failure(struct imsm_dev *dev, struct imsm_disk *disk, int idx)
        __u32 ord;
        int slot;
        struct imsm_map *map;
+       char buf[MAX_RAID_SERIAL_LEN+3];
+       unsigned int len, shift = 0;
 
        /* new failures are always set in map[0] */
        map = get_imsm_map(dev, 0);
@@ -5842,8 +6149,21 @@ static int mark_failure(struct imsm_dev *dev, struct imsm_disk *disk, int idx)
        if (is_failed(disk) && (ord & IMSM_ORD_REBUILD))
                return 0;
 
+       memcpy(buf, disk->serial, MAX_RAID_SERIAL_LEN);
+       buf[MAX_RAID_SERIAL_LEN] = '\000';
+       strcat(buf, ":0");
+       if ((len = strlen(buf)) >= MAX_RAID_SERIAL_LEN)
+               shift = len - MAX_RAID_SERIAL_LEN + 1;
+       strncpy((char *)disk->serial, &buf[shift], MAX_RAID_SERIAL_LEN);
+
        disk->status |= FAILED_DISK;
        set_imsm_ord_tbl_ent(map, slot, idx | IMSM_ORD_REBUILD);
+       if (is_gen_migration(dev)) {
+               struct imsm_map *map2 = get_imsm_map(dev, 1);
+               if (slot < map2->num_members)
+                       set_imsm_ord_tbl_ent(map2, slot,
+                                            idx | IMSM_ORD_REBUILD);
+       }
        if (map->failed_disk_num == 0xff)
                map->failed_disk_num = slot;
        return 1;
@@ -5862,17 +6182,12 @@ static void mark_missing(struct imsm_dev *dev, struct imsm_disk *disk, int idx)
 
 static void handle_missing(struct intel_super *super, struct imsm_dev *dev)
 {
-       __u8 map_state;
        struct dl *dl;
-       int failed;
 
        if (!super->missing)
                return;
-       failed = imsm_count_failed(super, dev);
-       map_state = imsm_check_degraded(super, dev, failed);
 
        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++;
@@ -5946,7 +6261,7 @@ static void imsm_progress_container_reshape(struct intel_super *super)
                map->num_members = prev_disks;
                dev->vol.migr_state = 1;
                dev->vol.curr_migr_unit = 0;
-               dev->vol.migr_type = MIGR_GEN_MIGR;
+               set_migr_type(dev, MIGR_GEN_MIGR);
                for (i = prev_num_members;
                     i < map->num_members; i++)
                        set_imsm_ord_tbl_ent(map, i, i);
@@ -5971,8 +6286,8 @@ static int imsm_set_array_state(struct active_array *a, int consistent)
        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);
+       int failed = imsm_count_failed(super, dev, MAP_0);
+       __u8 map_state = imsm_check_degraded(super, dev, failed, MAP_0);
        __u32 blocks_per_unit;
 
        if (dev->vol.migr_state &&
@@ -5996,7 +6311,7 @@ static int imsm_set_array_state(struct active_array *a, int consistent)
                                if (0) {
                                struct imsm_map *map2 = get_imsm_map(dev, 1);
                                dev->vol.migr_state = 0;
-                               dev->vol.migr_type = 0;
+                               set_migr_type(dev, 0);
                                dev->vol.curr_migr_unit = 0;
                                memcpy(map, map2, sizeof_imsm_map(map2));
                                super->updates_pending++;
@@ -6052,11 +6367,12 @@ static int imsm_set_array_state(struct active_array *a, int consistent)
                 */
                if (is_resyncing(dev)) {
                        dprintf("imsm: mark resync done\n");
-                       end_migration(dev, map_state);
+                       end_migration(dev, super, map_state);
                        super->updates_pending++;
                        a->last_checkpoint = 0;
                }
-       } else if (!is_resyncing(dev) && !failed) {
+       } else if ((!is_resyncing(dev) && !failed) &&
+                  (imsm_reshape_blocks_arrays_changes(super) == 0)) {
                /* mark the start of the init process if nothing is failed */
                dprintf("imsm: mark resync start\n");
                if (map->map_state == IMSM_T_STATE_UNINITIALIZED)
@@ -6128,7 +6444,7 @@ static void imsm_set_disk(struct active_array *a, int n, int state)
 
        dprintf("imsm: set_disk %d:%x\n", n, state);
 
-       ord = get_imsm_ord_tbl_ent(dev, n, -1);
+       ord = get_imsm_ord_tbl_ent(dev, n, 0);
        disk = get_imsm_disk(super, ord_to_idx(ord));
 
        /* check for new failures */
@@ -6145,47 +6461,93 @@ static void imsm_set_disk(struct active_array *a, int n, int state)
                super->updates_pending++;
        }
 
-       failed = imsm_count_failed(super, dev);
-       map_state = imsm_check_degraded(super, dev, failed);
+       failed = imsm_count_failed(super, dev, MAP_0);
+       map_state = imsm_check_degraded(super, dev, failed, MAP_0);
 
        /* 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++;
-               a->last_checkpoint = 0;
-       } 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++;
-               a->last_checkpoint = 0;
-       } 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++;
-               a->last_checkpoint = 0;
-       } else if (is_gen_migration(dev)) {
-               dprintf("imsm: Detected General Migration in state: ");
-               if (map_state == IMSM_T_STATE_NORMAL) {
-                       end_migration(dev, map_state);
+       dprintf("imsm: Detected transition to state ");
+       switch (map_state) {
+       case IMSM_T_STATE_NORMAL: /* transition to normal state */
+               dprintf("normal: ");
+               if (is_rebuilding(dev)) {
+                       dprintf("while rebuilding");
+                       end_migration(dev, super, map_state);
                        map = get_imsm_map(dev, 0);
                        map->failed_disk_num = ~0;
-                       dprintf("normal\n");
-               } else {
-                       if (map_state == IMSM_T_STATE_DEGRADED) {
-                               printf("degraded\n");
-                               end_migration(dev, map_state);
-                       } else {
-                               dprintf("failed\n");
-                       }
-                       map->map_state = map_state;
-               }
-               super->updates_pending++;
+                       super->updates_pending++;
+                       a->last_checkpoint = 0;
+                       break;
+               }
+               if (is_gen_migration(dev)) {
+                       dprintf("while general migration");
+                       if (a->last_checkpoint >= a->info.component_size)
+                               end_migration(dev, super, map_state);
+                       else
+                               map->map_state = map_state;
+                       map = get_imsm_map(dev, 0);
+                       map->failed_disk_num = ~0;
+                       super->updates_pending++;
+                       break;
+               }
+       break;
+       case IMSM_T_STATE_DEGRADED: /* transition to degraded state */
+               dprintf("degraded: ");
+               if ((map->map_state != map_state) &&
+                   !dev->vol.migr_state) {
+                       dprintf("mark degraded");
+                       map->map_state = map_state;
+                       super->updates_pending++;
+                       a->last_checkpoint = 0;
+                       break;
+               }
+               if (is_rebuilding(dev)) {
+                       dprintf("while rebuilding.");
+                       if (map->map_state != map_state)  {
+                               dprintf(" Map state change");
+                               end_migration(dev, super, map_state);
+                               super->updates_pending++;
+                       }
+                       break;
+               }
+               if (is_gen_migration(dev)) {
+                       dprintf("while general migration");
+                       if (a->last_checkpoint >= a->info.component_size)
+                               end_migration(dev, super, map_state);
+                       else {
+                               map->map_state = map_state;
+                               manage_second_map(super, dev);
+                       }
+                       super->updates_pending++;
+                       break;
+               }
+               if (is_initializing(dev)) {
+                       dprintf("while initialization.");
+                       map->map_state = map_state;
+                       super->updates_pending++;
+                       break;
+               }
+       break;
+       case IMSM_T_STATE_FAILED: /* transition to failed state */
+               dprintf("failed: ");
+               if (is_gen_migration(dev)) {
+                       dprintf("while general migration");
+                       map->map_state = map_state;
+                       super->updates_pending++;
+                       break;
+               }
+               if (map->map_state != map_state) {
+                       dprintf("mark failed");
+                       end_migration(dev, super, map_state);
+                       super->updates_pending++;
+                       a->last_checkpoint = 0;
+                       break;
+               }
+       break;
+       default:
+               dprintf("state %i\n", map_state);
        }
+       dprintf("\n");
+
 }
 
 static int store_imsm_mpb(int fd, struct imsm_super *mpb)
@@ -6375,7 +6737,8 @@ static int imsm_rebuild_allowed(struct supertype *cont, int dev_idx, int failed)
 
        dev2 = get_imsm_dev(cont->sb, dev_idx);
        if (dev2) {
-               state = imsm_check_degraded(cont->sb, dev2, failed);
+               state = imsm_check_degraded(cont->sb, dev2, failed,
+                                           MAP_0);
                if (state == IMSM_T_STATE_FAILED) {
                        map = get_imsm_map(dev2, 0);
                        if (!map)
@@ -6443,10 +6806,16 @@ static struct mdinfo *imsm_activate_spare(struct active_array *a,
        dprintf("imsm: activate spare: inst=%d failed=%d (%d) level=%d\n",
                inst, failed, a->info.array.raid_disks, a->info.array.level);
 
-       if (dev->vol.migr_state &&
-           dev->vol.migr_type == MIGR_GEN_MIGR)
-               /* No repair during migration */
+       if (imsm_reshape_blocks_arrays_changes(super))
+                       return NULL;
+
+       /* Cannot activate another spare if rebuild is in progress already
+        */
+       if (is_rebuilding(dev)) {
+               dprintf("imsm: No spare activation allowed. "
+                       "Rebuild in progress already.\n");
                return NULL;
+       }
 
        if (a->info.array.level == 4)
                /* No repair for takeovered array
@@ -6454,7 +6823,8 @@ static struct mdinfo *imsm_activate_spare(struct active_array *a,
                 */
                return NULL;
 
-       if (imsm_check_degraded(super, dev, failed) != IMSM_T_STATE_DEGRADED)
+       if (imsm_check_degraded(super, dev, failed, MAP_0) !=
+                       IMSM_T_STATE_DEGRADED)
                return NULL;
 
        /*
@@ -6463,9 +6833,9 @@ static struct mdinfo *imsm_activate_spare(struct active_array *a,
         * are removed from container.
         */
        if (failed) {
-               dprintf("found failed disks in %s, check if there another"
+               dprintf("found failed disks in %.*s, check if there another"
                        "failed sub-array.\n",
-                       dev->volume);
+                       MAX_RAID_SERIAL_LEN, dev->volume);
                /* check if states of the other volumes allow for rebuild */
                for (i = 0; i <  super->anchor->num_raid_devs; i++) {
                        if (i != inst) {
@@ -6495,9 +6865,9 @@ static struct mdinfo *imsm_activate_spare(struct active_array *a,
                 */
                dl = imsm_readd(super, i, a);
                if (!dl)
-                       dl = imsm_add_spare(super, i, a, 0, NULL);
+                       dl = imsm_add_spare(super, i, a, 0, rv);
                if (!dl)
-                       dl = imsm_add_spare(super, i, a, 1, NULL);
+                       dl = imsm_add_spare(super, i, a, 1, rv);
                if (!dl)
                        continue;
  
@@ -6534,8 +6904,6 @@ static struct mdinfo *imsm_activate_spare(struct active_array *a,
                num_spares++;
                dprintf("%x:%x to be %d at %llu\n", dl->major, dl->minor,
                        i, di->data_offset);
-
-               break;
        }
 
        if (!rv)
@@ -6805,6 +7173,121 @@ error_disk_add:
        return ret_val;
 }
 
+static int apply_update_activate_spare(struct imsm_update_activate_spare *u,
+                                      struct intel_super *super,       
+                                      struct active_array *active_array)
+{
+       struct imsm_super *mpb = super->anchor;
+       struct imsm_dev *dev = get_imsm_dev(super, u->array);
+       struct imsm_map *map = get_imsm_map(dev, 0);
+       struct imsm_map *migr_map;
+       struct active_array *a;
+       struct imsm_disk *disk;
+       __u8 to_state;
+       struct dl *dl;
+       unsigned int found;
+       int failed;
+       int victim;
+       int i;
+       int second_map_created = 0;
+
+       for (; u; u = u->next) {
+               victim = get_imsm_disk_idx(dev, u->slot, -1);
+
+               if (victim < 0)
+                       return 0;
+
+               for (dl = super->disks; dl; dl = dl->next)
+                       if (dl == u->dl)
+                               break;
+
+               if (!dl) {
+                       fprintf(stderr, "error: imsm_activate_spare passed "
+                               "an unknown disk (index: %d)\n",
+                               u->dl->index);
+                       return 0;
+               }
+
+               /* count failures (excluding rebuilds and the victim)
+                * to determine map[0] state
+                */
+               failed = 0;
+               for (i = 0; i < map->num_members; i++) {
+                       if (i == u->slot)
+                               continue;
+                       disk = get_imsm_disk(super,
+                                            get_imsm_disk_idx(dev, i, -1));
+                       if (!disk || is_failed(disk))
+                               failed++;
+               }
+
+               /* adding a pristine spare, assign a new index */
+               if (dl->index < 0) {
+                       dl->index = super->anchor->num_disks;
+                       super->anchor->num_disks++;
+               }
+               disk = &dl->disk;
+               disk->status |= CONFIGURED_DISK;
+               disk->status &= ~SPARE_DISK;
+
+               /* mark rebuild */
+               to_state = imsm_check_degraded(super, dev, failed,
+                                              MAP_0);
+               if (!second_map_created) {
+                       second_map_created = 1;
+                       map->map_state = IMSM_T_STATE_DEGRADED;
+                       migrate(dev, super, to_state, MIGR_REBUILD);
+               } else
+                       map->map_state = to_state;
+               migr_map = get_imsm_map(dev, 1);
+               set_imsm_ord_tbl_ent(map, u->slot, dl->index);
+               set_imsm_ord_tbl_ent(migr_map, u->slot,
+                                    dl->index | IMSM_ORD_REBUILD);
+
+               /* update the family_num to mark a new container
+                * generation, being careful to record the existing
+                * family_num in orig_family_num to clean up after
+                * earlier mdadm versions that neglected to set it.
+                */
+               if (mpb->orig_family_num == 0)
+                       mpb->orig_family_num = mpb->family_num;
+               mpb->family_num += super->random;
+
+               /* count arrays using the victim in the metadata */
+               found = 0;
+               for (a = active_array; a ; a = a->next) {
+                       dev = get_imsm_dev(super, a->info.container_member);
+                       map = get_imsm_map(dev, 0);
+
+                       if (get_imsm_disk_slot(map, victim) >= 0)
+                               found++;
+               }
+
+               /* delete the victim if it is no longer being
+                * utilized anywhere
+                */
+               if (!found) {
+                       struct dl **dlp;
+
+                       /* We know that 'manager' isn't touching anything,
+                        * so it is safe to delete
+                        */
+                       for (dlp = &super->disks; *dlp; dlp = &(*dlp)->next)
+                               if ((*dlp)->index == victim)
+                                       break;
+
+                       /* victim may be on the missing list */
+                       if (!*dlp)
+                               for (dlp = &super->missing; *dlp;
+                                    dlp = &(*dlp)->next)
+                                       if ((*dlp)->index == victim)
+                                               break;
+                       imsm_delete(super, dlp, victim);
+               }
+       }
+
+       return 1;
+}
 
 static int apply_reshape_container_disks_update(struct imsm_update_reshape *u,
                                                struct intel_super *super,
@@ -6878,7 +7361,7 @@ static int apply_reshape_container_disks_update(struct imsm_update_reshape *u,
                        devices_to_reshape--;
                        newdev->vol.migr_state = 1;
                        newdev->vol.curr_migr_unit = 0;
-                       newdev->vol.migr_type = MIGR_GEN_MIGR;
+                       set_migr_type(newdev, MIGR_GEN_MIGR);
                        newmap->num_members = u->new_raid_disks;
                        for (i = 0; i < delta_disks; i++) {
                                set_imsm_ord_tbl_ent(newmap,
@@ -6934,7 +7417,8 @@ static int apply_takeover_update(struct imsm_update_takeover *u,
 
        if (u->direction == R10_TO_R0) {
                /* Number of failed disks must be half of initial disk number */
-               if (imsm_count_failed(super, dev) != (map->num_members / 2))
+               if (imsm_count_failed(super, dev, MAP_0) !=
+                               (map->num_members / 2))
                        return 0;
 
                /* iterate through devices to mark removed disks as spare */
@@ -6948,8 +7432,7 @@ static int apply_takeover_update(struct imsm_update_takeover *u,
                                        if (du->index > idx)
                                                du->index--;
                                /* mark as spare disk */
-                               dm->disk.status = SPARE_DISK;
-                               dm->index = -1;
+                               mark_spare(dm);
                        }
                }
                /* update map */
@@ -7009,7 +7492,7 @@ static int apply_takeover_update(struct imsm_update_takeover *u,
        for (du = super->missing; du; du = du->next)
                if (du->index >= 0) {
                        set_imsm_ord_tbl_ent(map, du->index, du->index);
-                       mark_missing(dev_new, &du->disk, du->index);
+                       mark_missing(dv->dev, &du->disk, du->index);
                }
 
        return 1;
@@ -7100,99 +7583,8 @@ static void imsm_process_update(struct supertype *st,
        }
        case update_activate_spare: {
                struct imsm_update_activate_spare *u = (void *) update->buf; 
-               struct imsm_dev *dev = get_imsm_dev(super, u->array);
-               struct imsm_map *map = get_imsm_map(dev, 0);
-               struct imsm_map *migr_map;
-               struct active_array *a;
-               struct imsm_disk *disk;
-               __u8 to_state;
-               struct dl *dl;
-               unsigned int found;
-               int failed;
-               int victim = get_imsm_disk_idx(dev, u->slot, -1);
-               int i;
-
-               for (dl = super->disks; dl; dl = dl->next)
-                       if (dl == u->dl)
-                               break;
-
-               if (!dl) {
-                       fprintf(stderr, "error: imsm_activate_spare passed "
-                               "an unknown disk (index: %d)\n",
-                               u->dl->index);
-                       return;
-               }
-
-               super->updates_pending++;
-               /* count failures (excluding rebuilds and the victim)
-                * to determine map[0] state
-                */
-               failed = 0;
-               for (i = 0; i < map->num_members; i++) {
-                       if (i == u->slot)
-                               continue;
-                       disk = get_imsm_disk(super,
-                                            get_imsm_disk_idx(dev, i, -1));
-                       if (!disk || is_failed(disk))
-                               failed++;
-               }
-
-               /* adding a pristine spare, assign a new index */
-               if (dl->index < 0) {
-                       dl->index = super->anchor->num_disks;
-                       super->anchor->num_disks++;
-               }
-               disk = &dl->disk;
-               disk->status |= CONFIGURED_DISK;
-               disk->status &= ~SPARE_DISK;
-
-               /* mark rebuild */
-               to_state = imsm_check_degraded(super, dev, failed);
-               map->map_state = IMSM_T_STATE_DEGRADED;
-               migrate(dev, super, to_state, MIGR_REBUILD);
-               migr_map = get_imsm_map(dev, 1);
-               set_imsm_ord_tbl_ent(map, u->slot, dl->index);
-               set_imsm_ord_tbl_ent(migr_map, u->slot, dl->index | IMSM_ORD_REBUILD);
-
-               /* update the family_num to mark a new container
-                * generation, being careful to record the existing
-                * family_num in orig_family_num to clean up after
-                * earlier mdadm versions that neglected to set it.
-                */
-               if (mpb->orig_family_num == 0)
-                       mpb->orig_family_num = mpb->family_num;
-               mpb->family_num += super->random;
-
-               /* count arrays using the victim in the metadata */
-               found = 0;
-               for (a = st->arrays; a ; a = a->next) {
-                       dev = get_imsm_dev(super, a->info.container_member);
-                       map = get_imsm_map(dev, 0);
-
-                       if (get_imsm_disk_slot(map, victim) >= 0)
-                               found++;
-               }
-
-               /* delete the victim if it is no longer being
-                * utilized anywhere
-                */
-               if (!found) {
-                       struct dl **dlp;
-
-                       /* We know that 'manager' isn't touching anything,
-                        * so it is safe to delete
-                        */
-                       for (dlp = &super->disks; *dlp; dlp = &(*dlp)->next)
-                               if ((*dlp)->index == victim)
-                                       break;
-
-                       /* victim may be on the missing list */
-                       if (!*dlp)
-                               for (dlp = &super->missing; *dlp; dlp = &(*dlp)->next)
-                                       if ((*dlp)->index == victim)
-                                               break;
-                       imsm_delete(super, dlp, victim);
-               }
+               if (apply_update_activate_spare(u, super, st->arrays))
+                       super->updates_pending++;
                break;
        }
        case update_create_array: {
@@ -7701,6 +8093,75 @@ static void imsm_delete(struct intel_super *super, struct dl **dlp, unsigned ind
        }
 }
 #endif /* MDASSEMBLE */
+
+static void close_targets(int *targets, int new_disks)
+{
+       int i;
+
+       if (!targets)
+               return;
+
+       for (i = 0; i < new_disks; i++) {
+               if (targets[i] >= 0) {
+                       close(targets[i]);
+                       targets[i] = -1;
+               }
+       }
+}
+
+static int imsm_get_allowed_degradation(int level, int raid_disks,
+                                       struct intel_super *super,
+                                       struct imsm_dev *dev)
+{
+       switch (level) {
+       case 10:{
+               int ret_val = 0;
+               struct imsm_map *map;
+               int i;
+
+               ret_val = raid_disks/2;
+               /* check map if all disks pairs not failed
+                * in both maps
+                */
+               map = get_imsm_map(dev, 0);
+               for (i = 0; i < ret_val; i++) {
+                       int degradation = 0;
+                       if (get_imsm_disk(super, i) == NULL)
+                               degradation++;
+                       if (get_imsm_disk(super, i + 1) == NULL)
+                               degradation++;
+                       if (degradation == 2)
+                               return 0;
+               }
+               map = get_imsm_map(dev, 1);
+               /* if there is no second map
+                * result can be returned
+                */
+               if (map == NULL)
+                       return ret_val;
+               /* check degradation in second map
+                */
+               for (i = 0; i < ret_val; i++) {
+                       int degradation = 0;
+               if (get_imsm_disk(super, i) == NULL)
+                               degradation++;
+                       if (get_imsm_disk(super, i + 1) == NULL)
+                               degradation++;
+                       if (degradation == 2)
+                               return 0;
+               }
+               return ret_val;
+       }
+       case 5:
+               return 1;
+       case 6:
+               return 2;
+       default:
+               return 0;
+       }
+}
+
+
 /*******************************************************************************
  * Function:   open_backup_targets
  * Description:        Function opens file descriptors for all devices given in
@@ -7709,13 +8170,21 @@ static void imsm_delete(struct intel_super *super, struct dl **dlp, unsigned ind
  *     info            : general array info
  *     raid_disks      : number of disks
  *     raid_fds        : table of device's file descriptors
+ *     super           : intel super for raid10 degradation check
+ *     dev             : intel device for raid10 degradation check
  * Returns:
  *      0 : success
  *     -1 : fail
  ******************************************************************************/
-int open_backup_targets(struct mdinfo *info, int raid_disks, int *raid_fds)
+int open_backup_targets(struct mdinfo *info, int raid_disks, int *raid_fds,
+                       struct intel_super *super, struct imsm_dev *dev)
 {
        struct mdinfo *sd;
+       int i;
+       int opened = 0;
+
+       for (i = 0; i < raid_disks; i++)
+               raid_fds[i] = -1;
 
        for (sd = info->devs ; sd ; sd = sd->next) {
                char *dn;
@@ -7734,8 +8203,19 @@ int open_backup_targets(struct mdinfo *info, int raid_disks, int *raid_fds)
                raid_fds[sd->disk.raid_disk] = dev_open(dn, O_RDWR);
                if (raid_fds[sd->disk.raid_disk] < 0) {
                        fprintf(stderr, "cannot open component\n");
-                       return -1;
+                       continue;
                }
+               opened++;
+       }
+       /* check if maximum array degradation level is not exceeded
+       */
+       if ((raid_disks - opened) >
+                       imsm_get_allowed_degradation(info->new_level,
+                                                    raid_disks,
+                                                    super, dev)) {
+               fprintf(stderr, "Not enough disks can be opened.\n");
+               close_targets(raid_fds, raid_disks);
+               return -2;
        }
        return 0;
 }
@@ -7868,7 +8348,8 @@ int save_backup_imsm(struct supertype *st,
                target_offsets[i] -= start/data_disks;
        }
 
-       if (open_backup_targets(info, new_disks, targets))
+       if (open_backup_targets(info, new_disks, targets,
+                               super, dev))
                goto abort;
 
        dest_layout = imsm_level_to_layout(map_dest->raid_level);
@@ -7894,9 +8375,7 @@ int save_backup_imsm(struct supertype *st,
 
 abort:
        if (targets) {
-               for (i = 0; i < new_disks; i++)
-                       if (targets[i] >= 0)
-                               close(targets[i]);
+               close_targets(targets, new_disks);
                free(targets);
        }
        free(target_offsets);
@@ -7985,7 +8464,6 @@ int recover_backup_imsm(struct supertype *st, struct mdinfo *info)
        unsigned long num_migr_units = __le32_to_cpu(migr_rec->num_migr_units);
        char buffer[20];
        int skipped_disks = 0;
-       int max_degradation;
 
        err = sysfs_get_str(info, NULL, "array_state", (char *)buffer, 20);
        if (err < 1)
@@ -8009,7 +8487,6 @@ int recover_backup_imsm(struct supertype *st, struct mdinfo *info)
 
        map_dest = get_imsm_map(id->dev, 0);
        new_disks = map_dest->num_members;
-       max_degradation = new_disks - imsm_num_data_members(id->dev, 0);
 
        read_offset = (unsigned long long)
                        __le32_to_cpu(migr_rec->ckpt_area_pba) * 512;
@@ -8025,7 +8502,11 @@ int recover_backup_imsm(struct supertype *st, struct mdinfo *info)
        if (!targets)
                goto abort;
 
-       open_backup_targets(info, new_disks, targets);
+       if (open_backup_targets(info, new_disks, targets, super, id->dev)) {
+               fprintf(stderr,
+                       Name ": Cannot open some devices belonging to array.\n");
+               goto abort;
+       }
 
        for (i = 0; i < new_disks; i++) {
                if (targets[i] < 0) {
@@ -8036,29 +8517,36 @@ int recover_backup_imsm(struct supertype *st, struct mdinfo *info)
                        fprintf(stderr,
                                Name ": Cannot seek to block: %s\n",
                                strerror(errno));
-                       goto abort;
+                       skipped_disks++;
+                       continue;
                }
                if ((unsigned)read(targets[i], buf, unit_len) != unit_len) {
                        fprintf(stderr,
                                Name ": Cannot read copy area block: %s\n",
                                strerror(errno));
-                       goto abort;
+                       skipped_disks++;
+                       continue;
                }
                if (lseek64(targets[i], write_offset, SEEK_SET) < 0) {
                        fprintf(stderr,
                                Name ": Cannot seek to block: %s\n",
                                strerror(errno));
-                       goto abort;
+                       skipped_disks++;
+                       continue;
                }
                if ((unsigned)write(targets[i], buf, unit_len) != unit_len) {
                        fprintf(stderr,
                                Name ": Cannot restore block: %s\n",
                                strerror(errno));
-                       goto abort;
+                       skipped_disks++;
+                       continue;
                }
        }
 
-       if (skipped_disks > max_degradation) {
+       if (skipped_disks > imsm_get_allowed_degradation(info->new_level,
+                                                        new_disks,
+                                                        super,
+                                                        id->dev)) {
                fprintf(stderr,
                        Name ": Cannot restore data from backup."
                        " Too many failed disks\n");
@@ -8301,6 +8789,7 @@ static int imsm_create_metadata_update_for_reshape(
            || delta_disks > spares->array.spare_disks) {
                fprintf(stderr, Name ": imsm: ERROR: Cannot get spare devices "
                        "for %s.\n", geo->dev_name);
+               i = -1;
                goto abort;
        }
 
@@ -8450,6 +8939,8 @@ enum imsm_reshape_type imsm_analyze_change(struct supertype *st,
        int change = -1;
        int check_devs = 0;
        int chunk;
+       int devNumChange=0;
+       int layout = -1;
 
        getinfo_super_imsm_volume(st, &info, NULL);
        if ((geo->level != info.array.level) &&
@@ -8467,23 +8958,23 @@ enum imsm_reshape_type imsm_analyze_change(struct supertype *st,
                                        change = -1;
                                        goto analyse_change_exit;
                                }
+                               layout =  geo->layout;
                                check_devs = 1;
-                       }
-                       if (geo->level == 10) {
+                               devNumChange = 1; /* parity disk added */
+                       } else if (geo->level == 10) {
                                change = CH_TAKEOVER;
                                check_devs = 1;
+                               devNumChange = 2; /* two mirrors added */
+                               layout = 0x102; /* imsm supported layout */
                        }
                        break;
                case 1:
-                       if (geo->level == 0) {
-                               change = CH_TAKEOVER;
-                               check_devs = 1;
-                       }
-                       break;
                case 10:
                        if (geo->level == 0) {
                                change = CH_TAKEOVER;
                                check_devs = 1;
+                               devNumChange = -(geo->raid_disks/2);
+                               layout = 0; /* imsm raid0 layout */
                        }
                        break;
                }
@@ -8530,8 +9021,8 @@ enum imsm_reshape_type imsm_analyze_change(struct supertype *st,
        chunk = geo->chunksize / 1024;
        if (!validate_geometry_imsm(st,
                                    geo->level,
-                                   geo->layout,
-                                   geo->raid_disks,
+                                   layout,
+                                   geo->raid_disks + devNumChange,
                                    &chunk,
                                    geo->size,
                                    0, 0, 1))
@@ -8660,8 +9151,9 @@ static int imsm_reshape_super(struct supertype *st, long long size, int level,
                dprintf("imsm: info: Volume operation\n");
                /* find requested device */
                while (dev) {
-                       imsm_find_array_minor_by_subdev(dev->index, st->container_dev, &devnum);
-                       if (devnum == geo.dev_id)
+                       if (imsm_find_array_minor_by_subdev(
+                                   dev->index, st->container_dev, &devnum) == 0
+                           && devnum == geo.dev_id)
                                break;
                        dev = dev->next;
                }