]> git.ipfire.org Git - thirdparty/mdadm.git/blobdiff - super-intel.c
super-intel: enable loading metadata from non-IMSM compliant disks
[thirdparty/mdadm.git] / super-intel.c
index e8d2c6ba5538d7b42c64085476efd5e008a55644..2b41e086bf19d73c4d240160ff106cb3ce47b501 100644 (file)
@@ -408,8 +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;
 
@@ -477,6 +476,9 @@ static struct sys_dev* find_disk_attached_hba(int fd, const char *devname)
 #endif /* MDASSEMBLE */
 
 
+static int find_intel_hba_capability(int fd, struct intel_super *super,
+                                    char *devname);
+
 static struct supertype *match_metadata_desc_imsm(char *arg)
 {
        struct supertype *st;
@@ -1347,6 +1349,39 @@ static int ahci_get_port_count(const char *hba_path, int *port_count)
        return host_base;
 }
 
+static void print_imsm_capability(const struct imsm_orom *orom)
+{
+       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);
+       printf("    RAID Levels :%s%s%s%s%s\n",
+              imsm_orom_has_raid0(orom) ? " raid0" : "",
+              imsm_orom_has_raid1(orom) ? " raid1" : "",
+              imsm_orom_has_raid1e(orom) ? " raid1e" : "",
+              imsm_orom_has_raid10(orom) ? " raid10" : "",
+              imsm_orom_has_raid5(orom) ? " raid5" : "");
+       printf("    Chunk Sizes :%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n",
+              imsm_orom_has_chunk(orom, 2) ? " 2k" : "",
+              imsm_orom_has_chunk(orom, 4) ? " 4k" : "",
+              imsm_orom_has_chunk(orom, 8) ? " 8k" : "",
+              imsm_orom_has_chunk(orom, 16) ? " 16k" : "",
+              imsm_orom_has_chunk(orom, 32) ? " 32k" : "",
+              imsm_orom_has_chunk(orom, 64) ? " 64k" : "",
+              imsm_orom_has_chunk(orom, 128) ? " 128k" : "",
+              imsm_orom_has_chunk(orom, 256) ? " 256k" : "",
+              imsm_orom_has_chunk(orom, 512) ? " 512k" : "",
+              imsm_orom_has_chunk(orom, 1024*1) ? " 1M" : "",
+              imsm_orom_has_chunk(orom, 1024*2) ? " 2M" : "",
+              imsm_orom_has_chunk(orom, 1024*4) ? " 4M" : "",
+              imsm_orom_has_chunk(orom, 1024*8) ? " 8M" : "",
+              imsm_orom_has_chunk(orom, 1024*16) ? " 16M" : "",
+              imsm_orom_has_chunk(orom, 1024*32) ? " 32M" : "",
+              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
@@ -1367,9 +1402,20 @@ static int detail_platform_imsm(int verbose, int enumerate_only)
        int result=0;
 
        if (enumerate_only) {
-               if (check_env("IMSM_NO_PLATFORM") || find_imsm_orom())
+               if (check_env("IMSM_NO_PLATFORM"))
                        return 0;
-               return 2;
+               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();
@@ -1382,43 +1428,15 @@ static int detail_platform_imsm(int verbose, int enumerate_only)
        } else if (verbose)
                print_found_intel_controllers(list);
 
-       orom = find_imsm_orom();
-       if (!orom) {
-               free_sys_dev(&list);
-               if (verbose)
-                       fprintf(stderr, Name ": imsm option-rom not found\n");
-               return 2;
+       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);
        }
 
