]> git.ipfire.org Git - thirdparty/mdadm.git/blobdiff - super-intel.c
imsm: incorrect incremental behavior because of wrong index used
[thirdparty/mdadm.git] / super-intel.c
index e9d9af80343a3b7d4fb3d31632c9839d021504a2..a0672bf3b5803d1782344fccda79ace43dccd7ec 100644 (file)
@@ -88,6 +88,7 @@
 
 #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. */
@@ -827,6 +828,8 @@ 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 */
@@ -840,7 +843,7 @@ static struct extent *get_extents(struct intel_super *super, struct dl *dl)
         * IMSM_RESERVED_SECTORS region
         */
        if (dl->index == -1)
-               reservation = MPB_SECTOR_CNT;
+               reservation = imsm_min_reserved_sectors(super);
        else
                reservation = MPB_SECTOR_CNT + IMSM_RESERVED_SECTORS;
 
@@ -933,6 +936,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)
 {
@@ -959,8 +1007,10 @@ 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;
 }
 
@@ -968,6 +1018,8 @@ static unsigned long long min_acceptable_spare_size_imsm(struct supertype *st)
 static __u64 blocks_per_migr_unit(struct intel_super *super,
                                  struct imsm_dev *dev);
 
+static int is_gen_migration(struct imsm_dev *dev);
+
 static void print_imsm_dev(struct intel_super *super,
                           struct imsm_dev *dev,
                           char *uuid,
@@ -1060,9 +1112,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");
@@ -1091,8 +1147,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;
@@ -1522,8 +1576,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;
        }
@@ -2185,6 +2239,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;
@@ -2217,7 +2295,10 @@ 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 (prev_map && map->map_state == prev_map->map_state &&
+           (migr_type(dev) == MIGR_GEN_MIGR)) {
                info->reshape_active = 1;
                info->new_level = get_imsm_raid_level(map);
                info->new_layout = imsm_level_to_layout(info->new_level);
@@ -2227,7 +2308,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.
@@ -2296,8 +2377,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) {
@@ -2438,6 +2520,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;
@@ -2450,13 +2533,13 @@ static void getinfo_super_imsm(struct supertype *st, struct mdinfo *info, char *
 
                failed = imsm_count_failed(super, dev);
                state = imsm_check_degraded(super, dev, failed);
-               map = get_imsm_map(dev, dev->vol.migr_state);
+               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) &&
@@ -2783,14 +2866,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);
@@ -3031,7 +3116,7 @@ static void end_migration(struct imsm_dev *dev, __u8 map_state)
                        }
 
        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;
 }
@@ -4121,6 +4206,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);
@@ -5059,7 +5145,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;
@@ -5070,6 +5156,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");
@@ -5192,6 +5280,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",
@@ -5301,7 +5398,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
@@ -5310,12 +5411,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;
        }
@@ -5605,20 +5703,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
         */
@@ -5657,18 +5759,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",
@@ -5679,6 +5769,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;
@@ -5767,10 +5880,6 @@ 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;
 }
 
@@ -5848,6 +5957,7 @@ static int imsm_count_failed(struct intel_super *super, struct imsm_dev *dev)
        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_for_loop;
        __u32 ord;
        int idx;
 
@@ -5856,13 +5966,18 @@ static int imsm_count_failed(struct intel_super *super, struct imsm_dev *dev)
         * 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]);
+       map_for_loop = prev;
+       if (is_gen_migration(dev))
+               if (prev && (map->num_members > prev->num_members))
+                       map_for_loop = map;
+
+       for (i = 0; i < map_for_loop->num_members; i++) {
+               ord = 0;
+               if (i < prev->num_members)
+                       ord |= __le32_to_cpu(prev->disk_ord_tbl[i]);
+               if (i < map->num_members)
+                       ord |= __le32_to_cpu(map->disk_ord_tbl[i]);
                idx = ord_to_idx(ord);
 
                disk = get_imsm_disk(super, idx);
@@ -5934,7 +6049,9 @@ static int mark_failure(struct imsm_dev *dev, struct imsm_disk *disk, int idx)
        if (is_failed(disk) && (ord & IMSM_ORD_REBUILD))
                return 0;
 
-       sprintf(buf, "%s:0", disk->serial);
+       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);
@@ -6043,7 +6160,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);
@@ -6093,7 +6210,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++;
@@ -6153,7 +6270,8 @@ static int imsm_set_array_state(struct active_array *a, int consistent)
                        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)
@@ -6540,10 +6658,8 @@ 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 */
-               return NULL;
+       if (imsm_reshape_blocks_arrays_changes(super))
+                       return NULL;
 
        if (a->info.array.level == 4)
                /* No repair for takeovered array
@@ -6560,9 +6676,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) {
@@ -6592,9 +6708,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;
  
@@ -6631,8 +6747,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)
@@ -6902,6 +7016,120 @@ 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);
+               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,
@@ -6975,7 +7203,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,
@@ -7196,99 +7424,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: {
@@ -8547,6 +8684,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) &&
@@ -8564,23 +8703,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;
                }
@@ -8627,8 +8766,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))