]> git.ipfire.org Git - thirdparty/mdadm.git/blobdiff - super-intel.c
imsm: FIX: Variables declaration cleanup
[thirdparty/mdadm.git] / super-intel.c
index 8e999a5f6959fad172facf3dbc978f79236ddd4c..b55f6ec0dcfb52a03241cb1d027168bd5ef1f7de 100644 (file)
@@ -294,8 +294,7 @@ struct extent {
 /* definitions of reshape process types */
 enum imsm_reshape_type {
        CH_TAKEOVER,
-       CH_CHUNK_MIGR,
-       CH_LEVEL_MIGRATION
+       CH_MIGRATION,
 };
 
 /* definition of messages passed to imsm_process_update */
@@ -384,6 +383,7 @@ const char *get_sys_dev_type(enum sys_dev_type type)
        return _sys_dev_type[type];
 }
 
+#ifndef MDASSEMBLE
 static struct intel_hba * alloc_intel_hba(struct sys_dev *device)
 {
        struct intel_hba *result = malloc(sizeof(*result));
@@ -408,7 +408,6 @@ static struct intel_hba * find_intel_hba(struct intel_hba *hba, struct sys_dev *
 }
 
 
-
 static int attach_hba_to_super(struct intel_super *super, struct sys_dev *device,
                               const char *devname)
 {
@@ -475,6 +474,7 @@ static struct sys_dev* find_disk_attached_hba(int fd, const char *devname)
 
        return NULL;
 }
+#endif /* MDASSEMBLE */
 
 
 static struct supertype *match_metadata_desc_imsm(char *arg)
@@ -564,17 +564,24 @@ static size_t sizeof_imsm_map(struct imsm_map *map)
 
 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
+        *   -1   - we return the second map if it exists, else the first
+        */
        struct imsm_map *map = &dev->vol.map[0];
 
-       if (second_map && !dev->vol.migr_state)
+       if (second_map == 1 && !dev->vol.migr_state)
                return NULL;
-       else if (second_map) {
+       else if (second_map == 1 ||
+                (second_map < 0 && dev->vol.migr_state)) {
                void *ptr = map;
 
                return ptr + sizeof_imsm_map(map);
        } else
                return map;
-               
+
 }
 
 /* return the size of the device.
@@ -653,14 +660,7 @@ static __u32 get_imsm_ord_tbl_ent(struct imsm_dev *dev,
 {
        struct imsm_map *map;
 
-       if (second_map == -1) {
-               if (dev->vol.migr_state)
-                       map = get_imsm_map(dev, 1);
-               else
-                       map = get_imsm_map(dev, 0);
-       } else {
-               map = get_imsm_map(dev, second_map);
-       }
+       map = get_imsm_map(dev, second_map);
 
        /* top byte identifies disk under rebuild */
        return __le32_to_cpu(map->disk_ord_tbl[slot]);
@@ -897,6 +897,12 @@ static void print_imsm_dev(struct imsm_dev *dev, char *uuid, int disk_idx)
                printf("]");
        }
        printf("\n");