-       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);
-       printf("    RAID Levels :%s%s%s%s%s\n",
-              imsm_orom_has_raid0(orom) ? " raid0" : "",
-              imsm_orom_has_raid1(orom) ? " raid1" : "",
-              imsm_orom_has_raid1e(orom) ? " raid1e" : "",
-              imsm_orom_has_raid10(orom) ? " raid10" : "",
-              imsm_orom_has_raid5(orom) ? " raid5" : "");
-       printf("    Chunk Sizes :%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n",
-              imsm_orom_has_chunk(orom, 2) ? " 2k" : "",
-              imsm_orom_has_chunk(orom, 4) ? " 4k" : "",
-              imsm_orom_has_chunk(orom, 8) ? " 8k" : "",
-              imsm_orom_has_chunk(orom, 16) ? " 16k" : "",
-              imsm_orom_has_chunk(orom, 32) ? " 32k" : "",
-              imsm_orom_has_chunk(orom, 64) ? " 64k" : "",
-              imsm_orom_has_chunk(orom, 128) ? " 128k" : "",
-              imsm_orom_has_chunk(orom, 256) ? " 256k" : "",
-              imsm_orom_has_chunk(orom, 512) ? " 512k" : "",
-              imsm_orom_has_chunk(orom, 1024*1) ? " 1M" : "",
-              imsm_orom_has_chunk(orom, 1024*2) ? " 2M" : "",
-              imsm_orom_has_chunk(orom, 1024*4) ? " 4M" : "",
-              imsm_orom_has_chunk(orom, 1024*8) ? " 8M" : "",
-              imsm_orom_has_chunk(orom, 1024*16) ? " 16M" : "",
-              imsm_orom_has_chunk(orom, 1024*32) ? " 32M" : "",
-              imsm_orom_has_chunk(orom, 1024*64) ? " 64M" : "");
-       printf("      Max Disks : %d\n", orom->tds);
-       printf("    Max Volumes : %d\n", orom->vpa);
-
        for (hba = list; hba; hba = hba->next) {
                printf(" I/O Controller : %s (%s)\n",
                        hba->path, get_sys_dev_type(hba->type));
@@ -1431,11 +1449,6 @@ static int detail_platform_imsm(int verbose, int enumerate_only)
                                                "ports on SATA controller at %s.", hba->pci_id);
                                result |= 2;
                        }
-               } else if (hba->type == SYS_DEV_SAS) {
-                               if (verbose)
-                                       fprintf(stderr, Name ": failed to enumerate "
-                                               "devices on SAS controller at %s.", hba->pci_id);
-                               result |= 2;
                }
        }
 
@@ -1756,14 +1769,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;
@@ -1777,17 +1829,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) &&
-                              (map->map_state == prev_map->map_state);
-       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: {
@@ -1797,6 +1846,34 @@ 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;
+
+                       /* checkpoint is written per disks unit
+                        * recalculate it to reshape position
+                        */
+                       used_disks = imsm_num_data_members(dev, 0);
+                       info->reshape_progress *= used_disks;
+                       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
@@ -1804,15 +1881,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;
@@ -2139,6 +2214,19 @@ static int compare_super_imsm(struct supertype *st, struct supertype *tst)
                 tst->sb = NULL;
                 return 0;
         }
+       /* in platform dependent environment test if the disks
+        * use the same Intel hba
+        */
+       if (!check_env("IMSM_NO_PLATFORM")) {
+               if (!first->hba || !sec->hba ||
+                   (first->hba->type != sec->hba->type))  {
+                       fprintf(stderr,
+                               "HBAs of devices does not match %s != %s\n",
+                               first->hba ? get_sys_dev_type(first->hba->type) : NULL,
+                               sec->hba ? get_sys_dev_type(sec->hba->type) : NULL);
+                       return 3;
+               }
+       }
 
        /* if an anchor does not have num_raid_devs set then it is a free
         * floating spare
@@ -2546,7 +2634,7 @@ struct bbm_log *__get_imsm_bbm_log(struct imsm_super *mpb)
 static void __free_imsm(struct intel_super *super, int free_disks);
 
 /* load_imsm_mpb - read matrix metadata
- * allocates super->mpb to be freed by free_super
+ * allocates super->mpb to be freed by free_imsm
  */
 static int load_imsm_mpb(int fd, struct intel_super *super, char *devname)
 {
@@ -2598,6 +2686,10 @@ static int load_imsm_mpb(int fd, struct intel_super *super, char *devname)
        }
 
        __free_imsm(super, 0);
