]> git.ipfire.org Git - thirdparty/mdadm.git/blobdiff - super-intel.c
imsm: do not publish OROM/EFI unsupported arrays
[thirdparty/mdadm.git] / super-intel.c
index 7d6a3cda05d33cde90963cfdd2fb70ce3ede040e..7f620c0684698aca6cc4bc5c428b37f175a52ff6 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,9 +408,7 @@ 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)
+static int attach_hba_to_super(struct intel_super *super, struct sys_dev *device)
 {
        struct intel_hba *hba;
 
@@ -475,6 +473,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 +563,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 +659,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 +896,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);
@@ -1297,12 +1302,15 @@ static int ahci_enumerate_ports(const char *hba_path, int port_count, int host_b
 }
 
 
+
 static void print_found_intel_controllers(struct sys_dev *elem)
 {
        for (; elem; elem = elem->next) {
                fprintf(stderr, Name ": found Intel(R) ");
                if (elem->type == SYS_DEV_SATA)
                        fprintf(stderr, "SATA ");
+               else if (elem->type == SYS_DEV_SAS)
+                       fprintf(stderr, "SAS ");
                fprintf(stderr, "RAID controller");
                if (elem->pci_id)
                        fprintf(stderr, " at %s", elem->pci_id);
@@ -1311,7 +1319,6 @@ static void print_found_intel_controllers(struct sys_dev *elem)
        fflush(stderr);
 }
 
-
 static int ahci_get_port_count(const char *hba_path, int *port_count)
 {
        struct dirent *ent;
@@ -1339,52 +1346,8 @@ static int ahci_get_port_count(const char *hba_path, int *port_count)
        return host_base;
 }
 
-static int detail_platform_imsm(int verbose, int enumerate_only)
+static void print_imsm_capability(const struct imsm_orom *orom)
 {
-       /* There are two components to imsm platform support, the ahci SATA
-        * controller and the option-rom.  To find the SATA controller we
-        * simply look in /sys/bus/pci/drivers/ahci to see if an ahci
-        * controller with the Intel vendor id is present.  This approach
-        * allows mdadm to leverage the kernel's ahci detection logic, with the
-        * caveat that if ahci.ko is not loaded mdadm will not be able to
-        * detect platform raid capabilities.  The option-rom resides in a
-        * platform "Adapter ROM".  We scan for its signature to retrieve the
-        * platform capabilities.  If raid support is disabled in the BIOS the
-        * option-rom capability structure will not be available.
-        */
-       const struct imsm_orom *orom;
-       struct sys_dev *list, *hba;
-       int host_base = 0;
-       int port_count = 0;
-       int result=0;
-
-       if (enumerate_only) {
-               if (check_env("IMSM_NO_PLATFORM") || find_imsm_orom())
-                       return 0;
-               return 2;
-       }
-
-       list = find_driver_devices("pci", "ahci");
-       for (hba = list; hba; hba = hba->next)
-               if (devpath_to_vendor(hba->path) == 0x8086)
-                       break;
-
-       if (!hba) {
-               if (verbose)
-                       fprintf(stderr, Name ": unable to find active ahci controller\n");
-               free_sys_dev(&list);
-               return 2;
-       } else if (verbose)
-               fprintf(stderr, Name ": found Intel SATA AHCI Controller\n");
-
-       print_found_intel_controllers(list);
-       orom = find_imsm_orom();
-       if (!orom) {
-               if (verbose)
-                       fprintf(stderr, Name ": imsm option-rom not found\n");
-               return 2;
-       }
-
        printf("       Platform : Intel(R) Matrix Storage Manager\n");
        printf("        Version : %d.%d.%d.%d\n", orom->major_ver, orom->minor_ver,
               orom->hotfix_ver, orom->build);
@@ -1413,7 +1376,63 @@ static int detail_platform_imsm(int verbose, int enumerate_only)
               imsm_orom_has_chunk(orom, 1024*64) ? " 64M" : "");
        printf("      Max Disks : %d\n", orom->tds);
        printf("    Max Volumes : %d\n", orom->vpa);
+       return;
+}
+
+static int detail_platform_imsm(int verbose, int enumerate_only)
+{
+       /* There are two components to imsm platform support, the ahci SATA
+        * controller and the option-rom.  To find the SATA controller we
+        * simply look in /sys/bus/pci/drivers/ahci to see if an ahci
+        * controller with the Intel vendor id is present.  This approach
+        * allows mdadm to leverage the kernel's ahci detection logic, with the
+        * caveat that if ahci.ko is not loaded mdadm will not be able to
+        * detect platform raid capabilities.  The option-rom resides in a
+        * platform "Adapter ROM".  We scan for its signature to retrieve the
+        * platform capabilities.  If raid support is disabled in the BIOS the
+        * option-rom capability structure will not be available.
+        */
+       const struct imsm_orom *orom;
+       struct sys_dev *list, *hba;
+       int host_base = 0;
+       int port_count = 0;
+       int result=0;
+
+       if (enumerate_only) {
+               if (check_env("IMSM_NO_PLATFORM"))
+                       return 0;
+               list = find_intel_devices();
+               if (!list)
+                       return 2;
+               for (hba = list; hba; hba = hba->next) {
+                       orom = find_imsm_capability(hba->type);
+                       if (!orom) {
+                               result = 2;
+                               break;
+                       }
+               }
+               free_sys_dev(&list);
+               return result;
+       }
+
+       list = find_intel_devices();
+       if (!list) {
+               if (verbose)
+                       fprintf(stderr, Name ": no active Intel(R) RAID "
+                               "controller found.\n");
+               free_sys_dev(&list);
+               return 2;
+       } else if (verbose)
+               print_found_intel_controllers(list);
 
+       for (hba = list; hba; hba = hba->next) {
+               orom = find_imsm_capability(hba->type);
+               if (!orom)
+                       fprintf(stderr, Name ": imsm capabilities not found for controller: %s (type %s)\n",
+                               hba->path, get_sys_dev_type(hba->type));
+               else
+                       print_imsm_capability(orom);
+       }
 
        for (hba = list; hba; hba = hba->next) {
                printf(" I/O Controller : %s (%s)\n",
@@ -1429,6 +1448,7 @@ static int detail_platform_imsm(int verbose, int enumerate_only)
                        }
                }
        }