+       printf("    Failed disk : ");
+       if (map->failed_disk_num == 0xff)
+               printf("none");
+       else
+               printf("%i", map->failed_disk_num);
+       printf("\n");
        slot = get_imsm_disk_slot(map, disk_idx);
        if (slot >= 0) {
                ord = get_imsm_ord_tbl_ent(dev, slot, -1);
@@ -1684,6 +1690,8 @@ static __u64 blocks_per_migr_unit(struct imsm_dev *dev)
                migr_chunk = migr_strip_blocks_resync(dev);
                disks = imsm_num_data_members(dev, 0);
                blocks_per_unit = stripes_per_unit * migr_chunk * disks;
+               if (migr_type(dev) == MIGR_GEN_MIGR)
+                       return blocks_per_unit;
                stripe = __le32_to_cpu(map->blocks_per_strip) * disks;
                segment = blocks_per_unit / stripe;
                block_rel = blocks_per_unit - segment * stripe;
@@ -1750,14 +1758,47 @@ 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) {
+       if (prev_map && map->map_state == prev_map->map_state) {
+               info->reshape_active = 1;
                info->new_level = get_imsm_raid_level(map);
                info->new_layout = imsm_level_to_layout(info->new_level);
                info->new_chunk = __le16_to_cpu(map->blocks_per_strip) << 9;
+               info->delta_disks = map->num_members - prev_map->num_members;
+               /* We shape information that we give to md might have to be
+                * modify to cope with md's requirement for reshaping arrays.
+                * For example, when reshaping a RAID0, md requires it to be
+                * presented as a degraded RAID4.
+                * Also if a RAID0 is migrating to a RAID5 we need to specify
+                * the array as already being RAID5, but the 'before' layout
+                * is a RAID4-like layout.
+                */
+               switch (info->array.level) {
+               case 0:
+                       switch(info->new_level) {
+                       case 0:
+                               /* conversion is happening as RAID4 */
+                               info->array.level = 4;
+                               info->array.raid_disks += 1;
+                               break;
+                       case 5:
+                               /* conversion is happening as RAID5 */
+                               info->array.level = 5;
+                               info->array.layout = ALGORITHM_PARITY_N;
+                               info->array.raid_disks += 1;
+                               info->delta_disks -= 1;
+                               break;
+                       default:
+                               /* FIXME error message */
+                               info->array.level = UnSet;
+                               break;
+                       }
+                       break;
+               }
        } else {
                info->new_level = UnSet;
                info->new_layout = UnSet;
                info->new_chunk = info->array.chunk_size;
+               info->delta_disks = 0;
        }
        info->disk.major = 0;
        info->disk.minor = 0;
@@ -1771,12 +1812,8 @@ static void getinfo_super_imsm_volume(struct supertype *st, struct mdinfo *info,
                __le32_to_cpu(map_to_analyse->blocks_per_member);
        memset(info->uuid, 0, sizeof(info->uuid));
        info->recovery_start = MaxSector;
-       info->reshape_active = (prev_map != NULL);
-       if (info->reshape_active)
-               info->delta_disks = map->num_members - prev_map->num_members;
-       else
-               info->delta_disks = 0;
 
+       info->reshape_progress = 0;
        if (map_to_analyse->map_state == IMSM_T_STATE_UNINITIALIZED ||
            dev->vol.dirty) {
                info->resync_start = 0;
@@ -1790,6 +1827,28 @@ static void getinfo_super_imsm_volume(struct supertype *st, struct mdinfo *info,
                        info->resync_start = blocks_per_unit * units;
                        break;
                }
+               case MIGR_GEN_MIGR: {
+                       __u64 blocks_per_unit = blocks_per_migr_unit(dev);
+                       __u64 units = __le32_to_cpu(dev->vol.curr_migr_unit);
+                       unsigned long long array_blocks;
+                       int used_disks;
+
+                       info->reshape_progress = blocks_per_unit * units;
+                       dprintf("IMSM: General Migration checkpoint : %llu "
+                              "(%llu) -> read reshape progress : %llu\n",
+                               units, blocks_per_unit, info->reshape_progress);
+
+                       used_disks = imsm_num_data_members(dev, 1);
+                       if (used_disks > 0) {
+                               array_blocks = map->blocks_per_member *
+                                       used_disks;
+                               /* round array size down to closest MB
+                                */
+                               info->custom_array_size = (array_blocks
+                                               >> SECT_PER_MB_SHIFT)
+                                               << SECT_PER_MB_SHIFT;
+                       }
+               }
                case MIGR_VERIFY:
                        /* we could emulate the checkpointing of
                         * 'sync_action=check' migrations, but for now
@@ -1797,7 +1856,6 @@ static void getinfo_super_imsm_volume(struct supertype *st, struct mdinfo *info,
                         */
                case MIGR_REBUILD:
                        /* this is handled by container_content_imsm() */
-               case MIGR_GEN_MIGR:
                case MIGR_STATE_CHANGE:
                        /* FIXME handle other migrations */
                default:
@@ -2471,6 +2529,7 @@ static int parse_raid_devices(struct intel_super *super)
        int i;
        struct imsm_dev *dev_new;
        size_t len, len_migr;
+       size_t max_len = 0;
        size_t space_needed = 0;
        struct imsm_super *mpb = super->anchor;
 
@@ -2486,7 +2545,11 @@ static int parse_raid_devices(struct intel_super *super)
                dv = malloc(sizeof(*dv));
                if (!dv)
                        return 1;
-               dev_new = malloc(len_migr);
+               if (max_len < len_migr)
+                       max_len = len_migr;
+               if (max_len > len_migr)
+                       space_needed += max_len - len_migr;
+               dev_new = malloc(max_len);
                if (!dev_new) {
                        free(dv);
                        return 1;
@@ -2687,6 +2750,11 @@ static void free_imsm_disks(struct intel_super *super)
                super->disks = d->next;
                __free_imsm_disk(d);
        }
+       while (super->disk_mgmt_list) {
+               d = super->disk_mgmt_list;
+               super->disk_mgmt_list = d->next;
+               __free_imsm_disk(d);
+       }
        while (super->missing) {
                d = super->missing;
                super->missing = d->next;
@@ -3392,12 +3460,13 @@ static int init_super_imsm_volume(struct supertype *st, mdu_array_info_t *info,
                fprintf(stderr, Name ": failed to allocate device list entry\n");
                return 0;
        }
-       dev = malloc(sizeof(*dev) + sizeof(__u32) * (info->raid_disks - 1));
+       dev = calloc(1, sizeof(*dev) + sizeof(__u32) * (info->raid_disks - 1));
        if (!dev) {
                free(dv);
                fprintf(stderr, Name": could not allocate raid device\n");
                return 0;
        }
+
        strncpy((char *) dev->volume, name, MAX_RAID_SERIAL_LEN);
        if (info->level == 1)
                array_blocks = info_to_blocks_per_member(info);
@@ -3410,8 +3479,7 @@ static int init_super_imsm_volume(struct supertype *st, mdu_array_info_t *info,
 
        dev->size_low = __cpu_to_le32((__u32) array_blocks);
        dev->size_high = __cpu_to_le32((__u32) (array_blocks >> 32));
-       dev->status = __cpu_to_le32(0);
-       dev->reserved_blocks = __cpu_to_le32(0);
+       dev->status = (DEV_READ_COALESCING | DEV_WRITE_COALESCING);
        vol = &dev->vol;
        vol->migr_state = 0;
        set_migr_type(dev, MIGR_INIT);
@@ -4148,17 +4216,21 @@ static int is_raid_level_supported(const struct imsm_orom *orom, int level, int
 #define pr_vrb(fmt, arg...) (void) (verbose && fprintf(stderr, Name fmt, ##arg))
 static int
 validate_geometry_imsm_orom(struct intel_super *super, int level, int layout,
-                           int raiddisks, int chunk, int verbose)
+                           int raiddisks, int *chunk, int verbose)
 {
        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 &&
-           !imsm_orom_has_chunk(super->orom, chunk)) {
-               pr_vrb(": platform does not support a chunk size of: %d\n", chunk);
-               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 (layout != imsm_level_to_layout(level)) {
                if (level == 5)
@@ -4178,7 +4250,7 @@ validate_geometry_imsm_orom(struct intel_super *super, int level, int layout,
  * FIX ME add ahci details
  */
 static int validate_geometry_imsm_volume(struct supertype *st, int level,
-                                        int layout, int raiddisks, int chunk,
+                                        int layout, int raiddisks, int *chunk,
                                         unsigned long long size, char *dev,
                                         unsigned long long *freesize,
                                         int verbose)
@@ -4367,7 +4439,8 @@ static int reserve_space(struct supertype *st, int raiddisks,
        maxsize = merge_extents(super, extent_cnt);
        minsize = size;
        if (size == 0)
-               minsize = chunk;
+               /* chunk is in K */
+               minsize = chunk * 2;
 
        if (cnt < raiddisks ||
            (super->orom && used && used != raiddisks) ||
@@ -4380,8 +4453,8 @@ static int reserve_space(struct supertype *st, int raiddisks,
        if (size == 0) {
                size = maxsize;
                if (chunk) {
-                       size /= chunk;
-                       size *= chunk;
+                       size /= 2 * chunk;
+                       size *= 2 * chunk;
                }
        }
 
@@ -4396,7 +4469,7 @@ static int reserve_space(struct supertype *st, int raiddisks,
 }
 
 static int validate_geometry_imsm(struct supertype *st, int level, int layout,
-                                 int raiddisks, int chunk, unsigned long long size,
+                                 int raiddisks, int *chunk, unsigned long long size,
                                  char *dev, unsigned long long *freesize,
                                  int verbose)
 {
@@ -4410,7 +4483,8 @@ static int validate_geometry_imsm(struct supertype *st, int level, int layout,
        if (level == LEVEL_CONTAINER) {
                /* Must be a fresh device to add to a container */
                return validate_geometry_imsm_container(st, level, layout,
-                                                       raiddisks, chunk, size,
+                                                       raiddisks,
+                                                       chunk?*chunk:0, size,
                                                        dev, freesize,
                                                        verbose);
        }
@@ -4429,7 +4503,8 @@ static int validate_geometry_imsm(struct supertype *st, int level, int layout,
                                                         raiddisks, chunk,
                                                         verbose))
                                return 0;
-                       return reserve_space(st, raiddisks, size, chunk, freesize);
+                       return reserve_space(st, raiddisks, size,
+                                            chunk?*chunk:0, freesize);
                }
                return 1;
        }
@@ -4636,7 +4711,6 @@ static int update_subarray_imsm(struct supertype *st, char *subarray,
 
        return 0;
 }
-#endif /* MDASSEMBLE */
 
 static int is_gen_migration(struct imsm_dev *dev)
 {
@@ -4648,6 +4722,7 @@ static int is_gen_migration(struct imsm_dev *dev)
 
        return 0;
 }
+#endif /* MDASSEMBLE */
 
 static int is_rebuilding(struct imsm_dev *dev)
 {
@@ -4714,11 +4789,19 @@ static struct mdinfo *container_content_imsm(struct supertype *st, char *subarra
        struct mdinfo *rest = NULL;
        unsigned int i;
        int bbm_errors = 0;
+       struct dl *d;
+       int spare_disks = 0;
 
        /* check for bad blocks */
        if (imsm_bbm_log_size(super->anchor))
                bbm_errors = 1;
 
+       /* count spare devices, not used in maps
+        */
+       for (d = super->disks; d; d = d->next)
+               if (d->index == -1)
+                       spare_disks++;
+
        for (i = 0; i < mpb->num_raid_devs; i++) {
                struct imsm_dev *dev;
                struct imsm_map *map;
@@ -4767,7 +4850,7 @@ static struct mdinfo *container_content_imsm(struct supertype *st, char *subarra
 
                        skip = 0;
                        idx = get_imsm_disk_idx(dev, slot, 0);
-                       ord = get_imsm_ord_tbl_ent(dev, slot, 0);
+                       ord = get_imsm_ord_tbl_ent(dev, slot, -1);
                        for (d = super->disks; d ; d = d->next)
                                if (d->index == idx)
                                        break;
@@ -4835,6 +4918,7 @@ static struct mdinfo *container_content_imsm(struct supertype *st, char *subarra
                }
                /* now that the disk list is up-to-date fixup recovery_start */
                update_recovery_start(dev, this);
+               this->array.spare_disks += spare_disks;
                rest = this;
        }
 
@@ -4991,6 +5075,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);
@@ -5003,8 +5089,12 @@ 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);
+       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;
-       disk->status &= ~CONFIGURED_DISK;
        set_imsm_ord_tbl_ent(map, slot, idx | IMSM_ORD_REBUILD);
        if (map->failed_disk_num == 0xff)
                map->failed_disk_num = slot;
@@ -5040,6 +5130,37 @@ static void handle_missing(struct intel_super *super, struct imsm_dev *dev)
        super->updates_pending++;
 }
 
+static unsigned long long imsm_set_array_size(struct imsm_dev *dev)
+{
+       int used_disks = imsm_num_data_members(dev, 0);
+       unsigned long long array_blocks;
+       struct imsm_map *map;
+
+       if (used_disks == 0) {
+               /* when problems occures
+                * return current array_blocks value
+                */
+               array_blocks = __le32_to_cpu(dev->size_high);
+               array_blocks = array_blocks << 32;
+               array_blocks += __le32_to_cpu(dev->size_low);
+
+               return array_blocks;
+       }
+
+       /* set array size in metadata
+        */
+       map = get_imsm_map(dev, 0);
+       array_blocks = map->blocks_per_member * used_disks;
+
+       /* round array size down to closest MB
+        */
+       array_blocks = (array_blocks >> SECT_PER_MB_SHIFT) << SECT_PER_MB_SHIFT;
+       dev->size_low = __cpu_to_le32((__u32)array_blocks);
+       dev->size_high = __cpu_to_le32((__u32)(array_blocks >> 32));
+
+       return array_blocks;
+}
+
 static void imsm_set_disk(struct active_array *a, int n, int state);
 
 static void imsm_progress_container_reshape(struct intel_super *super)
@@ -5052,13 +5173,13 @@ static void imsm_progress_container_reshape(struct intel_super *super)
        struct imsm_super *mpb = super->anchor;
        int prev_disks = -1;
        int i;
+       int copy_map_size;
 
        for (i = 0; i < mpb->num_raid_devs; i++) {
                struct imsm_dev *dev = get_imsm_dev(super, i);
                struct imsm_map *map = get_imsm_map(dev, 0);
                struct imsm_map *map2;
                int prev_num_members;
-               int used_disks;
 
                if (dev->vol.migr_state)
                        return;
@@ -5072,6 +5193,7 @@ static void imsm_progress_container_reshape(struct intel_super *super)
                 * i.e it needs a migr_state
                 */
 
+               copy_map_size = sizeof_imsm_map(map);
                prev_num_members = map->num_members;
                map->num_members = prev_disks;
                dev->vol.migr_state = 1;
@@ -5082,29 +5204,10 @@ static void imsm_progress_container_reshape(struct intel_super *super)
                        set_imsm_ord_tbl_ent(map, i, i);
                map2 = get_imsm_map(dev, 1);
                /* Copy the current map */
-               memcpy(map2, map, sizeof_imsm_map(map));
+               memcpy(map2, map, copy_map_size);
                map2->num_members = prev_num_members;
 
-               /* calculate new size
-                */
-               used_disks = imsm_num_data_members(dev, 0);
-               if (used_disks) {
-                       unsigned long long array_blocks;
-
-                       array_blocks =
-                               map->blocks_per_member
-                               * used_disks;
-                       /* round array size down to closest MB
-                        */
-                       array_blocks = (array_blocks
-                                       >> SECT_PER_MB_SHIFT)
-                               << SECT_PER_MB_SHIFT;
-                       dev->size_low =
-                               __cpu_to_le32((__u32)array_blocks);
-                       dev->size_high =
-                               __cpu_to_le32(
-                                       (__u32)(array_blocks >> 32));
-               }
+               imsm_set_array_size(dev);
                super->updates_pending++;
        }
 }
@@ -5161,25 +5264,33 @@ static int imsm_set_array_state(struct active_array *a, int consistent)
                        if (a->last_checkpoint >= a->info.component_size) {
                                unsigned long long array_blocks;
                                int used_disks;
-                               /* it seems the reshape is all done */
-                               dev->vol.migr_state = 0;
-                               dev->vol.migr_type = 0;
-                               dev->vol.curr_migr_unit = 0;
+                               struct mdinfo *mdi;
+
+                               used_disks = imsm_num_data_members(dev, 0);
+                               if (used_disks > 0) {
+                                       array_blocks =
+                                               map->blocks_per_member *
+                                               used_disks;
+                                       /* round array size down to closest MB
+                                        */
+                                       array_blocks = (array_blocks
+                                                       >> SECT_PER_MB_SHIFT)
+                                               << SECT_PER_MB_SHIFT;
+                                       a->info.custom_array_size = array_blocks;
+                                       /* encourage manager to update array
+                                        * size
+                                        */
+
+                                       a->check_reshape = 1;
+}
+                               /* finalize online capacity expansion/reshape */
+                               for (mdi = a->info.devs; mdi; mdi = mdi->next)
+                                       imsm_set_disk(a,
+                                                     mdi->disk.raid_disk,
+                                                     mdi->curr_state);
 
-                               used_disks = imsm_num_data_members(dev, -1);
-                               array_blocks = map->blocks_per_member * used_disks;
-                               /* round array size down to closest MB */
-                               array_blocks = (array_blocks >> SECT_PER_MB_SHIFT)
-                                       << SECT_PER_MB_SHIFT;
-                               dev->size_low = __cpu_to_le32((__u32) array_blocks);
-                               dev->size_high = __cpu_to_le32((__u32) (array_blocks >> 32));
-                               a->info.custom_array_size = array_blocks;
-                               a->check_reshape = 1; /* encourage manager to update
-                                                      * array size
-                                                      */
-                               super->updates_pending++;
                                imsm_progress_container_reshape(super);
-                       }                               
+                       }
                }
        }
 
@@ -5244,15 +5355,6 @@ static int imsm_set_array_state(struct active_array *a, int consistent)
                super->updates_pending++;
        }
 
-       /* finalize online capacity expansion/reshape */
-       if ((a->curr_action != reshape) &&
-           (a->prev_action == reshape)) {
-               struct mdinfo *mdi;
-
-               for (mdi = a->info.devs; mdi; mdi = mdi->next)
-                       imsm_set_disk(a, mdi->disk.raid_disk, mdi->curr_state);
-       }
-
        return consistent;
 }
 
@@ -5843,16 +5945,17 @@ static int apply_reshape_container_disks_update(struct imsm_update_reshape *u,
        int devices_to_reshape = 1;
        struct imsm_super *mpb = super->anchor;
        int ret_val = 0;
+       unsigned int dev_id;
 
-       dprintf("imsm: imsm_process_update() for update_reshape\n");
+       dprintf("imsm: apply_reshape_container_disks_update()\n");
 
        /* enable spares to use in array */
        for (i = 0; i < delta_disks; i++) {
                new_disk = get_disk_super(super,
                                          major(u->new_disks[i]),
                                          minor(u->new_disks[i]));
-               dprintf("imsm: imsm_process_update(): new disk "
-                       "for reshape is: %i:%i (%p, index = %i)\n",
+               dprintf("imsm: new disk for reshape is: %i:%i "
+                       "(%p, index = %i)\n",
                        major(u->new_disks[i]), minor(u->new_disks[i]),
                        new_disk, new_disk->index);
                if ((new_disk == NULL) ||
@@ -5868,15 +5971,21 @@ static int apply_reshape_container_disks_update(struct imsm_update_reshape *u,
                new_disk->disk.status &= ~SPARE_DISK;
        }
 
-       dprintf("imsm: process_update(): update_reshape: volume set"
-               " mpb->num_raid_devs = %i\n", mpb->num_raid_devs);
+       dprintf("imsm: volume set mpb->num_raid_devs = %i\n",
+               mpb->num_raid_devs);
        /* manage changes in volume
         */
-       for (id = super->devlist ; id; id = id->next) {
+       for (dev_id = 0; dev_id < mpb->num_raid_devs; dev_id++) {
                void **sp = *space_list;
                struct imsm_dev *newdev;
                struct imsm_map *newmap, *oldmap;
 
+               for (id = super->devlist ; id; id = id->next) {
+                       if (id->index == dev_id)
+                               break;
+               }
+               if (id == NULL)
+                       break;
                if (!sp)
                        continue;
                *space_list = *sp;
@@ -5890,10 +5999,8 @@ static int apply_reshape_container_disks_update(struct imsm_update_reshape *u,
                /* update one device only
                 */
                if (devices_to_reshape) {
-                       int used_disks;
-
-                       dprintf("process_update(): modifying "
-                               "subdev: %i\n", id->index);
+                       dprintf("imsm: modifying subdev: %i\n",
+                               id->index);
                        devices_to_reshape--;
                        newdev->vol.migr_state = 1;
                        newdev->vol.curr_migr_unit = 0;
@@ -5909,24 +6016,7 @@ static int apply_reshape_container_disks_update(struct imsm_update_reshape *u,
                        newmap = get_imsm_map(newdev, 1);
                        memcpy(newmap, oldmap, sizeof_imsm_map(oldmap));
 
-                       /* calculate new size
-                        */
-                       used_disks = imsm_num_data_members(newdev, 0);
-                       if (used_disks) {
-                               unsigned long long array_blocks;
-
-                               array_blocks =
-                                       newmap->blocks_per_member * used_disks;
-                               /* round array size down to closest MB
-                                */
-                               array_blocks = (array_blocks
-                                               >> SECT_PER_MB_SHIFT)
-                                       << SECT_PER_MB_SHIFT;
-                               newdev->size_low =
-                                       __cpu_to_le32((__u32)array_blocks);
-                               newdev->size_high =
-                                       __cpu_to_le32((__u32)(array_blocks >> 32));
-                       }
+                       imsm_set_array_size(newdev);
                }
 
                sp = (void **)id->dev;
@@ -6008,8 +6098,6 @@ static int apply_takeover_update(struct imsm_update_takeover *u,
                        *space_list = *space;
                        du = (void *)space;
                        memcpy(du, super->disks, sizeof(*du));
-                       du->disk.status = FAILED_DISK;
-                       du->disk.scsi_id = 0;
                        du->fd = -1;
                        du->minor = 0;
                        du->major = 0;
@@ -6030,9 +6118,8 @@ static int apply_takeover_update(struct imsm_update_takeover *u,
                memcpy(dev_new, dev, sizeof(*dev));
                /* update new map */
                map = get_imsm_map(dev_new, 0);
-               map->failed_disk_num = map->num_members;
                map->num_members = map->num_members * 2;
-               map->map_state = IMSM_T_STATE_NORMAL;
+               map->map_state = IMSM_T_STATE_DEGRADED;
                map->num_domains = 2;
                map->raid_level = 1;
                /* replace dev<->dev_new */
@@ -6043,9 +6130,10 @@ static int apply_takeover_update(struct imsm_update_takeover *u,
                if (du->index >= 0)
                        set_imsm_ord_tbl_ent(map, du->index, du->index);
        for (du = super->missing; du; du = du->next)
-               if (du->index >= 0)
-                       set_imsm_ord_tbl_ent(map, du->index,
-                                               du->index | IMSM_ORD_REBUILD);
+               if (du->index >= 0) {
+                       set_imsm_ord_tbl_ent(map, du->index, du->index);
+                       mark_missing(dev_new, &du->disk, du->index);
+               }
 
        return 1;
 }
@@ -6094,8 +6182,10 @@ static void imsm_process_update(struct supertype *st,
        switch (type) {
        case update_takeover: {
                struct imsm_update_takeover *u = (void *)update->buf;
-               if (apply_takeover_update(u, super, &update->space_list))
+               if (apply_takeover_update(u, super, &update->space_list)) {
+                       imsm_update_version_info(super);
                        super->updates_pending++;
+               }
                break;
        }
 
@@ -6616,7 +6706,6 @@ static void imsm_delete(struct intel_super *super, struct dl **dlp, unsigned ind
                __free_imsm_disk(dl);
        }
 }
-#endif /* MDASSEMBLE */
 
 static char disk_by_path[] = "/dev/disk/by-path/";
 
@@ -6700,7 +6789,7 @@ static int imsm_reshape_is_allowed_on_container(struct supertype *st,
                dprintf("imsm: checking device_num: %i\n",
                        member->container_member);
 
-               if (geo->raid_disks < member->array.raid_disks) {
+               if (geo->raid_disks <= member->array.raid_disks) {
                        /* we work on container for Online Capacity Expansion
                         * only so raid_disks has to grow
                         */
@@ -6825,7 +6914,8 @@ static int imsm_create_metadata_update_for_reshape(
 
        if (spares == NULL
            || delta_disks > spares->array.spare_disks) {
-               dprintf("imsm: ERROR: Cannot get spare devices.\n");
+               fprintf(stderr, Name ": imsm: ERROR: Cannot get spare devices "
+                       "for %s.\n", geo->dev_name);
                goto abort;
        }
 
@@ -6899,6 +6989,7 @@ enum imsm_reshape_type imsm_analyze_change(struct supertype *st,
        struct mdinfo info;
        int change = -1;
        int check_devs = 0;
+       int chunk;
 
        getinfo_super_imsm_volume(st, &info, NULL);
 
@@ -6908,7 +6999,7 @@ enum imsm_reshape_type imsm_analyze_change(struct supertype *st,
                switch (info.array.level) {
                case 0:
                        if (geo->level == 5) {
-                               change = CH_LEVEL_MIGRATION;
+                               change = CH_MIGRATION;
                                check_devs = 1;
                        }
                        if (geo->level == 10) {
@@ -6923,8 +7014,8 @@ enum imsm_reshape_type imsm_analyze_change(struct supertype *st,
                        }
                        break;
                case 5:
-                       if (geo->level != 0)
-                               change = CH_LEVEL_MIGRATION;
+                       if (geo->level == 0)
+                               change = CH_MIGRATION;
                        break;
                case 10:
                        if (geo->level == 0) {
@@ -6945,7 +7036,7 @@ enum imsm_reshape_type imsm_analyze_change(struct supertype *st,
 
        if ((geo->layout != info.array.layout)
            && ((geo->layout != UnSet) && (geo->layout != -1))) {
-               change = CH_LEVEL_MIGRATION;
+               change = CH_MIGRATION;
                if ((info.array.layout == 0)
                    && (info.array.level == 5)
                    && (geo->layout == 5)) {
@@ -6969,15 +7060,16 @@ enum imsm_reshape_type imsm_analyze_change(struct supertype *st,
 
        if ((geo->chunksize > 0) && (geo->chunksize != UnSet)
            && (geo->chunksize != info.array.chunk_size))
-               change = CH_CHUNK_MIGR;
+               change = CH_MIGRATION;
        else
                geo->chunksize = info.array.chunk_size;
 
+       chunk = geo->chunksize / 1024;
        if (!validate_geometry_imsm(st,
                                    geo->level,
                                    geo->layout,
                                    geo->raid_disks,
-                                   (geo->chunksize / 1024),
+                                   &chunk,
                                    geo->size,
                                    0, 0, 1))
                change = -1;
@@ -7036,14 +7128,15 @@ int imsm_takeover(struct supertype *st, struct geo_params *geo)
 
 static int imsm_reshape_super(struct supertype *st, long long size, int level,
                              int layout, int chunksize, int raid_disks,
-                             char *backup, char *dev, int verbose)
+                             int delta_disks, char *backup, char *dev,
+                             int verbose)
 {
        int ret_val = 1;
        struct geo_params geo;
 
        dprintf("imsm: reshape_super called.\n");
 
-       memset(&geo, sizeof(struct geo_params), 0);
+       memset(&geo, 0, sizeof(struct geo_params));
 
        geo.dev_name = dev;
        geo.dev_id = st->devnum;
@@ -7052,6 +7145,8 @@ static int imsm_reshape_super(struct supertype *st, long long size, int level,
        geo.layout = layout;
        geo.chunksize = chunksize;
        geo.raid_disks = raid_disks;
+       if (delta_disks != UnSet)
+               geo.raid_disks += delta_disks;
 
        dprintf("\tfor level      : %i\n", geo.level);
        dprintf("\tfor raid_disks : %i\n", geo.raid_disks);
@@ -7086,8 +7181,8 @@ static int imsm_reshape_super(struct supertype *st, long long size, int level,
                                free(u);
 
                } else {
-                       fprintf(stderr, Name "imsm: Operation is not allowed "
-                               "on this container\n");
+                       fprintf(stderr, Name ": (imsm) Operation "
+                               "is not allowed on this container\n");
                }
        } else {
                /* On volume level we support following operations
@@ -7117,10 +7212,7 @@ static int imsm_reshape_super(struct supertype *st, long long size, int level,
                case CH_TAKEOVER:
                        ret_val = imsm_takeover(st, &geo);
                        break;
-               case CH_CHUNK_MIGR:
-                       ret_val = 0;
-                       break;
-               case CH_LEVEL_MIGRATION:
+               case CH_MIGRATION:
                        ret_val = 0;
                        break;
                default:
@@ -7144,6 +7236,7 @@ static int imsm_manage_reshape(
                afd, sra, reshape, st, stripes,
                fds, offsets, dests, destfd, destoffsets);
 }
+#endif /* MDASSEMBLE */
 
 struct superswitch super_imsm = {
 #ifndef        MDASSEMBLE
@@ -7161,6 +7254,10 @@ struct superswitch super_imsm = {
        .kill_subarray = kill_subarray_imsm,
        .update_subarray = update_subarray_imsm,
        .load_container = load_container_imsm,
+       .default_geometry = default_geometry_imsm,
+       .get_disk_controller_domain = imsm_get_disk_controller_domain,
+       .reshape_super  = imsm_reshape_super,
+       .manage_reshape = imsm_manage_reshape,
 #endif
        .match_home     = match_home_imsm,
        .uuid_from_super= uuid_from_super_imsm,
@@ -7179,10 +7276,6 @@ struct superswitch super_imsm = {
        .free_super     = free_super_imsm,
        .match_metadata_desc = match_metadata_desc_imsm,
        .container_content = container_content_imsm,
-       .default_geometry = default_geometry_imsm,
-       .get_disk_controller_domain = imsm_get_disk_controller_domain,
-       .reshape_super  = imsm_reshape_super,
-       .manage_reshape = imsm_manage_reshape,
 
        .external       = 1,
        .name = "imsm",