+       /*  reload capability and hba */
+
+       /* capability and hba must be updated with new super allocation */
+       find_intel_hba_capability(fd, super, devname);
        super->len = ROUND_UP(anchor->mpb_size, 512);
        if (posix_memalign(&super->buf, 512, super->len) != 0) {
                if (devname)
@@ -2721,6 +2813,8 @@ static void __free_imsm(struct intel_super *super, int free_disks)
                free(super->buf);
                super->buf = NULL;
        }
+       /* unlink capability description */
+       super->orom = NULL;
        if (free_disks)
                free_imsm_disks(super);
        free_devlist(super);
@@ -2760,13 +2854,66 @@ static struct intel_super *alloc_super(void)
                memset(super, 0, sizeof(*super));
                super->current_vol = -1;
                super->create_offset = ~((__u32 ) 0);
-               if (!check_env("IMSM_NO_PLATFORM"))
-                       super->orom = find_imsm_orom();
        }
-
        return super;
 }
 
+/*
+ * find and allocate hba and OROM/EFI based on valid fd of RAID component device
+ */
+static int find_intel_hba_capability(int fd, struct intel_super *super, char *devname)
+{
+       struct sys_dev *hba_name;
+       int rv = 0;
+
+       if ((fd < 0) || check_env("IMSM_NO_PLATFORM")) {
+               super->orom = NULL;
+               super->hba = NULL;
+               return 0;
+       }
+       hba_name = find_disk_attached_hba(fd, NULL);
+       if (!hba_name) {
+               if (devname)
+                       fprintf(stderr,
+                               Name ": %s is not attached to Intel(R) RAID controller.\n",
+                               devname);
+               return 1;
+       }
+       rv = attach_hba_to_super(super, hba_name);
+       if (rv == 2) {
+               if (devname) {
+                       struct intel_hba *hba = super->hba;
+
+                       fprintf(stderr, Name ": %s is attached to Intel(R) %s RAID "
+                               "controller (%s),\n"
+                               "    but the container is assigned to Intel(R) "
+                               "%s RAID controller (",
+                               devname,
+                               hba_name->path,
+                               hba_name->pci_id ? : "Err!",
+                               get_sys_dev_type(hba_name->type));
+
+                       while (hba) {
+                               fprintf(stderr, "%s", hba->pci_id ? : "Err!");
+                               if (hba->next)
+                                       fprintf(stderr, ", ");
+                               hba = hba->next;
+                       }
+
+                       fprintf(stderr, ").\n"
+                               "    Mixing devices attached to different controllers "
+                               "is not allowed.\n");
+               }
+               free_sys_dev(&hba_name);
+               return 2;
+       }
+       super->orom = find_imsm_capability(hba_name->type);
+       free_sys_dev(&hba_name);
+       if (!super->orom)
+               return 3;
+       return 0;
+}
+
 #ifndef MDASSEMBLE
 /* find_missing - helper routine for load_super_imsm_all that identifies
  * disks that have disappeared from the system.  This routine relies on
@@ -3147,6 +3294,7 @@ static int load_super_imsm_all(struct supertype *st, int fd, void **sbp,
                struct intel_super *s = alloc_super();
                char nm[32];
                int dfd;
+               int rv;
 
                err = 1;
                if (!s)
@@ -3160,6 +3308,11 @@ static int load_super_imsm_all(struct supertype *st, int fd, void **sbp,
                if (dfd < 0)
                        goto error;
 
+               rv = find_intel_hba_capability(dfd, s, devname);
+               /* no orom/efi or non-intel hba of the disk */
+               if (rv != 0)
+                       goto error;
+
                err = load_and_parse_mpb(dfd, s, NULL, 1);
 
                /* retry the load if we might have raced against mdmon */
@@ -3234,7 +3387,19 @@ static int load_super_imsm(struct supertype *st, int fd, char *devname)
                        sizeof(*super));
                return 1;
        }