+
        free_sys_dev(&list);
        return result;
 }
@@ -1680,6 +1700,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;
@@ -1746,14 +1768,53 @@ 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;
+               if (info->delta_disks) {
+                       /* this needs to be applied to every array
+                        * in the container.
+                        */
+                       info->reshape_active = 2;
+               }
+               /* 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;
@@ -1767,16 +1828,14 @@ 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;
+       info->resync_start = MaxSector;
        if (map_to_analyse->map_state == IMSM_T_STATE_UNINITIALIZED ||
            dev->vol.dirty) {
                info->resync_start = 0;
-       } else if (dev->vol.migr_state) {
+       }
+       if (dev->vol.migr_state) {
                switch (migr_type(dev)) {
                case MIGR_REPAIR:
                case MIGR_INIT: {
@@ -1786,6 +1845,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
@@ -1793,15 +1874,13 @@ 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:
                        /* we are not dirty, so... */
                        info->resync_start = MaxSector;
                }
-       } else
-               info->resync_start = MaxSector;
+       }
 
        strncpy(info->name, (char *) dev->volume, MAX_RAID_SERIAL_LEN);
        info->name[MAX_RAID_SERIAL_LEN] = 0;
@@ -2467,6 +2546,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;
 
@@ -2482,7 +2562,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;
@@ -2683,6 +2767,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;
@@ -3388,12 +3477,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);
@@ -3406,8 +3496,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);
@@ -3617,7 +3706,7 @@ static int add_to_super_imsm(struct supertype *st, mdu_disk_info_t *dk,
                                devname ? : "disk");
                        return 1;
                }
-               rv = attach_hba_to_super(super, hba_name, devname);
+               rv = attach_hba_to_super(super, hba_name);
                switch (rv) {
                case 2:
                        fprintf(stderr, Name ": %s is attached to Intel(R) %s RAID "
@@ -4144,17 +4233,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)
@@ -4174,7 +4267,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)
@@ -4363,7 +4456,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) ||
@@ -4376,8 +4470,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;
                }
        }
 
@@ -4392,7 +4486,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)
 {
@@ -4406,7 +4500,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);
        }
@@ -4425,7 +4520,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;
        }
@@ -4632,7 +4728,6 @@ static int update_subarray_imsm(struct supertype *st, char *subarray,
 
        return 0;
 }
-#endif /* MDASSEMBLE */
 
 static int is_gen_migration(struct imsm_dev *dev)
 {
@@ -4644,6 +4739,7 @@ static int is_gen_migration(struct imsm_dev *dev)
 
        return 0;
 }
+#endif /* MDASSEMBLE */
 
 static int is_rebuilding(struct imsm_dev *dev)
 {
@@ -4710,17 +4806,25 @@ 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;
                struct imsm_map *map2;
                struct mdinfo *this;
-               int slot;
+               int slot, chunk;
                char *ep;
 
                if (subarray &&
@@ -4741,7 +4845,21 @@ static struct mdinfo *container_content_imsm(struct supertype *st, char *subarra
                                dev->volume);
                        continue;
                }
+               /* do not publish arrays that are not support by controller's
+                * OROM/EFI
+                */
 
+               chunk = __le16_to_cpu(map->blocks_per_strip) >> 1;
+               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;
+               }
                this = malloc(sizeof(*this));
                if (!this) {
                        fprintf(stderr, Name ": failed to allocate %zu bytes\n",
@@ -4763,7 +4881,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;
@@ -4831,6 +4949,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;
        }
 
@@ -4987,6 +5106,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);
@@ -4999,8 +5120,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;
@@ -5036,6 +5161,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)
@@ -5048,13 +5204,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;
@@ -5068,6 +5224,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;
@@ -5078,29 +5235,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++;
        }
 }
