]> git.ipfire.org Git - thirdparty/mdadm.git/blobdiff - super-intel.c
imsm: FIX: Remove longer map request
[thirdparty/mdadm.git] / super-intel.c
index 3525dae6681dff82483ca5e2d9fb679f2cf93413..9b3d6bead456d018101db66fa44169efb799b643 100644 (file)
@@ -106,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 */
@@ -662,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;
 
 }
 
@@ -1014,6 +1030,8 @@ static unsigned long long min_acceptable_spare_size_imsm(struct supertype *st)
        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);
@@ -1110,9 +1128,13 @@ 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");
@@ -1141,8 +1163,6 @@ static void print_imsm_disk(struct imsm_disk *disk, 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;
@@ -1572,8 +1592,8 @@ static int ahci_enumerate_ports(const char *hba_path, int port_count, int host_b
                                printf(" (%.*s)\n", MAX_RAID_SERIAL_LEN, buf);
                        else
                                printf(" ()\n");
+                       close(fd);
                }
-               close(fd);
                free(path);
                path = NULL;
        }
@@ -2293,8 +2313,7 @@ static void getinfo_super_imsm_volume(struct supertype *st, struct mdinfo *info,
        info->custom_array_size   |= __le32_to_cpu(dev->size_low);
        info->recovery_blocked = imsm_reshape_blocks_arrays_changes(st->sb);
 
-       if (prev_map && map->map_state == prev_map->map_state &&
-           (migr_type(dev) == MIGR_GEN_MIGR)) {
+       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);
@@ -2465,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)
 {
@@ -2527,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) &&
@@ -2868,8 +2907,10 @@ static void fd2devname(int fd, char *name)
        
        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);
@@ -4200,6 +4241,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);
@@ -4282,7 +4324,11 @@ static int init_super_imsm_volume(struct supertype *st, mdu_array_info_t *info,
        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->failed_disks ? IMSM_T_STATE_DEGRADED : 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) {
@@ -4461,6 +4507,20 @@ static int add_to_super_imsm_volume(struct supertype *st, mdu_disk_info_t *dk,
                        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;
                }
@@ -5138,7 +5198,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;
@@ -5149,6 +5209,8 @@ 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");
@@ -5271,6 +5333,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",
@@ -5380,7 +5451,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
@@ -5389,12 +5464,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;
        }
@@ -5599,6 +5671,7 @@ static int update_subarray_imsm(struct supertype *st, char *subarray,
 
        return 0;
 }
+#endif /* MDASSEMBLE */
 
 static int is_gen_migration(struct imsm_dev *dev)
 {
@@ -5613,7 +5686,6 @@ static int is_gen_migration(struct imsm_dev *dev)
 
        return 0;
 }
-#endif /* MDASSEMBLE */
 
 static int is_rebuilding(struct imsm_dev *dev)
 {
@@ -5758,8 +5830,8 @@ static struct mdinfo *container_content_imsm(struct supertype *st, char *subarra
                                                 map->num_members, /* raid disks */
                                                 &chunk,
                                                 1 /* verbose */)) {
-                       fprintf(stderr, Name ": IMSM RAID gemetry validation failed. "
-                               "Array %s activation is blocked.\n",
+                       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) |
@@ -5865,9 +5937,12 @@ static struct mdinfo *container_content_imsm(struct supertype *st, char *subarra
 }
 
 
-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 ? 
@@ -5931,33 +6006,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;
@@ -6033,6 +6126,12 @@ static int mark_failure(struct imsm_dev *dev, struct imsm_disk *disk, int idx)
 
        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;
@@ -6051,17 +6150,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++;
@@ -6160,8 +6254,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 &&
@@ -6318,7 +6412,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 */
@@ -6335,8 +6429,8 @@ 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)) {
@@ -6360,20 +6454,26 @@ static void imsm_set_disk(struct active_array *a, int n, int state)
                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);
+
+               switch (map_state) {
+               case IMSM_T_STATE_NORMAL:
+                       dprintf("normal\n");
+                       if (a->last_checkpoint >= a->info.component_size)
+                               end_migration(dev, 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");
+                       break;
+               case IMSM_T_STATE_DEGRADED:
+                       dprintf("degraded\n");
+                       if (a->last_checkpoint >= a->info.component_size)
                                end_migration(dev, map_state);
-                       } else {
-                               dprintf("failed\n");
-                       }
-                       map->map_state = map_state;
+                       else
+                               manage_second_map(super, dev);
+                       break;
+               default:
+                       dprintf("failed\n");
                }
+               map->map_state = map_state;
                super->updates_pending++;
        }
 }
@@ -6565,7 +6665,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)
@@ -6642,7 +6743,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;
 
        /*
@@ -7049,7 +7151,8 @@ static int apply_update_activate_spare(struct imsm_update_activate_spare *u,
                disk->status &= ~SPARE_DISK;
 
                /* mark rebuild */
-               to_state = imsm_check_degraded(super, dev, failed);
+               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;
@@ -7234,7 +7337,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 */
@@ -7909,6 +8013,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
@@ -7917,13 +8090,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;
@@ -7942,8 +8123,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;
 }
@@ -8076,7 +8268,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);
@@ -8102,9 +8295,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);
@@ -8193,7 +8384,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)
@@ -8217,7 +8407,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;
@@ -8233,7 +8422,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) {
@@ -8244,29 +8437,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");
@@ -8659,6 +8859,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) &&
@@ -8676,23 +8878,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;
                }
@@ -8739,8 +8941,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))