-
+       /* Load hba and capabilities if they exist.
+        * But do not preclude loading metadata in case capabilities or hba are
+        * non-compliant and ignore_hw_compat is set.
+        */
+       rv = find_intel_hba_capability(fd, super, devname);
+       /* no orom/efi or non-intel hba of the disk */
+       if ((rv != 0) && (st->ignore_hw_compat == 0)) {
+               if (devname)
+                       fprintf(stderr,
+                               Name ": No OROM/EFI properties for %s\n", devname);
+               free_imsm(super);
+               return 2;
+       }
        rv = load_and_parse_mpb(fd, super, devname, 0);
 
        if (rv) {
@@ -3409,12 +3574,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);
@@ -3427,8 +3593,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);
@@ -3627,43 +3792,12 @@ static int add_to_super_imsm(struct supertype *st, mdu_disk_info_t *dk,
         * We do not need to test disks attachment for container based additions,
         * they shall be already tested when container was created/assembled.
         */
-       if ((fd != -1) && !check_env("IMSM_NO_PLATFORM")) {
-               struct sys_dev *hba_name;
-               struct intel_hba *hba;
-
-               hba_name = find_disk_attached_hba(fd, NULL);
-               if (!hba_name) {
-                       fprintf(stderr,
-                               Name ": %s is not attached to Intel(R) RAID controller.\n",
-                               devname ? : "disk");
-                       return 1;
-               }
-               rv = attach_hba_to_super(super, hba_name, devname);
-               switch (rv) {
-               case 2:
-                       fprintf(stderr, Name ": %s is attached to Intel(R) %s RAID "
-                               "controller (%s),\n    but the container is assigned to Intel(R) "
-                               "%s RAID controller (",
-                               devname,
-                               get_sys_dev_type(hba_name->type),
-                               hba_name->pci_id ? : "Err!",
-                               get_sys_dev_type(hba_name->type));
-
-                       hba = super->hba;
-                       while (hba) {
-                               fprintf(stderr, "%s", hba->pci_id ? : "Err!");
-                               if (hba->next)
-                                       fprintf(stderr, ", ");
-                               hba = hba->next;
-                       }
-
-                       fprintf(stderr, ").\n"
-                               "    Mixing devices attached to different controllers "
-                               "is not allowed.\n");
-                       free_sys_dev(&hba_name);
-                       return 1;
-               }
-               free_sys_dev(&hba_name);
+       rv = find_intel_hba_capability(fd, super, devname);
+       /* no orom/efi or non-intel hba of the disk */
+       if (rv != 0) {
+               dprintf("capability: %p fd: %d ret: %d\n",
+                       super->orom, fd, rv);
+               return 1;
        }
 
        if (super->current_vol >= 0)
@@ -3708,6 +3842,7 @@ static int add_to_super_imsm(struct supertype *st, mdu_disk_info_t *dk,
        } else {
                dd->next = super->disks;
                super->disks = dd;
+               super->updates_pending++;
        }
 
        return 0;
@@ -3995,25 +4130,14 @@ static int validate_geometry_imsm_container(struct supertype *st, int level,
 {
        int fd;
        unsigned long long ldsize;
-       const struct imsm_orom *orom;
+       struct intel_super *super=NULL;
+       int rv = 0;
 
        if (level != LEVEL_CONTAINER)
                return 0;
        if (!dev)
                return 1;
 
-       if (check_env("IMSM_NO_PLATFORM"))
-               orom = NULL;
-       else
-               orom = find_imsm_orom();
-       if (orom && raiddisks > orom->tds) {
-               if (verbose)
-                       fprintf(stderr, Name ": %d exceeds maximum number of"
-                               " platform supported disks: %d\n",
-                               raiddisks, orom->tds);
-               return 0;
-       }
-
        fd = open(dev, O_RDONLY|O_EXCL, 0);
        if (fd < 0) {
                if (verbose)
@@ -4025,9 +4149,45 @@ static int validate_geometry_imsm_container(struct supertype *st, int level,
                close(fd);
                return 0;
        }
+
+       /* capabilities retrieve could be possible
+        * note that there is no fd for the disks in array.
+        */
+       super = alloc_super();
+       if (!super) {
+               fprintf(stderr,
+                       Name ": malloc of %zu failed.\n",
+                       sizeof(*super));
+               close(fd);
+               return 0;
+       }
+
+       rv = find_intel_hba_capability(fd, super, verbose ? dev : NULL);
+       if (rv != 0) {
+#if DEBUG
+               char str[256];
+               fd2devname(fd, str);
+               dprintf("validate_geometry_imsm_container: fd: %d %s orom: %p rv: %d raiddisk: %d\n",
+                       fd, str, super->orom, rv, raiddisks);
+#endif
+               /* no orom/efi or non-intel hba of the disk */
+               close(fd);
+               free_imsm(super);
+               return 0;
+       }
        close(fd);
+       if (super->orom && raiddisks > super->orom->tds) {
+               if (verbose)
+                       fprintf(stderr, Name ": %d exceeds maximum number of"
+                               " platform supported disks: %d\n",
+                               raiddisks, super->orom->tds);
+
+               free_imsm(super);
+               return 0;
+       }
 
        *freesize = avail_size_imsm(st, ldsize >> 9);
+       free_imsm(super);
 
        return 1;
 }
@@ -4162,12 +4322,30 @@ static int is_raid_level_supported(const struct imsm_orom *orom, int level, int
        return 0;
 }
 
+
 #define pr_vrb(fmt, arg...) (void) (verbose && fprintf(stderr, Name fmt, ##arg))
+/*
+ * validate volume parameters with OROM/EFI capabilities
+ */
 static int
 validate_geometry_imsm_orom(struct intel_super *super, int level, int layout,
                            int raiddisks, int *chunk, int verbose)
 {
-       if (!is_raid_level_supported(super->orom, level, raiddisks)) {
+#if DEBUG
+       verbose = 1;
+#endif
+       /* validate container capabilities */
+       if (super->orom && raiddisks > super->orom->tds) {
+               if (verbose)
+                       fprintf(stderr, Name ": %d exceeds maximum number of"
+                               " platform supported disks: %d\n",
+                               raiddisks, super->orom->tds);
+               return 0;
+       }
+
+        /* capabilities of OROM tested - copied from validate_geometry_imsm_volume */
+       if (super->orom && (!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;
@@ -4191,7 +4369,6 @@ validate_geometry_imsm_orom(struct intel_super *super, int level, int layout,
                                layout, level);
                return 0;
        }
-
        return 1;
 }
 
@@ -4217,9 +4394,11 @@ static int validate_geometry_imsm_volume(struct supertype *st, int level,
        if (!super)
                return 0;
 
-       if (!validate_geometry_imsm_orom(super, level, layout, raiddisks, chunk, verbose))
+       if (!validate_geometry_imsm_orom(super, level, layout, raiddisks, chunk, verbose)) {
+               fprintf(stderr, Name ": RAID gemetry validation failed. "
+                       "Cannot proceed with the action(s).\n");
                return 0;
-
+       }
        if (!dev) {
                /* General test:  make sure there is space for
                 * 'raiddisks' device extents of size 'size' at a given
@@ -4426,7 +4605,8 @@ static int validate_geometry_imsm(struct supertype *st, int level, int layout,
        struct mdinfo *sra;
        int is_member = 0;
 
-       /* if given unused devices create a container 
+       /* load capability
+        * if given unused devices create a container
         * if given given devices in a container create a member volume
         */
        if (level == LEVEL_CONTAINER) {
@@ -4756,7 +4936,7 @@ static struct mdinfo *container_content_imsm(struct supertype *st, char *subarra
                struct imsm_map *map;
                struct imsm_map *map2;
                struct mdinfo *this;
-               int slot;
+               int slot, chunk;
                char *ep;
 
                if (subarray &&
@@ -4777,7 +4957,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",
@@ -5024,6 +5218,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);
@@ -5036,8 +5232,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;
@@ -5180,18 +5380,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.
@@ -5225,7 +5414,7 @@ static int imsm_set_array_state(struct active_array *a, int consistent)
                                         */
 
                                        a->check_reshape = 1;
-}
+                               }
                                /* finalize online capacity expansion/reshape */
                                for (mdi = a->info.devs; mdi; mdi = mdi->next)
                                        imsm_set_disk(a,
@@ -5268,6 +5457,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) {
@@ -6041,8 +6231,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;
@@ -6063,9 +6251,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 */
@@ -6076,9 +6263,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;
 }
@@ -6127,8 +6315,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;
        }
 
@@ -6857,7 +7047,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;
        }