@@ -5130,18 +5268,7 @@ static int imsm_set_array_state(struct active_array *a, int consistent)
                 */
                if (a->curr_action == reshape) {
                        /* still reshaping, maybe update curr_migr_unit */
-                       long long blocks_per_unit = blocks_per_migr_unit(dev);
-                       long long unit = a->last_checkpoint;
-                       if (blocks_per_unit) {
-                               unit /= blocks_per_unit;
-                               if (unit >
-                                   __le32_to_cpu(dev->vol.curr_migr_unit)) {
-                                       dev->vol.curr_migr_unit =
-                                               __cpu_to_le32(unit);
-                                       super->updates_pending++;
-                               }
-                       }
-                       return 0;
+                       goto mark_checkpoint;
                } else {
                        if (a->last_checkpoint == 0 && a->prev_action == reshape) {
                                /* for some reason we aborted the reshape.
@@ -5157,25 +5284,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);
-                       }                               
+                       }
                }
        }
 
@@ -5210,6 +5345,7 @@ static int imsm_set_array_state(struct active_array *a, int consistent)
                super->updates_pending++;
        }
 
+mark_checkpoint:
        /* check if we can update curr_migr_unit from resync_start, recovery_start */
        blocks_per_unit = blocks_per_migr_unit(dev);
        if (blocks_per_unit) {
@@ -5240,15 +5376,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;
 }
 
@@ -5839,16 +5966,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) ||
@@ -5864,15 +5992,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;
@@ -5886,10 +6020,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;
@@ -5905,24 +6037,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;
@@ -6004,8 +6119,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;
@@ -6026,9 +6139,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 */
@@ -6039,9 +6151,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;
 }
@@ -6090,8 +6203,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;
        }
 
@@ -6612,40 +6727,38 @@ 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/";
 
 static const char *imsm_get_disk_controller_domain(const char *path)
 {
-       struct sys_dev *list, *hba = NULL;
        char disk_path[PATH_MAX];
-       int ahci = 0;
-       char *dpath = NULL;
-
-       list = find_driver_devices("pci", "ahci");
-       for (hba = list; hba; hba = hba->next)
-               if (devpath_to_vendor(hba->path) == 0x8086)
-                       break;
-
-       if (hba) {
-               struct stat st;
+       char *drv=NULL;
+       struct stat st;
 
-               strncpy(disk_path, disk_by_path, PATH_MAX - 1);
-               strncat(disk_path, path, PATH_MAX - strlen(disk_path) - 1);
-               if (stat(disk_path, &st) == 0) {
-                       dpath = devt_to_devpath(st.st_rdev);
-                       if (dpath)
-                               ahci = path_attached_to_hba(dpath, hba->path);
-               }
+       strncpy(disk_path, disk_by_path, PATH_MAX - 1);
+       strncat(disk_path, path, PATH_MAX - strlen(disk_path) - 1);
+       if (stat(disk_path, &st) == 0) {
+               struct sys_dev* hba;
+               char *path=NULL;
+
+               path = devt_to_devpath(st.st_rdev);
+               if (path == NULL)
+                       return "unknown";
+               hba = find_disk_attached_hba(-1, path);
+               if (hba && hba->type == SYS_DEV_SAS)
+                       drv = "isci";
+               else if (hba && hba->type == SYS_DEV_SATA)
+                       drv = "ahci";
+               else 
+                       drv = "unknown";
+               dprintf("path: %s hba: %s attached: %s\n",
+                       path, (hba) ? hba->path : "NULL", drv);
+               free(path);
+               if (hba)
+                       free_sys_dev(&hba);
        }
-       dprintf("path: %s(%s) hba: %s attached: %d\n",
-               path, dpath, (hba) ? hba->path : "NULL", ahci);
-       free_sys_dev(&list);
-       if (ahci)
-               return "ahci";
-       else
-               return NULL;
+       return drv;
 }
 
 static int imsm_find_array_minor_by_subdev(int subdev, int container, int *minor)
@@ -6697,7 +6810,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
                         */
@@ -6822,7 +6935,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;
        }
 
@@ -6896,6 +7010,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);
 
@@ -6905,7 +7020,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) {
@@ -6913,9 +7028,15 @@ enum imsm_reshape_type imsm_analyze_change(struct supertype *st,
                                check_devs = 1;
                        }
                        break;
+               case 1:
+                       if (geo->level == 0) {
+                               change = CH_TAKEOVER;
+                               check_devs = 1;
+                       }
+                       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) {
@@ -6936,7 +7057,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)) {
@@ -6960,15 +7081,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;
@@ -7027,14 +7149,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;
@@ -7043,6 +7166,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);
@@ -7077,8 +7202,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
@@ -7108,10 +7233,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:
@@ -7135,6 +7257,7 @@ static int imsm_manage_reshape(
                afd, sra, reshape, st, stripes,
                fds, offsets, dests, destfd, destoffsets);
 }
+#endif /* MDASSEMBLE */
 
 struct superswitch super_imsm = {
 #ifndef        MDASSEMBLE
@@ -7152,6 +7275,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,
@@ -7170,10 +7297,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",