]> git.ipfire.org Git - thirdparty/mdadm.git/blobdiff - super-intel.c
Create.c: fix uclibc build
[thirdparty/mdadm.git] / super-intel.c
index e4d2122daeeef218e582cfc320b427c1fd1c7d4c..1faab6077f9ecebc6edafc6fbf0d6a1dcaf01afb 100644 (file)
 #define HAVE_STDINT_H 1
 #include "mdadm.h"
 #include "mdmon.h"
+#include "dlink.h"
 #include "sha1.h"
 #include "platform-intel.h"
 #include <values.h>
 #include <scsi/sg.h>
 #include <ctype.h>
 #include <dirent.h>
+#include "drive_encryption.h"
 
 /* MPB == Metadata Parameter Block */
 #define MPB_SIGNATURE "Intel Raid ISM Cfg Sig. "
                                                   * mutliple PPL area
                                                   */
 
+/*
+ * Internal Write-intent bitmap is stored in the same area where PPL.
+ * Both features are mutually exclusive, so it is not an issue.
+ * The first 8KiB of the area are reserved and shall not be used.
+ */
+#define IMSM_BITMAP_AREA_RESERVED_SIZE 8192
+
+#define IMSM_BITMAP_HEADER_OFFSET (IMSM_BITMAP_AREA_RESERVED_SIZE)
+#define IMSM_BITMAP_HEADER_SIZE MAX_SECTOR_SIZE
+
+#define IMSM_BITMAP_START_OFFSET (IMSM_BITMAP_HEADER_OFFSET + IMSM_BITMAP_HEADER_SIZE)
+#define IMSM_BITMAP_AREA_SIZE (MULTIPLE_PPL_AREA_SIZE_IMSM - IMSM_BITMAP_START_OFFSET)
+#define IMSM_BITMAP_AND_HEADER_SIZE (IMSM_BITMAP_AREA_SIZE + IMSM_BITMAP_HEADER_SIZE)
+
+#define IMSM_DEFAULT_BITMAP_CHUNKSIZE (64 * 1024 * 1024)
+#define IMSM_DEFAULT_BITMAP_DAEMON_SLEEP 5
+
 /*
  * This macro let's us ensure that no-one accidentally
  * changes the size of a struct
@@ -164,7 +183,7 @@ struct imsm_map {
 ASSERT_SIZE(imsm_map, 52)
 
 struct imsm_vol {
-       __u32 curr_migr_unit;
+       __u32 curr_migr_unit_lo;
        __u32 checkpoint_id;    /* id to access curr_migr_unit */
        __u8  migr_state;       /* Normal or Migrating */
 #define MIGR_INIT 0
@@ -181,7 +200,8 @@ struct imsm_vol {
        __u8  fs_state;         /* fast-sync state for CnG (0xff == disabled) */
        __u16 verify_errors;    /* number of mismatches */
        __u16 bad_blocks;       /* number of bad blocks during verify */
-       __u32 filler[4];
+       __u32 curr_migr_unit_hi;
+       __u32 filler[3];
        struct imsm_map map[1];
        /* here comes another one if migr_state */
 };
@@ -229,6 +249,7 @@ struct imsm_dev {
 #define RWH_MULTIPLE_DISTRIBUTED 3
 #define RWH_MULTIPLE_PPLS_JOURNALING_DRIVE 4
 #define RWH_MULTIPLE_OFF 5
+#define RWH_BITMAP 6
        __u8  rwh_policy; /* Raid Write Hole Policy */
        __u8  jd_serial[MAX_RAID_SERIAL_LEN]; /* Journal Drive serial number */
        __u8  filler1;
@@ -260,8 +281,9 @@ struct imsm_super {
                                         * (starts at 1)
                                         */
        __u16 filler1;                  /* 0x4E - 0x4F */
-#define IMSM_FILLERS 34
-       __u32 filler[IMSM_FILLERS];     /* 0x50 - 0xD7 RAID_MPB_FILLERS */
+       __u64 creation_time;            /* 0x50 - 0x57 Array creation time */
+#define IMSM_FILLERS 32
+       __u32 filler[IMSM_FILLERS];     /* 0x58 - 0xD7 RAID_MPB_FILLERS */
        struct imsm_disk disk[1];       /* 0xD8 diskTbl[numDisks] */
        /* here comes imsm_dev[num_raid_devs] */
        /* here comes BBM logs */
@@ -342,8 +364,21 @@ struct migr_record {
                                       * destination - high order 32 bits */
        __u32 num_migr_units_hi;      /* Total num migration units-of-op
                                       * high order 32 bits */
+       __u32 filler[16];
 };
-ASSERT_SIZE(migr_record, 64)
+ASSERT_SIZE(migr_record, 128)
+
+/**
+ * enum imsm_status - internal IMSM return values representation.
+ * @STATUS_OK: function succeeded.
+ * @STATUS_ERROR: General error ocurred (not specified).
+ *
+ * Typedefed to imsm_status_t.
+ */
+typedef enum imsm_status {
+       IMSM_STATUS_ERROR = -1,
+       IMSM_STATUS_OK = 0,
+} imsm_status_t;
 
 struct md_list {
        /* usage marker:
@@ -359,8 +394,6 @@ struct md_list {
        struct md_list *next;
 };
 
-#define pr_vrb(fmt, arg...) (void) (verbose && pr_err(fmt, ##arg))
-
 static __u8 migr_type(struct imsm_dev *dev)
 {
        if (dev->vol.migr_type == MIGR_VERIFY &&
@@ -465,8 +498,15 @@ struct intel_disk {
        struct intel_disk *next;
 };
 
+/**
+ * struct extent - reserved space details.
+ * @start: start offset.
+ * @size: size of reservation, set to 0 for metadata reservation.
+ * @vol: index of the volume, meaningful if &size is set.
+ */
 struct extent {
        unsigned long long start, size;
+       int vol;
 };
 
 /* definitions of reshape process types */
@@ -550,7 +590,7 @@ struct imsm_update_size_change {
 
 struct imsm_update_general_migration_checkpoint {
        enum imsm_update_type type;
-       __u32 curr_migr_unit;
+       __u64 curr_migr_unit;
 };
 
 struct disk_info {
@@ -593,9 +633,53 @@ static const char *_sys_dev_type[] = {
        [SYS_DEV_SAS] = "SAS",
        [SYS_DEV_SATA] = "SATA",
        [SYS_DEV_NVME] = "NVMe",
-       [SYS_DEV_VMD] = "VMD"
+       [SYS_DEV_VMD] = "VMD",
+       [SYS_DEV_SATA_VMD] = "SATA VMD"
 };
 
+static int no_platform = -1;
+
+static int check_no_platform(void)
+{
+       static const char search[] = "mdadm.imsm.test=1";
+       FILE *fp;
+
+       if (no_platform >= 0)
+               return no_platform;
+
+       if (check_env("IMSM_NO_PLATFORM")) {
+               no_platform = 1;
+               return 1;
+       }
+       fp = fopen("/proc/cmdline", "r");
+       if (fp) {
+               char *l = conf_line(fp);
+               char *w = l;
+
+               if (l == NULL) {
+                       fclose(fp);
+                       return 0;
+               }
+
+               do {
+                       if (strcmp(w, search) == 0)
+                               no_platform = 1;
+                       w = dl_next(w);
+               } while (w != l);
+               free_line(l);
+               fclose(fp);
+               if (no_platform >= 0)
+                       return no_platform;
+       }
+       no_platform = 0;
+       return 0;
+}
+
+void imsm_set_no_platform(int v)
+{
+       no_platform = v;
+}
+
 const char *get_sys_dev_type(enum sys_dev_type type)
 {
        if (type >= SYS_DEV_MAX)
@@ -670,22 +754,22 @@ static struct sys_dev* find_disk_attached_hba(int fd, const char *devname)
        if ((list = find_intel_devices()) == NULL)
                return 0;
 
-       if (fd < 0)
+       if (!is_fd_valid(fd))
                disk_path  = (char *) devname;
        else
-               disk_path = diskfd_to_devpath(fd);
+               disk_path = diskfd_to_devpath(fd, 1, NULL);
 
        if (!disk_path)
                return 0;
 
        for (elem = list; elem; elem = elem->next)
                if (path_attached_to_hba(disk_path, elem->path))
-                       return elem;
+                       break;
 
        if (disk_path != devname)
                free(disk_path);
 
-       return NULL;
+       return elem;
 }
 
 static int find_intel_hba_capability(int fd, struct intel_super *super,
@@ -830,6 +914,21 @@ static struct disk_info *get_disk_info(struct imsm_update_create_array *update)
        return inf;
 }
 
+/**
+ * __get_imsm_dev() - Get device with index from imsm_super.
+ * @mpb: &imsm_super pointer, not NULL.
+ * @index: Device index.
+ *
+ * Function works as non-NULL, aborting in such a case,
+ * when NULL would be returned.
+ *
+ * Device index should be in range 0 up to num_raid_devs.
+ * Function assumes the index was already verified.
+ * Index must be valid, otherwise abort() is called.
+ *
+ * Return: Pointer to corresponding imsm_dev.
+ *
+ */
 static struct imsm_dev *__get_imsm_dev(struct imsm_super *mpb, __u8 index)
 {
        int offset;
@@ -837,30 +936,47 @@ static struct imsm_dev *__get_imsm_dev(struct imsm_super *mpb, __u8 index)
        void *_mpb = mpb;
 
        if (index >= mpb->num_raid_devs)
-               return NULL;
+               goto error;
 
        /* devices start after all disks */
        offset = ((void *) &mpb->disk[mpb->num_disks]) - _mpb;
 
-       for (i = 0; i <= index; i++)
+       for (i = 0; i <= index; i++, offset += sizeof_imsm_dev(_mpb + offset, 0))
                if (i == index)
                        return _mpb + offset;
-               else
-                       offset += sizeof_imsm_dev(_mpb + offset, 0);
-
-       return NULL;
+error:
+       pr_err("cannot find imsm_dev with index %u in imsm_super\n", index);
+       abort();
 }
 
+/**
+ * get_imsm_dev() - Get device with index from intel_super.
+ * @super: &intel_super pointer, not NULL.
+ * @index: Device index.
+ *
+ * Function works as non-NULL, aborting in such a case,
+ * when NULL would be returned.
+ *
+ * Device index should be in range 0 up to num_raid_devs.
+ * Function assumes the index was already verified.
+ * Index must be valid, otherwise abort() is called.
+ *
+ * Return: Pointer to corresponding imsm_dev.
+ *
+ */
 static struct imsm_dev *get_imsm_dev(struct intel_super *super, __u8 index)
 {
        struct intel_dev *dv;
 
        if (index >= super->anchor->num_raid_devs)
-               return NULL;
+               goto error;
+
        for (dv = super->devlist; dv; dv = dv->next)
                if (dv->index == index)
                        return dv->dev;
-       return NULL;
+error:
+       pr_err("cannot find imsm_dev with index %u in intel_super\n", index);
+       abort();
 }
 
 static inline unsigned long long __le48_to_cpu(const struct bbm_log_block_addr
@@ -1130,7 +1246,7 @@ static void set_imsm_ord_tbl_ent(struct imsm_map *map, int slot, __u32 ord)
        map->disk_ord_tbl[slot] = __cpu_to_le32(ord);
 }
 
-static int get_imsm_disk_slot(struct imsm_map *map, unsigned idx)
+static int get_imsm_disk_slot(struct imsm_map *map, const unsigned int idx)
 {
        int slot;
        __u32 ord;
@@ -1141,7 +1257,7 @@ static int get_imsm_disk_slot(struct imsm_map *map, unsigned idx)
                        return slot;
        }
 
-       return -1;
+       return IMSM_STATUS_ERROR;
 }
 
 static int get_imsm_raid_level(struct imsm_map *map)
@@ -1156,6 +1272,23 @@ static int get_imsm_raid_level(struct imsm_map *map)
        return map->raid_level;
 }
 
+/**
+ * get_disk_slot_in_dev() - retrieve disk slot from &imsm_dev.
+ * @super: &intel_super pointer, not NULL.
+ * @dev_idx: imsm device index.
+ * @idx: disk index.
+ *
+ * Return: Slot on success, IMSM_STATUS_ERROR otherwise.
+ */
+static int get_disk_slot_in_dev(struct intel_super *super, const __u8 dev_idx,
+                               const unsigned int idx)
+{
+       struct imsm_dev *dev = get_imsm_dev(super, dev_idx);
+       struct imsm_map *map = get_imsm_map(dev, MAP_0);
+
+       return get_imsm_disk_slot(map, idx);
+}
+
 static int cmp_extent(const void *av, const void *bv)
 {
        const struct extent *a = av;
@@ -1172,13 +1305,9 @@ static int count_memberships(struct dl *dl, struct intel_super *super)
        int memberships = 0;
        int i;
 
-       for (i = 0; i < super->anchor->num_raid_devs; i++) {
-               struct imsm_dev *dev = get_imsm_dev(super, i);
-               struct imsm_map *map = get_imsm_map(dev, MAP_0);
-
-               if (get_imsm_disk_slot(map, dl->index) >= 0)
+       for (i = 0; i < super->anchor->num_raid_devs; i++)
+               if (get_disk_slot_in_dev(super, i, dl->index) >= 0)
                        memberships++;
-       }
 
        return memberships;
 }
@@ -1207,6 +1336,33 @@ static unsigned long long total_blocks(struct imsm_disk *disk)
        return join_u32(disk->total_blocks_lo, disk->total_blocks_hi);
 }
 
+/**
+ * imsm_num_data_members() - get data drives count for an array.
+ * @map: Map to analyze.
+ *
+ * num_data_members value represents minimal count of drives for level.
+ * The name of the property could be misleading for RAID5 with asymmetric layout
+ * because some data required to be calculated from parity.
+ * The property is extracted from level and num_members value.
+ *
+ * Return: num_data_members value on success, zero otherwise.
+ */
+static __u8 imsm_num_data_members(struct imsm_map *map)
+{
+       switch (get_imsm_raid_level(map)) {
+       case 0:
+               return map->num_members;
+       case 1:
+       case 10:
+               return map->num_members / 2;
+       case 5:
+               return map->num_members - 1;
+       default:
+               dprintf("unsupported raid level\n");
+               return 0;
+       }
+}
+
 static unsigned long long pba_of_lba0(struct imsm_map *map)
 {
        if (map == NULL)
@@ -1228,6 +1384,14 @@ static unsigned long long num_data_stripes(struct imsm_map *map)
        return join_u32(map->num_data_stripes_lo, map->num_data_stripes_hi);
 }
 
+static unsigned long long vol_curr_migr_unit(struct imsm_dev *dev)
+{
+       if (dev == NULL)
+               return 0;
+
+       return join_u32(dev->vol.curr_migr_unit_lo, dev->vol.curr_migr_unit_hi);
+}
+
 static unsigned long long imsm_dev_size(struct imsm_dev *dev)
 {
        if (dev == NULL)
@@ -1272,6 +1436,24 @@ static void set_total_blocks(struct imsm_disk *disk, unsigned long long n)
        split_ull(n, &disk->total_blocks_lo, &disk->total_blocks_hi);
 }
 
+/**
+ * set_num_domains() - Set number of domains for an array.
+ * @map: Map to be updated.
+ *
+ * num_domains property represents copies count of each data drive, thus make
+ * it meaningful only for RAID1 and RAID10. IMSM supports two domains for
+ * raid1 and raid10.
+ */
+static void set_num_domains(struct imsm_map *map)
+{
+       int level = get_imsm_raid_level(map);
+
+       if (level == 1 || level == 10)
+               map->num_domains = 2;
+       else
+               map->num_domains = 1;
+}
+
 static void set_pba_of_lba0(struct imsm_map *map, unsigned long long n)
 {
        split_ull(n, &map->pba_of_lba0_lo, &map->pba_of_lba0_hi);
@@ -1287,6 +1469,32 @@ static void set_num_data_stripes(struct imsm_map *map, unsigned long long n)
        split_ull(n, &map->num_data_stripes_lo, &map->num_data_stripes_hi);
 }
 
+/**
+ * update_num_data_stripes() - Calculate and update num_data_stripes value.
+ * @map: map to be updated.
+ * @dev_size: size of volume.
+ *
+ * num_data_stripes value is addictionally divided by num_domains, therefore for
+ * levels where num_domains is not 1, nds is a part of real value.
+ */
+static void update_num_data_stripes(struct imsm_map *map,
+                                    unsigned long long dev_size)
+{
+       unsigned long long nds = dev_size / imsm_num_data_members(map);
+
+       nds /= map->num_domains;
+       nds /= map->blocks_per_strip;
+       set_num_data_stripes(map, nds);
+}
+
+static void set_vol_curr_migr_unit(struct imsm_dev *dev, unsigned long long n)
+{
+       if (dev == NULL)
+               return;
+
+       split_ull(n, &dev->vol.curr_migr_unit_lo, &dev->vol.curr_migr_unit_hi);
+}
+
 static void set_imsm_dev_size(struct imsm_dev *dev, unsigned long long n)
 {
        split_ull(n, &dev->size_low, &dev->size_high);
@@ -1337,9 +1545,10 @@ static struct extent *get_extents(struct intel_super *super, struct dl *dl,
                                  int get_minimal_reservation)
 {
        /* find a list of used extents on the given physical device */
-       struct extent *rv, *e;
-       int i;
        int memberships = count_memberships(dl, super);
+       struct extent *rv = xcalloc(memberships + 1, sizeof(struct extent));
+       struct extent *e = rv;
+       int i;
        __u32 reservation;
 
        /* trim the reserved area for spares, so they can join any array
@@ -1351,9 +1560,6 @@ static struct extent *get_extents(struct intel_super *super, struct dl *dl,
        else
                reservation = MPB_SECTOR_CNT + IMSM_RESERVED_SECTORS;
 
-       rv = xcalloc(sizeof(struct extent), (memberships + 1));
-       e = rv;
-
        for (i = 0; i < super->anchor->num_raid_devs; i++) {
                struct imsm_dev *dev = get_imsm_dev(super, i);
                struct imsm_map *map = get_imsm_map(dev, MAP_0);
@@ -1361,6 +1567,7 @@ static struct extent *get_extents(struct intel_super *super, struct dl *dl,
                if (get_imsm_disk_slot(map, dl->index) >= 0) {
                        e->start = pba_of_lba0(map);
                        e->size = per_dev_array_size(map);
+                       e->vol = i;
                        e++;
                }
        }
@@ -1442,17 +1649,29 @@ static int is_journal(struct imsm_disk *disk)
        return (disk->status & JOURNAL_DISK) == JOURNAL_DISK;
 }
 
-/* round array size down to closest MB and ensure it splits evenly
- * between members
+/**
+ * round_member_size_to_mb()- Round given size to closest MiB.
+ * @size: size to round in sectors.
  */
-static unsigned long long round_size_to_mb(unsigned long long size, unsigned int
-                                          disk_count)
+static inline unsigned long long round_member_size_to_mb(unsigned long long size)
 {
-       size /= disk_count;
-       size = (size >> SECT_PER_MB_SHIFT) << SECT_PER_MB_SHIFT;
-       size *= disk_count;
+       return (size >> SECT_PER_MB_SHIFT) << SECT_PER_MB_SHIFT;
+}
 
-       return size;
+/**
+ * round_size_to_mb()- Round given size.
+ * @array_size: size to round in sectors.
+ * @disk_count: count of data members.
+ *
+ * Get size per each data member and round it to closest MiB to ensure that data
+ * splits evenly between members.
+ *
+ * Return: Array size, rounded down.
+ */
+static inline unsigned long long round_size_to_mb(unsigned long long array_size,
+                                                 unsigned int disk_count)
+{
+       return round_member_size_to_mb(array_size / disk_count) * disk_count;
 }
 
 static int able_to_resync(int raid_level, int missing_disks)
@@ -1516,49 +1735,7 @@ static __u32 imsm_min_reserved_sectors(struct intel_super *super)
        return  (remainder < rv) ? remainder : rv;
 }
 
-/*
- * Return minimum size of a spare and sector size
- * that can be used in this array
- */
-int get_spare_criteria_imsm(struct supertype *st, struct spare_criteria *c)
-{
-       struct intel_super *super = st->sb;
-       struct dl *dl;
-       struct extent *e;
-       int i;
-       unsigned long long size = 0;
-
-       c->min_size = 0;
-       c->sector_size = 0;
-
-       if (!super)
-               return -EINVAL;
-       /* find first active disk in array */
-       dl = super->disks;
-       while (dl && (is_failed(&dl->disk) || dl->index == -1))
-               dl = dl->next;
-       if (!dl)
-               return -EINVAL;
-       /* find last lba used by subarrays */
-       e = get_extents(super, dl, 0);
-       if (!e)
-               return -EINVAL;
-       for (i = 0; e[i].size; i++)
-               continue;
-       if (i > 0)
-               size = e[i-1].start + e[i-1].size;
-       free(e);
-
-       /* add the amount of space needed for metadata */
-       size += imsm_min_reserved_sectors(super);
-
-       c->min_size = size * 512;
-       c->sector_size = super->sector_size;
-
-       return 0;
-}
-
-static int is_gen_migration(struct imsm_dev *dev);
+static bool is_gen_migration(struct imsm_dev *dev);
 
 #define IMSM_4K_DIV 8
 
@@ -1578,6 +1755,7 @@ static void print_imsm_dev(struct intel_super *super,
 
        printf("\n");
        printf("[%.16s]:\n", dev->volume);
+       printf("       Subarray : %d\n", super->current_vol);
        printf("           UUID : %s\n", uuid);
        printf("     RAID Level : %d", get_imsm_raid_level(map));
        if (map2)
@@ -1604,7 +1782,7 @@ static void print_imsm_dev(struct intel_super *super,
        printf("\n");
        printf("    Failed disk : ");
        if (map->failed_disk_num == 0xff)
-               printf("none");
+               printf(STR_COMMON_NONE);
        else
                printf("%i", map->failed_disk_num);
        printf("\n");
@@ -1625,7 +1803,7 @@ static void print_imsm_dev(struct intel_super *super,
                   (unsigned long long)sz * 512 / super->sector_size,
               human_size(sz * 512));
        printf("  Sector Offset : %llu\n",
-               pba_of_lba0(map));
+               pba_of_lba0(map) * 512 / super->sector_size);
        printf("    Num Stripes : %llu\n",
                num_data_stripes(map));
        printf("     Chunk Size : %u KiB",
@@ -1658,8 +1836,7 @@ static void print_imsm_dev(struct intel_super *super,
                struct imsm_map *map = get_imsm_map(dev, MAP_1);
 
                printf(" <-- %s", map_state_str[map->map_state]);
-               printf("\n     Checkpoint : %u ",
-                          __le32_to_cpu(dev->vol.curr_migr_unit));
+               printf("\n     Checkpoint : %llu ", vol_curr_migr_unit(dev));
                if (is_gen_migration(dev) && (slot > 1 || slot < 0))
                        printf("(N/A)");
                else
@@ -1680,8 +1857,12 @@ static void print_imsm_dev(struct intel_super *super,
                printf("Multiple distributed PPLs\n");
        else if (dev->rwh_policy == RWH_MULTIPLE_PPLS_JOURNALING_DRIVE)
                printf("Multiple PPLs on journaling drive\n");
+       else if (dev->rwh_policy == RWH_BITMAP)
+               printf("Write-intent bitmap\n");
        else
                printf("<unknown:%d>\n", dev->rwh_policy);
+
+       printf("      Volume ID : %u\n", dev->my_vol_raid_dev_num);
 }
 
 static void print_imsm_disk(struct imsm_disk *disk,
@@ -1748,7 +1929,8 @@ void convert_to_4k(struct intel_super *super)
                struct imsm_map *map = get_imsm_map(dev, MAP_0);
                /* dev */
                set_imsm_dev_size(dev, imsm_dev_size(dev)/IMSM_4K_DIV);
-               dev->vol.curr_migr_unit /= IMSM_4K_DIV;
+               set_vol_curr_migr_unit(dev,
+                                      vol_curr_migr_unit(dev) / IMSM_4K_DIV);
 
                /* map0 */
                set_blocks_per_member(map, blocks_per_member(map)/IMSM_4K_DIV);
@@ -1797,13 +1979,14 @@ void examine_migr_rec_imsm(struct intel_super *super)
                struct imsm_map *map;
                int slot = -1;
 
-               if (is_gen_migration(dev) == 0)
+               if (is_gen_migration(dev) == false)
                                continue;
 
                printf("\nMigration Record Information:");
 
                /* first map under migration */
                map = get_imsm_map(dev, MAP_0);
+
                if (map)
                        slot = get_imsm_disk_slot(map, super->disks->index);
                if (map == NULL || slot > 1 || slot < 0) {
@@ -1876,7 +2059,8 @@ void convert_from_4k(struct intel_super *super)
                struct imsm_map *map = get_imsm_map(dev, MAP_0);
                /* dev */
                set_imsm_dev_size(dev, imsm_dev_size(dev)*IMSM_4K_DIV);
-               dev->vol.curr_migr_unit *= IMSM_4K_DIV;
+               set_vol_curr_migr_unit(dev,
+                                      vol_curr_migr_unit(dev) * IMSM_4K_DIV);
 
                /* map0 */
                set_blocks_per_member(map, blocks_per_member(map)*IMSM_4K_DIV);
@@ -2014,6 +2198,7 @@ static void examine_super_imsm(struct supertype *st, char *homehost)
        __u32 sum;
        __u32 reserved = imsm_reserved_sectors(super, super->disks);
        struct dl *dl;
+       time_t creation_time;
 
        strncpy(str, (char *)mpb->sig, MPB_SIG_LEN);
        str[MPB_SIG_LEN-1] = '\0';
@@ -2022,13 +2207,16 @@ static void examine_super_imsm(struct supertype *st, char *homehost)
        printf("    Orig Family : %08x\n", __le32_to_cpu(mpb->orig_family_num));
        printf("         Family : %08x\n", __le32_to_cpu(mpb->family_num));
        printf("     Generation : %08x\n", __le32_to_cpu(mpb->generation_num));
+       creation_time = __le64_to_cpu(mpb->creation_time);
+       printf("  Creation Time : %.24s\n",
+               creation_time ? ctime(&creation_time) : "Unknown");
        printf("     Attributes : ");
        if (imsm_check_attributes(mpb->attributes))
                printf("All supported\n");
        else
                printf("not supported\n");
        getinfo_super_imsm(st, &info, NULL);
-       fname_from_uuid(st, &info, nbuf, ':');
+       fname_from_uuid(&info, nbuf);
        printf("           UUID : %s\n", nbuf + 5);
        sum = __le32_to_cpu(mpb->check_sum);
        printf("       Checksum : %08x %s\n", sum,
@@ -2053,7 +2241,7 @@ static void examine_super_imsm(struct supertype *st, char *homehost)
 
                super->current_vol = i;
                getinfo_super_imsm(st, &info, NULL);
-               fname_from_uuid(st, &info, nbuf, ':');
+               fname_from_uuid(&info, nbuf);
                print_imsm_dev(super, dev, nbuf + 5, super->disks->index);
        }
        for (i = 0; i < mpb->num_disks; i++) {
@@ -2076,15 +2264,9 @@ static void brief_examine_super_imsm(struct supertype *st, int verbose)
        /* We just write a generic IMSM ARRAY entry */
        struct mdinfo info;
        char nbuf[64];
-       struct intel_super *super = st->sb;
-
-       if (!super->anchor->num_raid_devs) {
-               printf("ARRAY metadata=imsm\n");
-               return;
-       }
 
        getinfo_super_imsm(st, &info, NULL);
-       fname_from_uuid(st, &info, nbuf, ':');
+       fname_from_uuid(&info, nbuf);
        printf("ARRAY metadata=imsm UUID=%s\n", nbuf + 5);
 }
 
@@ -2101,14 +2283,14 @@ static void brief_examine_subarrays_imsm(struct supertype *st, int verbose)
                return;
 
        getinfo_super_imsm(st, &info, NULL);
-       fname_from_uuid(st, &info, nbuf, ':');
+       fname_from_uuid(&info, nbuf);
        for (i = 0; i < super->anchor->num_raid_devs; i++) {
                struct imsm_dev *dev = get_imsm_dev(super, i);
 
                super->current_vol = i;
                getinfo_super_imsm(st, &info, NULL);
-               fname_from_uuid(st, &info, nbuf1, ':');
-               printf("ARRAY /dev/md/%.16s container=%s member=%d UUID=%s\n",
+               fname_from_uuid(&info, nbuf1);
+               printf("ARRAY " DEV_MD_DIR "%.16s container=%s member=%d UUID=%s\n",
                       dev->volume, nbuf + 5, i, nbuf1 + 5);
        }
 }
@@ -2121,66 +2303,12 @@ static void export_examine_super_imsm(struct supertype *st)
        char nbuf[64];
 
        getinfo_super_imsm(st, &info, NULL);
-       fname_from_uuid(st, &info, nbuf, ':');
+       fname_from_uuid(&info, nbuf);
        printf("MD_METADATA=imsm\n");
        printf("MD_LEVEL=container\n");
        printf("MD_UUID=%s\n", nbuf+5);
        printf("MD_DEVICES=%u\n", mpb->num_disks);
-}
-
-static int copy_metadata_imsm(struct supertype *st, int from, int to)
-{
-       /* The second last sector of the device contains
-        * the "struct imsm_super" metadata.
-        * This contains mpb_size which is the size in bytes of the
-        * extended metadata.  This is located immediately before
-        * the imsm_super.
-        * We want to read all that, plus the last sector which
-        * may contain a migration record, and write it all
-        * to the target.
-        */
-       void *buf;
-       unsigned long long dsize, offset;
-       int sectors;
-       struct imsm_super *sb;
-       struct intel_super *super = st->sb;
-       unsigned int sector_size = super->sector_size;
-       unsigned int written = 0;
-
-       if (posix_memalign(&buf, MAX_SECTOR_SIZE, MAX_SECTOR_SIZE) != 0)
-               return 1;
-
-       if (!get_dev_size(from, NULL, &dsize))
-               goto err;
-
-       if (lseek64(from, dsize-(2*sector_size), 0) < 0)
-               goto err;
-       if ((unsigned int)read(from, buf, sector_size) != sector_size)
-               goto err;
-       sb = buf;
-       if (strncmp((char*)sb->sig, MPB_SIGNATURE, MPB_SIG_LEN) != 0)
-               goto err;
-
-       sectors = mpb_sectors(sb, sector_size) + 2;
-       offset = dsize - sectors * sector_size;
-       if (lseek64(from, offset, 0) < 0 ||
-           lseek64(to, offset, 0) < 0)
-               goto err;
-       while (written < sectors * sector_size) {
-               int n = sectors*sector_size - written;
-               if (n > 4096)
-                       n = 4096;
-               if (read(from, buf, n) != n)
-                       goto err;
-               if (write(to, buf, n) != n)
-                       goto err;
-               written += n;
-       }
-       free(buf);
-       return 0;
-err:
-       free(buf);
-       return 1;
+       printf("MD_CREATION_TIME=%llu\n", __le64_to_cpu(mpb->creation_time));
 }
 
 static void detail_super_imsm(struct supertype *st, char *homehost,
@@ -2195,7 +2323,7 @@ static void detail_super_imsm(struct supertype *st, char *homehost,
                super->current_vol = strtoul(subarray, NULL, 10);
 
        getinfo_super_imsm(st, &info, NULL);
-       fname_from_uuid(st, &info, nbuf, ':');
+       fname_from_uuid(&info, nbuf);
        printf("\n              UUID : %s\n", nbuf + 5);
 
        super->current_vol = temp_vol;
@@ -2212,7 +2340,7 @@ static void brief_detail_super_imsm(struct supertype *st, char *subarray)
                super->current_vol = strtoul(subarray, NULL, 10);
 
        getinfo_super_imsm(st, &info, NULL);
-       fname_from_uuid(st, &info, nbuf, ':');
+       fname_from_uuid(&info, nbuf);
        printf(" UUID=%s", nbuf + 5);
 
        super->current_vol = temp_vol;
@@ -2222,12 +2350,41 @@ static int imsm_read_serial(int fd, char *devname, __u8 *serial,
                            size_t serial_buf_len);
 static void fd2devname(int fd, char *name);
 
-static int ahci_enumerate_ports(const char *hba_path, int port_count, int host_base, int verbose)
+void print_encryption_information(int disk_fd, enum sys_dev_type hba_type)
+{
+       struct encryption_information information = {0};
+       mdadm_status_t status = MDADM_STATUS_SUCCESS;
+       const char *indent = "                  ";
+
+       switch (hba_type) {
+       case SYS_DEV_VMD:
+       case SYS_DEV_NVME:
+               status = get_nvme_opal_encryption_information(disk_fd, &information, 1);
+               break;
+       case SYS_DEV_SATA:
+       case SYS_DEV_SATA_VMD:
+               status = get_ata_encryption_information(disk_fd, &information, 1);
+               break;
+       default:
+               return;
+       }
+
+       if (status) {
+               pr_err("Failed to get drive encryption information.\n");
+               return;
+       }
+
+       printf("%sEncryption(Ability|Status): %s|%s\n", indent,
+              get_encryption_ability_string(information.ability),
+              get_encryption_status_string(information.status));
+}
+
+static int ahci_enumerate_ports(struct sys_dev *hba, int port_count, int host_base, int verbose)
 {
        /* dump an unsorted list of devices attached to AHCI Intel storage
         * controller, as well as non-connected ports
         */
-       int hba_len = strlen(hba_path) + 1;
+       int hba_len = strlen(hba->path) + 1;
        struct dirent *ent;
        DIR *dir;
        char *path = NULL;
@@ -2253,36 +2410,31 @@ static int ahci_enumerate_ports(const char *hba_path, int port_count, int host_b
                char vendor[64];
                char buf[1024];
                int major, minor;
-               char *device;
+               char device[PATH_MAX];
                char *c;
                int port;
                int type;
 
                if (sscanf(ent->d_name, "%d:%d", &major, &minor) != 2)
                        continue;
-               path = devt_to_devpath(makedev(major, minor));
+               path = devt_to_devpath(makedev(major, minor), 1, NULL);
                if (!path)
                        continue;
-               if (!path_attached_to_hba(path, hba_path)) {
+               if (!path_attached_to_hba(path, hba->path)) {
                        free(path);
                        path = NULL;
                        continue;
                }
 
-               /* retrieve the scsi device type */
-               if (asprintf(&device, "/sys/dev/block/%d:%d/device/xxxxxxx", major, minor) < 0) {
+               /* retrieve the scsi device */
+               if (!devt_to_devpath(makedev(major, minor), 1, device)) {
                        if (verbose > 0)
-                               pr_err("failed to allocate 'device'\n");
+                               pr_err("failed to get device\n");
                        err = 2;
                        break;
                }
-               sprintf(device, "/sys/dev/block/%d:%d/device/type", major, minor);
-               if (load_sys(device, buf, sizeof(buf)) != 0) {
-                       if (verbose > 0)
-                               pr_err("failed to read device type for %s\n",
-                                       path);
+               if (devpath_to_char(device, "type", buf, sizeof(buf), 0)) {
                        err = 2;
-                       free(device);
                        break;
                }
                type = strtoul(buf, NULL, 10);
@@ -2291,8 +2443,9 @@ static int ahci_enumerate_ports(const char *hba_path, int port_count, int host_b
                if (!(type == 0 || type == 7 || type == 14)) {
                        vendor[0] = '\0';
                        model[0] = '\0';
-                       sprintf(device, "/sys/dev/block/%d:%d/device/vendor", major, minor);
-                       if (load_sys(device, buf, sizeof(buf)) == 0) {
+
+                       if (devpath_to_char(device, "vendor", buf,
+                                           sizeof(buf), 0) == 0) {
                                strncpy(vendor, buf, sizeof(vendor));
                                vendor[sizeof(vendor) - 1] = '\0';
                                c = (char *) &vendor[sizeof(vendor) - 1];
@@ -2300,8 +2453,9 @@ static int ahci_enumerate_ports(const char *hba_path, int port_count, int host_b
                                        *c-- = '\0';
 
                        }
-                       sprintf(device, "/sys/dev/block/%d:%d/device/model", major, minor);
-                       if (load_sys(device, buf, sizeof(buf)) == 0) {
+
+                       if (devpath_to_char(device, "model", buf,
+                                           sizeof(buf), 0) == 0) {
                                strncpy(model, buf, sizeof(model));
                                model[sizeof(model) - 1] = '\0';
                                c = (char *) &model[sizeof(model) - 1];
@@ -2326,7 +2480,6 @@ static int ahci_enumerate_ports(const char *hba_path, int port_count, int host_b
                                }
                } else
                        buf[0] = '\0';
-               free(device);
 
                /* chop device path to 'host%d' and calculate the port number */
                c = strchr(&path[hba_len], '/');
@@ -2360,7 +2513,7 @@ static int ahci_enumerate_ports(const char *hba_path, int port_count, int host_b
                }
 
                fd = dev_open(ent->d_name, O_RDONLY);
-               if (fd < 0)
+               if (!is_fd_valid(fd))
                        printf("          Port%d : - disk info unavailable -\n", port);
                else {
                        fd2devname(fd, buf);
@@ -2370,6 +2523,8 @@ static int ahci_enumerate_ports(const char *hba_path, int port_count, int host_b
                                printf(" (%s)\n", buf);
                        else
                                printf(" ()\n");
+
+                       print_encryption_information(fd, hba->type);
                        close(fd);
                }
                free(path);
@@ -2392,43 +2547,52 @@ static int ahci_enumerate_ports(const char *hba_path, int port_count, int host_b
 
 static int print_nvme_info(struct sys_dev *hba)
 {
-       char buf[1024];
        struct dirent *ent;
        DIR *dir;
-       char *rp;
-       int fd;
 
        dir = opendir("/sys/block/");
        if (!dir)
                return 1;
 
        for (ent = readdir(dir); ent; ent = readdir(dir)) {
-               if (strstr(ent->d_name, "nvme")) {
-                       sprintf(buf, "/sys/block/%s", ent->d_name);
-                       rp = realpath(buf, NULL);
-                       if (!rp)
-                               continue;
-                       if (path_attached_to_hba(rp, hba->path)) {
-                               fd = open_dev(ent->d_name);
-                               if (fd < 0) {
-                                       free(rp);
-                                       continue;
-                               }
+               char ns_path[PATH_MAX];
+               char cntrl_path[PATH_MAX];
+               char buf[PATH_MAX];
+               int fd = -1;
 
-                               fd2devname(fd, buf);
-                               if (hba->type == SYS_DEV_VMD)
-                                       printf(" NVMe under VMD : %s", buf);
-                               else if (hba->type == SYS_DEV_NVME)
-                                       printf("    NVMe Device : %s", buf);
-                               if (!imsm_read_serial(fd, NULL, (__u8 *)buf,
-                                                     sizeof(buf)))
-                                       printf(" (%s)\n", buf);
-                               else
-                                       printf("()\n");
-                               close(fd);
-                       }
-                       free(rp);
-               }
+               if (!strstr(ent->d_name, "nvme"))
+                       goto skip;
+
+               fd = open_dev(ent->d_name);
+               if (!is_fd_valid(fd))
+                       goto skip;
+
+               if (!diskfd_to_devpath(fd, 0, ns_path) ||
+                   !diskfd_to_devpath(fd, 1, cntrl_path))
+                       goto skip;
+
+               if (!path_attached_to_hba(cntrl_path, hba->path))
+                       goto skip;
+
+               if (!imsm_is_nvme_namespace_supported(fd, 0))
+                       goto skip;
+
+               fd2devname(fd, buf);
+               if (hba->type == SYS_DEV_VMD)
+                       printf(" NVMe under VMD : %s", buf);
+               else if (hba->type == SYS_DEV_NVME)
+                       printf("    NVMe Device : %s", buf);
+
+               if (!imsm_read_serial(fd, NULL, (__u8 *)buf,
+                                     sizeof(buf)))
+                       printf(" (%s)\n", buf);
+               else
+                       printf("()\n");
+
+               print_encryption_information(fd, hba->type);
+
+skip:
+               close_fd(&fd);
        }
 
        closedir(dir);
@@ -2448,6 +2612,8 @@ static void print_found_intel_controllers(struct sys_dev *elem)
 
                if (elem->type == SYS_DEV_VMD)
                        fprintf(stderr, "VMD domain");
+               else if (elem->type == SYS_DEV_SATA_VMD)
+                       fprintf(stderr, "SATA VMD domain");
                else
                        fprintf(stderr, "RAID controller");
 
@@ -2496,9 +2662,14 @@ static void print_imsm_capability(const struct imsm_orom *orom)
        else
                printf("Rapid Storage Technology%s\n",
                        imsm_orom_is_enterprise(orom) ? " enterprise" : "");
-       if (orom->major_ver || orom->minor_ver || orom->hotfix_ver || orom->build)
-               printf("        Version : %d.%d.%d.%d\n", orom->major_ver,
-                               orom->minor_ver, orom->hotfix_ver, orom->build);
+       if (orom->major_ver || orom->minor_ver || orom->hotfix_ver || orom->build) {
+               if (imsm_orom_is_vmd_without_efi(orom))
+                       printf("        Version : %d.%d\n", orom->major_ver,
+                              orom->minor_ver);
+               else
+                       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" : "",
@@ -2588,7 +2759,7 @@ static int detail_platform_imsm(int verbose, int enumerate_only, char *controlle
        int result=1;
 
        if (enumerate_only) {
-               if (check_env("IMSM_NO_PLATFORM"))
+               if (check_no_platform())
                        return 0;
                list = find_intel_devices();
                if (!list)
@@ -2618,8 +2789,9 @@ static int detail_platform_imsm(int verbose, int enumerate_only, char *controlle
                if (!find_imsm_capability(hba)) {
                        char buf[PATH_MAX];
                        pr_err("imsm capabilities not found for controller: %s (type %s)\n",
-                                 hba->type == SYS_DEV_VMD ? vmd_domain_to_controller(hba, buf) : hba->path,
-                                 get_sys_dev_type(hba->type));
+                                 hba->type == SYS_DEV_VMD || hba->type == SYS_DEV_SATA_VMD ?
+                                 vmd_domain_to_controller(hba, buf) :
+                                 hba->path, get_sys_dev_type(hba->type));
                        continue;
                }
                result = 0;
@@ -2672,11 +2844,12 @@ static int detail_platform_imsm(int verbose, int enumerate_only, char *controlle
 
                        printf(" I/O Controller : %s (%s)\n",
                                hba->path, get_sys_dev_type(hba->type));
-                       if (hba->type == SYS_DEV_SATA) {
+                       if (hba->type == SYS_DEV_SATA || hba->type == SYS_DEV_SATA_VMD) {
                                host_base = ahci_get_port_count(hba->path, &port_count);
-                               if (ahci_enumerate_ports(hba->path, port_count, host_base, verbose)) {
+                               if (ahci_enumerate_ports(hba, port_count, host_base, verbose)) {
                                        if (verbose > 0)
-                                               pr_err("failed to enumerate ports on SATA controller at %s.\n", hba->pci_id);
+                                               pr_err("failed to enumerate ports on %s controller at %s.\n",
+                                                       get_sys_dev_type(hba->type), hba->pci_id);
                                        result |= 2;
                                }
                        }
@@ -2706,7 +2879,8 @@ static int export_detail_platform_imsm(int verbose, char *controller_path)
                if (!find_imsm_capability(hba) && verbose > 0) {
                        char buf[PATH_MAX];
                        pr_err("IMSM_DETAIL_PLATFORM_ERROR=NO_IMSM_CAPABLE_DEVICE_UNDER_%s\n",
-                       hba->type == SYS_DEV_VMD ? vmd_domain_to_controller(hba, buf) : hba->path);
+                               hba->type == SYS_DEV_VMD || hba->type == SYS_DEV_SATA_VMD ?
+                               vmd_domain_to_controller(hba, buf) : hba->path);
                }
                else
                        result = 0;
@@ -2715,7 +2889,7 @@ static int export_detail_platform_imsm(int verbose, char *controller_path)
        const struct orom_entry *entry;
 
        for (entry = orom_entries; entry; entry = entry->next) {
-               if (entry->type == SYS_DEV_VMD) {
+               if (entry->type == SYS_DEV_VMD || entry->type == SYS_DEV_SATA_VMD) {
                        for (hba = list; hba; hba = hba->next)
                                print_imsm_capability_export(&entry->orom);
                        continue;
@@ -2792,34 +2966,6 @@ static void uuid_from_super_imsm(struct supertype *st, int uuid[4])
        memcpy(uuid, buf, 4*4);
 }
 
-#if 0
-static void
-get_imsm_numerical_version(struct imsm_super *mpb, int *m, int *p)
-{
-       __u8 *v = get_imsm_version(mpb);
-       __u8 *end = mpb->sig + MAX_SIGNATURE_LENGTH;
-       char major[] = { 0, 0, 0 };
-       char minor[] = { 0 ,0, 0 };
-       char patch[] = { 0, 0, 0 };
-       char *ver_parse[] = { major, minor, patch };
-       int i, j;
-
-       i = j = 0;
-       while (*v != '\0' && v < end) {
-               if (*v != '.' && j < 2)
-                       ver_parse[i][j++] = *v;
-               else {
-                       i++;
-                       j = 0;
-               }
-               v++;
-       }
-
-       *m = strtol(minor, NULL, 0);
-       *p = strtol(patch, NULL, 0);
-}
-#endif
-
 static __u32 migr_strip_blocks_resync(struct imsm_dev *dev)
 {
        /* migr_strip_size when repairing or initializing parity */
@@ -2881,26 +3027,6 @@ static __u32 num_stripes_per_unit_rebuild(struct imsm_dev *dev)
                return num_stripes_per_unit_resync(dev);
 }
 
-static __u8 imsm_num_data_members(struct imsm_map *map)
-{
-       /* named 'imsm_' because raid0, raid1 and raid10
-        * counter-intuitively have the same number of data disks
-        */
-       switch (get_imsm_raid_level(map)) {
-       case 0:
-               return map->num_members;
-               break;
-       case 1:
-       case 10:
-               return map->num_members/2;
-       case 5:
-               return map->num_members - 1;
-       default:
-               dprintf("unsupported raid level\n");
-               return 0;
-       }
-}
-
 static unsigned long long calc_component_size(struct imsm_map *map,
                                              struct imsm_dev *dev)
 {
@@ -3099,15 +3225,13 @@ static struct imsm_dev *imsm_get_device_during_migration(
  *             sector of disk)
  * Parameters:
  *     super   : imsm internal array info
- *     info    : general array info
  * Returns:
  *      0 : success
  *     -1 : fail
  *     -2 : no migration in progress
  ******************************************************************************/
-static int load_imsm_migr_rec(struct intel_super *super, struct mdinfo *info)
+static int load_imsm_migr_rec(struct intel_super *super)
 {
-       struct mdinfo *sd;
        struct dl *dl;
        char nm[30];
        int retval = -1;
@@ -3115,6 +3239,7 @@ static int load_imsm_migr_rec(struct intel_super *super, struct mdinfo *info)
        struct imsm_dev *dev;
        struct imsm_map *map;
        int slot = -1;
+       int keep_fd = 1;
 
        /* find map under migration */
        dev = imsm_get_device_during_migration(super);
@@ -3123,44 +3248,41 @@ static int load_imsm_migr_rec(struct intel_super *super, struct mdinfo *info)
        if (dev == NULL)
                return -2;
 
-       if (info) {
-               for (sd = info->devs ; sd ; sd = sd->next) {
-                       /* read only from one of the first two slots */
-                       if ((sd->disk.raid_disk < 0) ||
-                           (sd->disk.raid_disk > 1))
-                               continue;
+       map = get_imsm_map(dev, MAP_0);
+       if (!map)
+               return -1;
+
+       for (dl = super->disks; dl; dl = dl->next) {
+               /* skip spare and failed disks
+                */
+               if (dl->index < 0)
+                       continue;
+               /* read only from one of the first two slots
+                */
+               slot = get_imsm_disk_slot(map, dl->index);
+               if (slot > 1 || slot < 0)
+                       continue;
 
-                       sprintf(nm, "%d:%d", sd->disk.major, sd->disk.minor);
+               if (!is_fd_valid(dl->fd)) {
+                       sprintf(nm, "%d:%d", dl->major, dl->minor);
                        fd = dev_open(nm, O_RDONLY);
-                       if (fd >= 0)
-                               break;
-               }
-       }
-       if (fd < 0) {
-               map = get_imsm_map(dev, MAP_0);
-               for (dl = super->disks; dl; dl = dl->next) {
-                       /* skip spare and failed disks
-                       */
-                       if (dl->index < 0)
-                               continue;
-                       /* read only from one of the first two slots */
-                       if (map)
-                               slot = get_imsm_disk_slot(map, dl->index);
-                       if (map == NULL || slot > 1 || slot < 0)
-                               continue;
-                       sprintf(nm, "%d:%d", dl->major, dl->minor);
-                       fd = dev_open(nm, O_RDONLY);
-                       if (fd >= 0)
+
+                       if (is_fd_valid(fd)) {
+                               keep_fd = 0;
                                break;
+                       }
+               } else {
+                       fd = dl->fd;
+                       break;
                }
        }
-       if (fd < 0)
-               goto out;
-       retval = read_imsm_migr_rec(fd, super);
 
-out:
-       if (fd >= 0)
+       if (!is_fd_valid(fd))
+               return retval;
+       retval = read_imsm_migr_rec(fd, super);
+       if (!keep_fd)
                close(fd);
+
        return retval;
 }
 
@@ -3198,7 +3320,7 @@ static int imsm_create_metadata_checkpoint_update(
        }
        (*u)->type = update_general_migration_checkpoint;
        (*u)->curr_migr_unit = current_migr_unit(super->migr_rec);
-       dprintf("prepared for %u\n", (*u)->curr_migr_unit);
+       dprintf("prepared for %llu\n", (unsigned long long)(*u)->curr_migr_unit);
 
        return update_memory_size;
 }
@@ -3221,8 +3343,6 @@ static int write_imsm_migr_rec(struct supertype *st)
        struct intel_super *super = st->sb;
        unsigned int sector_size = super->sector_size;
        unsigned long long dsize;
-       char nm[30];
-       int fd = -1;
        int retval = -1;
        struct dl *sd;
        int len;
@@ -3255,26 +3375,21 @@ static int write_imsm_migr_rec(struct supertype *st)
                if (map == NULL || slot > 1 || slot < 0)
                        continue;
 
-               sprintf(nm, "%d:%d", sd->major, sd->minor);
-               fd = dev_open(nm, O_RDWR);
-               if (fd < 0)
-                       continue;
-               get_dev_size(fd, NULL, &dsize);
-               if (lseek64(fd, dsize - (MIGR_REC_SECTOR_POSITION*sector_size),
+               get_dev_size(sd->fd, NULL, &dsize);
+               if (lseek64(sd->fd, dsize - (MIGR_REC_SECTOR_POSITION *
+                   sector_size),
                    SEEK_SET) < 0) {
                        pr_err("Cannot seek to anchor block: %s\n",
                               strerror(errno));
                        goto out;
                }
-               if ((unsigned int)write(fd, super->migr_rec_buf,
+               if ((unsigned int)write(sd->fd, super->migr_rec_buf,
                    MIGR_REC_BUF_SECTORS*sector_size) !=
                    MIGR_REC_BUF_SECTORS*sector_size) {
                        pr_err("Cannot write migr record block: %s\n",
                               strerror(errno));
                        goto out;
                }
-               close(fd);
-               fd = -1;
        }
        if (sector_size == 4096)
                convert_from_4k_imsm_migr_rec(super);
@@ -3300,8 +3415,6 @@ static int write_imsm_migr_rec(struct supertype *st)
 
        retval = 0;
  out:
-       if (fd >= 0)
-               close(fd);
        return retval;
 }
 
@@ -3354,6 +3467,53 @@ static unsigned long long imsm_component_size_alignment_check(int level,
        return component_size;
 }
 
+/*******************************************************************************
+ * Function:   get_bitmap_header_sector
+ * Description:        Returns the sector where the bitmap header is placed.
+ * Parameters:
+ *     st              : supertype information
+ *     dev_idx         : index of the device with bitmap
+ *
+ * Returns:
+ *      The sector where the bitmap header is placed
+ ******************************************************************************/
+static unsigned long long get_bitmap_header_sector(struct intel_super *super,
+                                                  int dev_idx)
+{
+       struct imsm_dev *dev = get_imsm_dev(super, dev_idx);
+       struct imsm_map *map = get_imsm_map(dev, MAP_0);
+
+       if (!super->sector_size) {
+               dprintf("sector size is not set\n");
+               return 0;
+       }
+
+       return pba_of_lba0(map) + calc_component_size(map, dev) +
+              (IMSM_BITMAP_HEADER_OFFSET / super->sector_size);
+}
+
+/*******************************************************************************
+ * Function:   get_bitmap_sector
+ * Description:        Returns the sector where the bitmap is placed.
+ * Parameters:
+ *     st              : supertype information
+ *     dev_idx         : index of the device with bitmap
+ *
+ * Returns:
+ *      The sector where the bitmap is placed
+ ******************************************************************************/
+static unsigned long long get_bitmap_sector(struct intel_super *super,
+                                           int dev_idx)
+{
+       if (!super->sector_size) {
+               dprintf("sector size is not set\n");
+               return 0;
+       }
+
+       return get_bitmap_header_sector(super, dev_idx) +
+              (IMSM_BITMAP_HEADER_SIZE / super->sector_size);
+}
+
 static unsigned long long get_ppl_sector(struct intel_super *super, int dev_idx)
 {
        struct imsm_dev *dev = get_imsm_dev(super, dev_idx);
@@ -3394,6 +3554,12 @@ static void getinfo_super_imsm_volume(struct supertype *st, struct mdinfo *info,
        info->recovery_blocked = imsm_reshape_blocks_arrays_changes(st->sb);
 
        if (is_gen_migration(dev)) {
+               /*
+                * device prev_map should be added if it is in the middle
+                * of migration
+                */
+               assert(prev_map);
+
                info->reshape_active = 1;
                info->new_level = get_imsm_raid_level(map);
                info->new_layout = imsm_level_to_layout(info->new_level);
@@ -3474,7 +3640,12 @@ static void getinfo_super_imsm_volume(struct supertype *st, struct mdinfo *info,
        } else if (info->array.level <= 0) {
                info->consistency_policy = CONSISTENCY_POLICY_NONE;
        } else {
-               info->consistency_policy = CONSISTENCY_POLICY_RESYNC;
+               if (dev->rwh_policy == RWH_BITMAP) {
+                       info->bitmap_offset = get_bitmap_sector(super, super->current_vol);
+                       info->consistency_policy = CONSISTENCY_POLICY_BITMAP;
+               } else {
+                       info->consistency_policy = CONSISTENCY_POLICY_RESYNC;
+               }
        }
 
        info->reshape_progress = 0;
@@ -3490,7 +3661,7 @@ static void getinfo_super_imsm_volume(struct supertype *st, struct mdinfo *info,
                case MIGR_INIT: {
                        __u64 blocks_per_unit = blocks_per_migr_unit(super,
                                                                     dev);
-                       __u64 units = __le32_to_cpu(dev->vol.curr_migr_unit);
+                       __u64 units = vol_curr_migr_unit(dev);
 
                        info->resync_start = blocks_per_unit * units;
                        break;
@@ -3499,7 +3670,6 @@ static void getinfo_super_imsm_volume(struct supertype *st, struct mdinfo *info,
                        __u64 blocks_per_unit = blocks_per_migr_unit(super,
                                                                     dev);
                        __u64 units = current_migr_unit(migr_rec);
-                       unsigned long long array_blocks;
                        int used_disks;
 
                        if (__le32_to_cpu(migr_rec->ascending_migr) &&
@@ -3518,12 +3688,8 @@ static void getinfo_super_imsm_volume(struct supertype *st, struct mdinfo *info,
 
                        used_disks = imsm_num_data_members(prev_map);
                        if (used_disks > 0) {
-                               array_blocks = per_dev_array_size(map) *
+                               info->custom_array_size = per_dev_array_size(map) *
                                        used_disks;
-                               info->custom_array_size =
-                                       round_size_to_mb(array_blocks,
-                                                        used_disks);
-
                        }
                }
                case MIGR_VERIFY:
@@ -3692,7 +3858,7 @@ static void getinfo_super_imsm(struct supertype *st, struct mdinfo *info, char *
                 */
                max_enough = max(max_enough, enough);
        }
-       dprintf("enough: %d\n", max_enough);
+
        info->container_enough = max_enough;
 
        if (super->disks) {
@@ -3762,8 +3928,8 @@ struct mdinfo *getinfo_super_disks_imsm(struct supertype *st)
 }
 
 static int update_super_imsm(struct supertype *st, struct mdinfo *info,
-                            char *update, char *devname, int verbose,
-                            int uuid_set, char *homehost)
+                            enum update_opt update, char *devname,
+                            int verbose, int uuid_set, char *homehost)
 {
        /* For 'assemble' and 'force' we need to return non-zero if any
         * change was made.  For others, the return value is ignored.
@@ -3799,7 +3965,8 @@ static int update_super_imsm(struct supertype *st, struct mdinfo *info,
 
        mpb = super->anchor;
 
-       if (strcmp(update, "uuid") == 0) {
+       switch (update) {
+       case UOPT_UUID:
                /* We take this to mean that the family_num should be updated.
                 * However that is much smaller than the uuid so we cannot really
                 * allow an explicit uuid to be given.  And it is hard to reliably
@@ -3823,10 +3990,14 @@ static int update_super_imsm(struct supertype *st, struct mdinfo *info,
                }
                if (rv == 0)
                        mpb->orig_family_num = info->uuid[0];
-       } else if (strcmp(update, "assemble") == 0)
+               break;
+       case UOPT_SPEC_ASSEMBLE:
                rv = 0;
-       else
+               break;
+       default:
                rv = -1;
+               break;
+       }
 
        /* successful update? recompute checksum */
        if (rv == 0)
@@ -3878,14 +4049,12 @@ static void imsm_copy_dev(struct imsm_dev *dest, struct imsm_dev *src)
        memcpy(dest, src, sizeof_imsm_dev(src, 0));
 }
 
-static int compare_super_imsm(struct supertype *st, struct supertype *tst)
+static int compare_super_imsm(struct supertype *st, struct supertype *tst,
+                             int verbose)
 {
-       /*
-        * return:
+       /*  return:
         *  0 same, or first was empty, and second was copied
-        *  1 second had wrong number
-        *  2 wrong uuid
-        *  3 wrong other info
+        *  1 sb are different
         */
        struct intel_super *first = st->sb;
        struct intel_super *sec = tst->sb;
@@ -3895,29 +4064,30 @@ 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 not on Intel hba at all, allow anything.
+        * if not on Intel hba at all, allow anything.
+        * doesn't check HBAs if num_raid_devs is not set, as it means
+        * it is a free floating spare, and all spares regardless of HBA type
+        * will fall into separate container during the assembly
         */
-       if (!check_env("IMSM_NO_PLATFORM") && first->hba && sec->hba) {
+       if (first->hba && sec->hba && first->anchor->num_raid_devs != 0) {
                if (first->hba->type != sec->hba->type) {
-                       fprintf(stderr,
-                               "HBAs of devices do not match %s != %s\n",
-                               get_sys_dev_type(first->hba->type),
-                               get_sys_dev_type(sec->hba->type));
-                       return 3;
+                       if (verbose)
+                               pr_err("HBAs of devices do not match %s != %s\n",
+                                      get_sys_dev_type(first->hba->type),
+                                      get_sys_dev_type(sec->hba->type));
+                       return 1;
                }
                if (first->orom != sec->orom) {
-                       fprintf(stderr,
-                               "HBAs of devices do not match %s != %s\n",
-                               first->hba->pci_id, sec->hba->pci_id);
-                       return 3;
+                       if (verbose)
+                               pr_err("HBAs of devices do not match %s != %s\n",
+                                      first->hba->pci_id, sec->hba->pci_id);
+                       return 1;
                }
        }
 
-       /* if an anchor does not have num_raid_devs set then it is a free
-        * floating spare
-        */
        if (first->anchor->num_raid_devs > 0 &&
            sec->anchor->num_raid_devs > 0) {
                /* Determine if these disks might ever have been
@@ -3929,7 +4099,7 @@ static int compare_super_imsm(struct supertype *st, struct supertype *tst)
 
                if (memcmp(first->anchor->sig, sec->anchor->sig,
                           MAX_SIGNATURE_LENGTH) != 0)
-                       return 3;
+                       return 1;
 
                if (first_family == 0)
                        first_family = first->anchor->family_num;
@@ -3937,76 +4107,35 @@ static int compare_super_imsm(struct supertype *st, struct supertype *tst)
                        sec_family = sec->anchor->family_num;
 
                if (first_family != sec_family)
-                       return 3;
+                       return 1;
 
        }
 
-       /* if 'first' is a spare promote it to a populated mpb with sec's
-        * family number
-        */
-       if (first->anchor->num_raid_devs == 0 &&
-           sec->anchor->num_raid_devs > 0) {
-               int i;
-               struct intel_dev *dv;
-               struct imsm_dev *dev;
-
-               /* we need to copy raid device info from sec if an allocation
-                * fails here we don't associate the spare
-                */
-               for (i = 0; i < sec->anchor->num_raid_devs; i++) {
-                       dv = xmalloc(sizeof(*dv));
-                       dev = xmalloc(sizeof_imsm_dev(get_imsm_dev(sec, i), 1));
-                       dv->dev = dev;
-                       dv->index = i;
-                       dv->next = first->devlist;
-                       first->devlist = dv;
-               }
-               if (i < sec->anchor->num_raid_devs) {
-                       /* allocation failure */
-                       free_devlist(first);
-                       pr_err("imsm: failed to associate spare\n");
-                       return 3;
-               }
-               first->anchor->num_raid_devs = sec->anchor->num_raid_devs;
-               first->anchor->orig_family_num = sec->anchor->orig_family_num;
-               first->anchor->family_num = sec->anchor->family_num;
-               memcpy(first->anchor->sig, sec->anchor->sig, MAX_SIGNATURE_LENGTH);
-               for (i = 0; i < sec->anchor->num_raid_devs; i++)
-                       imsm_copy_dev(get_imsm_dev(first, i), get_imsm_dev(sec, i));
-       }
+       /* if an anchor does not have num_raid_devs set then it is a free
+       * floating spare. don't assosiate spare with any array, as during assembly
+       * spares shall fall into separate container, from which they can be moved
+       * when necessary
+       */
+       if (first->anchor->num_raid_devs ^ sec->anchor->num_raid_devs)
+               return 1;
 
        return 0;
 }
 
 static void fd2devname(int fd, char *name)
 {
-       struct stat st;
-       char path[256];
-       char dname[PATH_MAX];
        char *nm;
-       int rv;
 
-       name[0] = '\0';
-       if (fstat(fd, &st) != 0)
+       nm = fd2kname(fd);
+       if (!nm)
                return;
-       sprintf(path, "/sys/dev/block/%d:%d",
-               major(st.st_rdev), minor(st.st_rdev));
 
-       rv = readlink(path, dname, sizeof(dname)-1);
-       if (rv <= 0)
-               return;
-
-       dname[rv] = '\0';
-       nm = strrchr(dname, '/');
-       if (nm) {
-               nm++;
-               snprintf(name, MAX_RAID_SERIAL_LEN, "/dev/%s", nm);
-       }
+       snprintf(name, MAX_RAID_SERIAL_LEN, "/dev/%s", nm);
 }
 
 static int nvme_get_serial(int fd, void *buf, size_t buf_len)
 {
-       char path[60];
+       char path[PATH_MAX];
        char *name = fd2kname(fd);
 
        if (!name)
@@ -4015,9 +4144,10 @@ static int nvme_get_serial(int fd, void *buf, size_t buf_len)
        if (strncmp(name, "nvme", 4) != 0)
                return 1;
 
-       snprintf(path, sizeof(path) - 1, "/sys/block/%s/device/serial", name);
+       if (!diskfd_to_devpath(fd, 1, path))
+               return 1;
 
-       return load_sys(path, buf, buf_len);
+       return devpath_to_char(path, "serial", buf, buf_len, 0);
 }
 
 extern int scsi_get_serial(int fd, void *buf, size_t buf_len);
@@ -4034,17 +4164,17 @@ static int imsm_read_serial(int fd, char *devname,
 
        memset(buf, 0, sizeof(buf));
 
+       if (check_env("IMSM_DEVNAME_AS_SERIAL")) {
+               memset(serial, 0, serial_buf_len);
+               fd2devname(fd, (char *) serial);
+               return 0;
+       }
+
        rv = nvme_get_serial(fd, buf, sizeof(buf));
 
        if (rv)
                rv = scsi_get_serial(fd, buf, sizeof(buf));
 
-       if (rv && check_env("IMSM_DEVNAME_AS_SERIAL")) {
-               memset(serial, 0, MAX_RAID_SERIAL_LEN);
-               fd2devname(fd, (char *) serial);
-               return 0;
-       }
-
        if (rv != 0) {
                if (devname)
                        pr_err("Failed to retrieve serial for %s\n",
@@ -4194,7 +4324,7 @@ static void migrate(struct imsm_dev *dev, struct intel_super *super,
 
        dev->vol.migr_state = 1;
        set_migr_type(dev, migr_type);
-       dev->vol.curr_migr_unit = 0;
+       set_vol_curr_migr_unit(dev, 0);
        dest = get_imsm_map(dev, MAP_1);
 
        /* duplicate and then set the target end state in map[0] */
@@ -4229,7 +4359,7 @@ static void end_migration(struct imsm_dev *dev, struct intel_super *super,
         *
         * FIXME add support for raid-level-migration
         */
-       if (map_state != map->map_state && (is_gen_migration(dev) == 0) &&
+       if (map_state != map->map_state && (is_gen_migration(dev) == false) &&
            prev->map_state != IMSM_T_STATE_UNINITIALIZED) {
                /* when final map state is other than expected
                 * merge maps (not for migration)
@@ -4254,7 +4384,7 @@ static void end_migration(struct imsm_dev *dev, struct intel_super *super,
 
        dev->vol.migr_state = 0;
        set_migr_type(dev, 0);
-       dev->vol.curr_migr_unit = 0;
+       set_vol_curr_migr_unit(dev, 0);
        map->map_state = map_state;
 }
 
@@ -4332,8 +4462,7 @@ int check_mpb_migr_compatibility(struct intel_super *super)
        for (i = 0; i < super->anchor->num_raid_devs; i++) {
                struct imsm_dev *dev_iter = __get_imsm_dev(super->anchor, i);
 
-               if (dev_iter &&
-                   dev_iter->vol.migr_state == 1 &&
+               if (dev_iter->vol.migr_state == 1 &&
                    dev_iter->vol.migr_type == MIGR_GEN_MIGR) {
                        /* This device is migrating */
                        map0 = get_imsm_map(dev_iter, MAP_0);
@@ -4421,6 +4550,7 @@ static int load_imsm_mpb(int fd, struct intel_super *super, char *devname)
            MIGR_REC_BUF_SECTORS*MAX_SECTOR_SIZE) != 0) {
                pr_err("could not allocate migr_rec buffer\n");
                free(super->buf);
+               super->buf = NULL;
                return 2;
        }
        super->clean_migration_record_by_mdmon = 0;
@@ -4482,8 +4612,6 @@ static void clear_hi(struct intel_super *super)
        }
        for (i = 0; i < mpb->num_raid_devs; ++i) {
                struct imsm_dev *dev = get_imsm_dev(super, i);
-               if (!dev)
-                       return;
                for (n = 0; n < 2; ++n) {
                        struct imsm_map *map = get_imsm_map(dev, n);
                        if (!map)
@@ -4516,10 +4644,10 @@ load_and_parse_mpb(int fd, struct intel_super *super, char *devname, int keep_fd
        return err;
 }
 
-static void __free_imsm_disk(struct dl *d)
+static void __free_imsm_disk(struct dl *d, int do_close)
 {
-       if (d->fd >= 0)
-               close(d->fd);
+       if (do_close)
+               close_fd(&d->fd);
        if (d->devname)
                free(d->devname);
        if (d->e)
@@ -4535,17 +4663,17 @@ static void free_imsm_disks(struct intel_super *super)
        while (super->disks) {
                d = super->disks;
                super->disks = d->next;
-               __free_imsm_disk(d);
+               __free_imsm_disk(d, 1);
        }
        while (super->disk_mgmt_list) {
                d = super->disk_mgmt_list;
                super->disk_mgmt_list = d->next;
-               __free_imsm_disk(d);
+               __free_imsm_disk(d, 1);
        }
        while (super->missing) {
                d = super->missing;
                super->missing = d->next;
-               __free_imsm_disk(d);
+               __free_imsm_disk(d, 1);
        }
 
 }
@@ -4624,12 +4752,12 @@ static int find_intel_hba_capability(int fd, struct intel_super *super, char *de
        struct sys_dev *hba_name;
        int rv = 0;
 
-       if (fd >= 0 && test_partition(fd)) {
+       if (is_fd_valid(fd) && test_partition(fd)) {
                pr_err("imsm: %s is a partition, cannot be used in IMSM\n",
                       devname);
                return 1;
        }
-       if (fd < 0 || check_env("IMSM_NO_PLATFORM")) {
+       if (!is_fd_valid(fd) || check_no_platform()) {
                super->orom = NULL;
                super->hba = NULL;
                return 0;
@@ -4650,10 +4778,12 @@ static int find_intel_hba_capability(int fd, struct intel_super *super, char *de
                                "    but the container is assigned to Intel(R) %s %s (",
                                devname,
                                get_sys_dev_type(hba_name->type),
-                               hba_name->type == SYS_DEV_VMD ? "domain" : "RAID controller",
+                               hba_name->type == SYS_DEV_VMD || hba_name->type == SYS_DEV_SATA_VMD ?
+                                       "domain" : "RAID controller",
                                hba_name->pci_id ? : "Err!",
                                get_sys_dev_type(super->hba->type),
-                               hba->type == SYS_DEV_VMD ? "domain" : "RAID controller");
+                               hba->type == SYS_DEV_VMD || hba_name->type == SYS_DEV_SATA_VMD ?
+                                       "domain" : "RAID controller");
 
                        while (hba) {
                                fprintf(stderr, "%s", hba->pci_id ? : "Err!");
@@ -5038,7 +5168,7 @@ static int load_super_imsm_all(struct supertype *st, int fd, void **sbp,
        int err = 0;
        int i = 0;
 
-       if (fd >= 0)
+       if (is_fd_valid(fd))
                /* 'fd' is an opened container */
                err = get_sra_super_block(fd, &super_list, devname, &i, keep_fd);
        else
@@ -5060,7 +5190,7 @@ static int load_super_imsm_all(struct supertype *st, int fd, void **sbp,
        }
 
        /* load migration record */
-       err = load_imsm_migr_rec(super, NULL);
+       err = load_imsm_migr_rec(super);
        if (err == -1) {
                /* migration is in progress,
                 * but migr_rec cannot be loaded,
@@ -5095,7 +5225,7 @@ static int load_super_imsm_all(struct supertype *st, int fd, void **sbp,
                return err;
 
        *sbp = super;
-       if (fd >= 0)
+       if (is_fd_valid(fd))
                strcpy(st->container_devnm, fd2devnm(fd));
        else
                st->container_devnm[0] = 0;
@@ -5121,7 +5251,7 @@ get_devlist_super_block(struct md_list *devlist, struct intel_super **super_list
                if (tmpdev->container == 1) {
                        int lmax = 0;
                        int fd = dev_open(tmpdev->devname, O_RDONLY|O_EXCL);
-                       if (fd < 0) {
+                       if (!is_fd_valid(fd)) {
                                pr_err("cannot open device %s: %s\n",
                                        tmpdev->devname, strerror(errno));
                                err = 8;
@@ -5173,19 +5303,22 @@ static int get_super_block(struct intel_super **super_list, char *devnm, char *d
 
        sprintf(nm, "%d:%d", major, minor);
        dfd = dev_open(nm, O_RDWR);
-       if (dfd < 0) {
+       if (!is_fd_valid(dfd)) {
                err = 2;
                goto error;
        }
 
-       get_dev_sector_size(dfd, NULL, &s->sector_size);
+       if (!get_dev_sector_size(dfd, NULL, &s->sector_size)) {
+               err = 2;
+               goto error;
+       }
        find_intel_hba_capability(dfd, s, devname);
        err = load_and_parse_mpb(dfd, s, NULL, keep_fd);
 
        /* retry the load if we might have raced against mdmon */
        if (err == 3 && devnm && mdmon_running(devnm))
                for (retry = 0; retry < 3; retry++) {
-                       usleep(3000);
+                       sleep_for(0, MSEC_TO_NSEC(3), true);
                        err = load_and_parse_mpb(dfd, s, NULL, keep_fd);
                        if (err != 3)
                                break;
@@ -5197,11 +5330,10 @@ static int get_super_block(struct intel_super **super_list, char *devnm, char *d
        } else {
                if (s)
                        free_imsm(s);
-               if (dfd >= 0)
-                       close(dfd);
+               close_fd(&dfd);
        }
-       if (dfd >= 0 && !keep_fd)
-               close(dfd);
+       if (!keep_fd)
+               close_fd(&dfd);
        return err;
 
 }
@@ -5257,9 +5389,13 @@ static int load_super_imsm(struct supertype *st, int fd, char *devname)
        free_super_imsm(st);
 
        super = alloc_super();
-       get_dev_sector_size(fd, NULL, &super->sector_size);
        if (!super)
                return 1;
+
+       if (!get_dev_sector_size(fd, NULL, &super->sector_size)) {
+               free_imsm(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.
@@ -5284,7 +5420,7 @@ static int load_super_imsm(struct supertype *st, int fd, char *devname)
 
                if (mdstat && mdmon_running(mdstat->devnm) && getpid() != mdmon_pid(mdstat->devnm)) {
                        for (retry = 0; retry < 3; retry++) {
-                               usleep(3000);
+                               sleep_for(0, MSEC_TO_NSEC(3), true);
                                rv = load_and_parse_mpb(fd, super, devname, 0);
                                if (rv != 3)
                                        break;
@@ -5309,7 +5445,7 @@ static int load_super_imsm(struct supertype *st, int fd, char *devname)
        }
 
        /* load migration record */
-       if (load_imsm_migr_rec(super, NULL) == 0) {
+       if (load_imsm_migr_rec(super) == 0) {
                /* Check for unsupported migration features */
                if (check_mpb_migr_compatibility(super) != 0) {
                        pr_err("Unsupported migration detected");
@@ -5387,40 +5523,37 @@ static void imsm_update_version_info(struct intel_super *super)
        }
 }
 
-static int check_name(struct intel_super *super, char *name, int quiet)
+/**
+ * imsm_check_name() - check imsm naming criteria.
+ * @super: &intel_super pointer, not NULL.
+ * @name: name to check.
+ * @verbose: verbose level.
+ *
+ * Name must be no longer than &MAX_RAID_SERIAL_LEN and must be unique across volumes.
+ *
+ * Returns: &true if @name matches, &false otherwise.
+ */
+static bool imsm_is_name_allowed(struct intel_super *super, const char * const name,
+                                const int verbose)
 {
        struct imsm_super *mpb = super->anchor;
-       char *reason = NULL;
-       char *start = name;
-       size_t len = strlen(name);
        int i;
 
-       if (len > 0) {
-               while (isspace(start[len - 1]))
-                       start[--len] = 0;
-               while (*start && isspace(*start))
-                       ++start, --len;
-               memmove(name, start, len + 1);
+       if (is_string_lq(name, MAX_RAID_SERIAL_LEN + 1) == false) {
+               pr_vrb("imsm: Name \"%s\" is too long\n", name);
+               return false;
        }
 
-       if (len > MAX_RAID_SERIAL_LEN)
-               reason = "must be 16 characters or less";
-       else if (len == 0)
-               reason = "must be a non-empty string";
-
        for (i = 0; i < mpb->num_raid_devs; i++) {
                struct imsm_dev *dev = get_imsm_dev(super, i);
 
                if (strncmp((char *) dev->volume, name, MAX_RAID_SERIAL_LEN) == 0) {
-                       reason = "already exists";
-                       break;
+                       pr_vrb("imsm: Name \"%s\" already exists\n", name);
+                       return false;
                }
        }
 
-       if (reason && !quiet)
-               pr_err("imsm volume name %s\n", reason);
-
-       return !reason;
+       return true;
 }
 
 static int init_super_imsm_volume(struct supertype *st, mdu_array_info_t *info,
@@ -5443,7 +5576,6 @@ static int init_super_imsm_volume(struct supertype *st, mdu_array_info_t *info,
        int namelen;
        unsigned long long array_blocks;
        size_t size_old, size_new;
-       unsigned long long num_data_stripes;
        unsigned int data_disks;
        unsigned long long size_per_member;
 
@@ -5516,8 +5648,9 @@ static int init_super_imsm_volume(struct supertype *st, mdu_array_info_t *info,
                }
        }
 
-       if (!check_name(super, name, 0))
+       if (imsm_is_name_allowed(super, name, 1) == false)
                return 0;
+
        dv = xmalloc(sizeof(*dv));
        dev = xcalloc(1, sizeof(*dev) + sizeof(__u32) * (info->raid_disks - 1));
        /*
@@ -5540,7 +5673,7 @@ static int init_super_imsm_volume(struct supertype *st, mdu_array_info_t *info,
        vol->migr_state = 0;
        set_migr_type(dev, MIGR_INIT);
        vol->dirty = !info->state;
-       vol->curr_migr_unit = 0;
+       set_vol_curr_migr_unit(dev, 0);
        map = get_imsm_map(dev, MAP_0);
        set_pba_of_lba0(map, super->create_offset);
        map->blocks_per_strip = __cpu_to_le16(info_to_blocks_per_strip(info));
@@ -5561,18 +5694,9 @@ static int init_super_imsm_volume(struct supertype *st, mdu_array_info_t *info,
        }
 
        map->raid_level = info->level;
-       if (info->level == 10) {
+       if (info->level == 10)
                map->raid_level = 1;
-               map->num_domains = info->raid_disks / 2;
-       } else if (info->level == 1)
-               map->num_domains = info->raid_disks;
-       else
-               map->num_domains = 1;
-
-       /* info->size is only int so use the 'size' parameter instead */
-       num_data_stripes = size_per_member / info_to_blocks_per_strip(info);
-       num_data_stripes /= map->num_domains;
-       set_num_data_stripes(map, num_data_stripes);
+       set_num_domains(map);
 
        size_per_member += NUM_BLOCKS_DIRTY_STRIPE_REGION;
        set_blocks_per_member(map, info_to_blocks_per_member(info,
@@ -5580,6 +5704,7 @@ static int init_super_imsm_volume(struct supertype *st, mdu_array_info_t *info,
                                                             BLOCKS_PER_KB));
 
        map->num_members = info->raid_disks;
+       update_num_data_stripes(map, array_blocks);
        for (i = 0; i < map->num_members; i++) {
                /* initialized in add_to_super */
                set_imsm_ord_tbl_ent(map, i, IMSM_ORD_REBUILD);
@@ -5596,7 +5721,7 @@ static int init_super_imsm_volume(struct supertype *st, mdu_array_info_t *info,
                free(dev);
                free(dv);
                pr_err("imsm does not support consistency policy %s\n",
-                      map_num(consistency_policies, s->consistency_policy));
+                      map_num_s(consistency_policies, s->consistency_policy));
                return 0;
        }
 
@@ -5683,7 +5808,7 @@ static int drive_validate_sector_size(struct intel_super *super, struct dl *dl)
 {
        unsigned int member_sector_size;
 
-       if (dl->fd < 0) {
+       if (!is_fd_valid(dl->fd)) {
                pr_err("Invalid file descriptor for %s\n", dl->devname);
                return 0;
        }
@@ -5705,6 +5830,10 @@ static int add_to_super_imsm_volume(struct supertype *st, mdu_disk_info_t *dk,
        struct imsm_map *map;
        struct dl *dl, *df;
        int slot;
+       int autolayout = 0;
+
+       if (!is_fd_valid(fd))
+               autolayout = 1;
 
        dev = get_imsm_dev(super, super->current_vol);
        map = get_imsm_map(dev, MAP_0);
@@ -5715,25 +5844,32 @@ static int add_to_super_imsm_volume(struct supertype *st, mdu_disk_info_t *dk,
                return 1;
        }
 
-       if (fd == -1) {
-               /* we're doing autolayout so grab the pre-marked (in
-                * validate_geometry) raid_disk
-                */
-               for (dl = super->disks; dl; dl = dl->next)
+       for (dl = super->disks; dl ; dl = dl->next) {
+               if (autolayout) {
                        if (dl->raiddisk == dk->raid_disk)
                                break;
-       } else {
-               for (dl = super->disks; dl ; dl = dl->next)
-                       if (dl->major == dk->major &&
-                           dl->minor == dk->minor)
-                               break;
+               } else if (dl->major == dk->major && dl->minor == dk->minor)
+                       break;
        }
 
        if (!dl) {
-               pr_err("%s is not a member of the same container\n", devname);
+               if (!autolayout)
+                       pr_err("%s is not a member of the same container.\n",
+                              devname);
                return 1;
        }
 
+       if (!autolayout && super->current_vol > 0) {
+               int _slot = get_disk_slot_in_dev(super, 0, dl->index);
+
+               if (_slot != dk->raid_disk) {
+                       pr_err("Member %s is in %d slot for the first volume, but is in %d slot for a new volume.\n",
+                              dl->devname, _slot, dk->raid_disk);
+                       pr_err("Raid members are in different order than for the first volume, aborting.\n");
+                       return 1;
+               }
+       }
+
        if (mpb->num_disks == 0)
                if (!get_dev_sector_size(dl->fd, dl->devname,
                                         &super->sector_size))
@@ -5807,7 +5943,7 @@ static int add_to_super_imsm_volume(struct supertype *st, mdu_disk_info_t *dk,
                struct imsm_dev *_dev = __get_imsm_dev(mpb, 0);
 
                _disk = __get_imsm_disk(mpb, dl->index);
-               if (!_dev || !_disk) {
+               if (!_disk) {
                        pr_err("BUG mpb setup error\n");
                        return 1;
                }
@@ -5817,6 +5953,7 @@ static int add_to_super_imsm_volume(struct supertype *st, mdu_disk_info_t *dk,
                sum += __gen_imsm_checksum(mpb);
                mpb->family_num = __cpu_to_le32(sum);
                mpb->orig_family_num = mpb->family_num;
+               mpb->creation_time = __cpu_to_le64((__u64)time(NULL));
        }
        super->current_disk = dl;
        return 0;
@@ -5854,6 +5991,9 @@ int mark_spare(struct dl *disk)
        return ret_val;
 }
 
+
+static int write_super_imsm_spare(struct intel_super *super, struct dl *d);
+
 static int add_to_super_imsm(struct supertype *st, mdu_disk_info_t *dk,
                             int fd, char *devname,
                             unsigned long long data_offset)
@@ -5893,29 +6033,30 @@ static int add_to_super_imsm(struct supertype *st, mdu_disk_info_t *dk,
        rv = imsm_read_serial(fd, devname, dd->serial, MAX_RAID_SERIAL_LEN);
        if (rv) {
                pr_err("failed to retrieve scsi serial, aborting\n");
-               if (dd->devname)
-                       free(dd->devname);
-               free(dd);
+               __free_imsm_disk(dd, 0);
                abort();
        }
+
        if (super->hba && ((super->hba->type == SYS_DEV_NVME) ||
           (super->hba->type == SYS_DEV_VMD))) {
                int i;
-               char *devpath = diskfd_to_devpath(fd);
-               char controller_path[PATH_MAX];
-
-               if (!devpath) {
-                       pr_err("failed to get devpath, aborting\n");
-                       if (dd->devname)
-                               free(dd->devname);
-                       free(dd);
+               char cntrl_path[PATH_MAX];
+               char *cntrl_name;
+               char pci_dev_path[PATH_MAX];
+
+               if (!diskfd_to_devpath(fd, 2, pci_dev_path) ||
+                   !diskfd_to_devpath(fd, 1, cntrl_path)) {
+                       pr_err("failed to get dev paths, aborting\n");
+                       __free_imsm_disk(dd, 0);
                        return 1;
                }
 
-               snprintf(controller_path, PATH_MAX-1, "%s/device", devpath);
-               free(devpath);
+               cntrl_name = basename(cntrl_path);
+               if (is_multipath_nvme(fd))
+                       pr_err("%s controller supports Multi-Path I/O, Intel (R) VROC does not support multipathing\n",
+                              cntrl_name);
 
-               if (devpath_to_vendor(controller_path) == 0x8086) {
+               if (devpath_to_vendor(pci_dev_path) == 0x8086) {
                        /*
                         * If Intel's NVMe drive has serial ended with
                         * "-A","-B","-1" or "-2" it means that this is "x8"
@@ -5942,14 +6083,16 @@ static int add_to_super_imsm(struct supertype *st, mdu_disk_info_t *dk,
                    !imsm_orom_has_tpv_support(super->orom)) {
                        pr_err("\tPlatform configuration does not support non-Intel NVMe drives.\n"
                               "\tPlease refer to Intel(R) RSTe/VROC user guide.\n");
-                       free(dd->devname);
-                       free(dd);
+                       __free_imsm_disk(dd, 0);
                        return 1;
                }
        }
 
        get_dev_size(fd, NULL, &size);
-       get_dev_sector_size(fd, NULL, &member_sector_size);
+       if (!get_dev_sector_size(fd, NULL, &member_sector_size)) {
+               __free_imsm_disk(dd, 0);
+               return 1;
+       }
 
        if (super->sector_size == 0) {
                /* this a first device, so sector_size is not set yet */
@@ -5983,9 +6126,13 @@ static int add_to_super_imsm(struct supertype *st, mdu_disk_info_t *dk,
                dd->next = super->disk_mgmt_list;
                super->disk_mgmt_list = dd;
        } else {
+               /* this is called outside of mdmon
+                * write initial spare metadata
+                * mdmon will overwrite it.
+                */
                dd->next = super->disks;
                super->disks = dd;
-               super->updates_pending++;
+               write_super_imsm_spare(super, dd);
        }
 
        return 0;
@@ -6024,15 +6171,15 @@ static union {
        struct imsm_super anchor;
 } spare_record __attribute__ ((aligned(MAX_SECTOR_SIZE)));
 
-/* spare records have their own family number and do not have any defined raid
- * devices
- */
-static int write_super_imsm_spares(struct intel_super *super, int doclose)
+
+static int write_super_imsm_spare(struct intel_super *super, struct dl *d)
 {
        struct imsm_super *mpb = super->anchor;
        struct imsm_super *spare = &spare_record.anchor;
        __u32 sum;
-       struct dl *d;
+
+       if (d->index != -1)
+               return 1;
 
        spare->mpb_size = __cpu_to_le32(sizeof(struct imsm_super));
        spare->generation_num = __cpu_to_le32(1UL);
@@ -6045,32 +6192,43 @@ static int write_super_imsm_spares(struct intel_super *super, int doclose)
        snprintf((char *) spare->sig, MAX_SIGNATURE_LENGTH,
                 MPB_SIGNATURE MPB_VERSION_RAID0);
 
-       for (d = super->disks; d; d = d->next) {
-               if (d->index != -1)
-                       continue;
+       spare->disk[0] = d->disk;
+       if (__le32_to_cpu(d->disk.total_blocks_hi) > 0)
+               spare->attributes |= MPB_ATTRIB_2TB_DISK;
+
+       if (super->sector_size == 4096)
+               convert_to_4k_imsm_disk(&spare->disk[0]);
 
-               spare->disk[0] = d->disk;
-               if (__le32_to_cpu(d->disk.total_blocks_hi) > 0)
-                       spare->attributes |= MPB_ATTRIB_2TB_DISK;
+       sum = __gen_imsm_checksum(spare);
+       spare->family_num = __cpu_to_le32(sum);
+       spare->orig_family_num = 0;
+       sum = __gen_imsm_checksum(spare);
+       spare->check_sum = __cpu_to_le32(sum);
 
-               if (super->sector_size == 4096)
-                       convert_to_4k_imsm_disk(&spare->disk[0]);
+       if (store_imsm_mpb(d->fd, spare)) {
+               pr_err("failed for device %d:%d %s\n",
+                       d->major, d->minor, strerror(errno));
+               return 1;
+       }
+
+       return 0;
+}
+/* spare records have their own family number and do not have any defined raid
+ * devices
+ */
+static int write_super_imsm_spares(struct intel_super *super, int doclose)
+{
+       struct dl *d;
 
-               sum = __gen_imsm_checksum(spare);
-               spare->family_num = __cpu_to_le32(sum);
-               spare->orig_family_num = 0;
-               sum = __gen_imsm_checksum(spare);
-               spare->check_sum = __cpu_to_le32(sum);
+       for (d = super->disks; d; d = d->next) {
+               if (d->index != -1)
+                       continue;
 
-               if (store_imsm_mpb(d->fd, spare)) {
-                       pr_err("failed for device %d:%d %s\n",
-                               d->major, d->minor, strerror(errno));
+               if (write_super_imsm_spare(super, d))
                        return 1;
-               }
-               if (doclose) {
-                       close(d->fd);
-                       d->fd = -1;
-               }
+
+               if (doclose)
+                       close_fd(&d->fd);
        }
 
        return 0;
@@ -6120,10 +6278,10 @@ static int write_super_imsm(struct supertype *st, int doclose)
        for (i = 0; i < mpb->num_raid_devs; i++) {
                struct imsm_dev *dev = __get_imsm_dev(mpb, i);
                struct imsm_dev *dev2 = get_imsm_dev(super, i);
-               if (dev && dev2) {
-                       imsm_copy_dev(dev, dev2);
-                       mpb_size += sizeof_imsm_dev(dev, 0);
-               }
+
+               imsm_copy_dev(dev, dev2);
+               mpb_size += sizeof_imsm_dev(dev, 0);
+
                if (is_gen_migration(dev2))
                        clear_migration_record = 0;
        }
@@ -6184,10 +6342,8 @@ static int write_super_imsm(struct supertype *st, int doclose)
                                d->major, d->minor,
                                d->fd, strerror(errno));
 
-               if (doclose) {
-                       close(d->fd);
-                       d->fd = -1;
-               }
+               if (doclose)
+                       close_fd(&d->fd);
        }
 
        if (spares)
@@ -6418,7 +6574,7 @@ static int validate_ppl_imsm(struct supertype *st, struct mdinfo *info,
                if (mdmon_running(st->container_devnm))
                        st->update_tail = &st->updates;
 
-               if (st->ss->update_subarray(st, subarray, "ppl", NULL)) {
+               if (st->ss->update_subarray(st, subarray, UOPT_PPL, NULL)) {
                        pr_err("Failed to update subarray %s\n",
                              subarray);
                } else {
@@ -6441,7 +6597,7 @@ static int validate_ppl_imsm(struct supertype *st, struct mdinfo *info,
                   (map->map_state == IMSM_T_STATE_NORMAL &&
                   !(dev->vol.dirty & RAIDVOL_DIRTY)) ||
                   (is_rebuilding(dev) &&
-                   dev->vol.curr_migr_unit == 0 &&
+                   vol_curr_migr_unit(dev) == 0 &&
                    get_imsm_disk_idx(dev, disk->disk.raid_disk, MAP_1) != idx))
                        ret = st->ss->write_init_ppl(st, info, d->fd);
                else
@@ -6486,6 +6642,60 @@ static int write_init_ppl_imsm_all(struct supertype *st, struct mdinfo *info)
        return ret;
 }
 
+/*******************************************************************************
+ * Function:   write_init_bitmap_imsm_vol
+ * Description:        Write a bitmap header and prepares the area for the bitmap.
+ * Parameters:
+ *     st      : supertype information
+ *     vol_idx : the volume index to use
+ *
+ * Returns:
+ *      0 : success
+ *     -1 : fail
+ ******************************************************************************/
+static int write_init_bitmap_imsm_vol(struct supertype *st, int vol_idx)
+{
+       struct intel_super *super = st->sb;
+       int prev_current_vol = super->current_vol;
+       struct dl *d;
+       int ret = 0;
+
+       super->current_vol = vol_idx;
+       for (d = super->disks; d; d = d->next) {
+               if (d->index < 0 || is_failed(&d->disk))
+                       continue;
+               ret = st->ss->write_bitmap(st, d->fd, NoUpdate);
+               if (ret)
+                       break;
+       }
+       super->current_vol = prev_current_vol;
+       return ret;
+}
+
+/*******************************************************************************
+ * Function:   write_init_bitmap_imsm_all
+ * Description:        Write a bitmap header and prepares the area for the bitmap.
+ *             Operation is executed for volumes with CONSISTENCY_POLICY_BITMAP.
+ * Parameters:
+ *     st      : supertype information
+ *     info    : info about the volume where the bitmap should be written
+ *     vol_idx : the volume index to use
+ *
+ * Returns:
+ *      0 : success
+ *     -1 : fail
+ ******************************************************************************/
+static int write_init_bitmap_imsm_all(struct supertype *st, struct mdinfo *info,
+                                     int vol_idx)
+{
+       int ret = 0;
+
+       if (info && (info->consistency_policy == CONSISTENCY_POLICY_BITMAP))
+               ret = write_init_bitmap_imsm_vol(st, vol_idx);
+
+       return ret;
+}
+
 static int write_init_super_imsm(struct supertype *st)
 {
        struct intel_super *super = st->sb;
@@ -6509,7 +6719,10 @@ static int write_init_super_imsm(struct supertype *st)
                         */
                        rv = mgmt_disk(st);
                } else {
+                       /* adding the second volume to the array */
                        rv = write_init_ppl_imsm_all(st, &info);
+                       if (!rv)
+                               rv = write_init_bitmap_imsm_all(st, &info, current_vol);
                        if (!rv)
                                rv = create_array(st, current_vol);
                }
@@ -6517,8 +6730,12 @@ static int write_init_super_imsm(struct supertype *st)
                struct dl *d;
                for (d = super->disks; d; d = d->next)
                        Kill(d->devname, NULL, 0, -1, 1);
-               if (current_vol >= 0)
+               if (current_vol >= 0) {
                        rv = write_init_ppl_imsm_all(st, &info);
+                       if (!rv)
+                               rv = write_init_bitmap_imsm_all(st, &info, current_vol);
+               }
+
                if (!rv)
                        rv = write_super_imsm(st, 1);
        }
@@ -6540,8 +6757,7 @@ static int store_super_imsm(struct supertype *st, int fd)
 }
 
 static int validate_geometry_imsm_container(struct supertype *st, int level,
-                                           int layout, int raiddisks, int chunk,
-                                           unsigned long long size,
+                                           int raiddisks,
                                            unsigned long long data_offset,
                                            char *dev,
                                            unsigned long long *freesize,
@@ -6549,39 +6765,31 @@ static int validate_geometry_imsm_container(struct supertype *st, int level,
 {
        int fd;
        unsigned long long ldsize;
-       struct intel_super *super;
+       struct intel_super *super = NULL;
        int rv = 0;
 
-       if (level != LEVEL_CONTAINER)
+       if (!is_container(level))
                return 0;
        if (!dev)
                return 1;
 
-       fd = open(dev, O_RDONLY|O_EXCL, 0);
-       if (fd < 0) {
-               if (verbose > 0)
-                       pr_err("imsm: Cannot open %s: %s\n",
-                               dev, strerror(errno));
-               return 0;
-       }
-       if (!get_dev_size(fd, dev, &ldsize)) {
-               close(fd);
+       fd = dev_open(dev, O_RDONLY|O_EXCL);
+       if (!is_fd_valid(fd)) {
+               pr_vrb("imsm: Cannot open %s: %s\n", dev, strerror(errno));
                return 0;
        }
+       if (!get_dev_size(fd, dev, &ldsize))
+               goto exit;
 
        /* capabilities retrieve could be possible
         * note that there is no fd for the disks in array.
         */
        super = alloc_super();
-       if (!super) {
-               close(fd);
-               return 0;
-       }
-       if (!get_dev_sector_size(fd, NULL, &super->sector_size)) {
-               close(fd);
-               free_imsm(super);
-               return 0;
-       }
+       if (!super)
+               goto exit;
+
+       if (!get_dev_sector_size(fd, NULL, &super->sector_size))
+               goto exit;
 
        rv = find_intel_hba_capability(fd, super, verbose > 0 ? dev : NULL);
        if (rv != 0) {
@@ -6592,32 +6800,42 @@ static int validate_geometry_imsm_container(struct supertype *st, int level,
                        fd, str, super->orom, rv, raiddisks);
 #endif
                /* no orom/efi or non-intel hba of the disk */
-               close(fd);
-               free_imsm(super);
-               return 0;
+               rv = 0;
+               goto exit;
        }
-       close(fd);
        if (super->orom) {
                if (raiddisks > super->orom->tds) {
                        if (verbose)
                                pr_err("%d exceeds maximum number of platform supported disks: %d\n",
                                        raiddisks, super->orom->tds);
-                       free_imsm(super);
-                       return 0;
+                       goto exit;
                }
                if ((super->orom->attr & IMSM_OROM_ATTR_2TB_DISK) == 0 &&
                    (ldsize >> 9) >> 32 > 0) {
                        if (verbose)
                                pr_err("%s exceeds maximum platform supported size\n", dev);
-                       free_imsm(super);
-                       return 0;
+                       goto exit;
                }
-       }
 
-       *freesize = avail_size_imsm(st, ldsize >> 9, data_offset);
-       free_imsm(super);
+               if (super->hba->type == SYS_DEV_VMD ||
+                   super->hba->type == SYS_DEV_NVME) {
+                       if (!imsm_is_nvme_namespace_supported(fd, 1)) {
+                               if (verbose)
+                                       pr_err("NVMe namespace %s is not supported by IMSM\n",
+                                               basename(dev));
+                               goto exit;
+                       }
+               }
+       }
+       if (freesize)
+               *freesize = avail_size_imsm(st, ldsize >> 9, data_offset);
+       rv = 1;
+exit:
+       if (super)
+               free_imsm(super);
+       close(fd);
 
-       return 1;
+       return rv;
 }
 
 static unsigned long long find_size(struct extent *e, int *idx, int num_extents)
@@ -6647,20 +6865,35 @@ static unsigned long long find_size(struct extent *e, int *idx, int num_extents)
        return end - base_start;
 }
 
-static unsigned long long merge_extents(struct intel_super *super, int sum_extents)
+/** merge_extents() - analyze extents and get free size.
+ * @super: Intel metadata, not NULL.
+ * @expanding: if set, we are expanding &super->current_vol.
+ *
+ * Build a composite disk with all known extents and generate a size given the
+ * "all disks in an array must share a common start offset" constraint.
+ * If a volume is expanded, then return free space after the volume.
+ *
+ * Return: Free space or 0 on failure.
+ */
+static unsigned long long merge_extents(struct intel_super *super, const bool expanding)
 {
-       /* build a composite disk with all known extents and generate a new
-        * 'maxsize' given the "all disks in an array must share a common start
-        * offset" constraint
-        */
-       struct extent *e = xcalloc(sum_extents, sizeof(*e));
+       struct extent *e;
        struct dl *dl;
-       int i, j;
-       int start_extent;
-       unsigned long long pos;
+       int i, j, pos_vol_idx = -1;
+       int extent_idx = 0;
+       int sum_extents = 0;
+       unsigned long long pos = 0;
        unsigned long long start = 0;
-       unsigned long long maxsize;
-       unsigned long reserve;
+       unsigned long long free_size = 0;
+
+       unsigned long pre_reservation = 0;
+       unsigned long post_reservation = IMSM_RESERVED_SECTORS;
+       unsigned long reservation_size;
+
+       for (dl = super->disks; dl; dl = dl->next)
+               if (dl->e)
+                       sum_extents += dl->extent_cnt;
+       e = xcalloc(sum_extents, sizeof(struct extent));
 
        /* coalesce and sort all extents. also, check to see if we need to
         * reserve space between member arrays
@@ -6679,50 +6912,57 @@ static unsigned long long merge_extents(struct intel_super *super, int sum_exten
        j = 0;
        while (i < sum_extents) {
                e[j].start = e[i].start;
+               e[j].vol = e[i].vol;
                e[j].size = find_size(e, &i, sum_extents);
                j++;
                if (e[j-1].size == 0)
                        break;
        }
 
-       pos = 0;
-       maxsize = 0;
-       start_extent = 0;
        i = 0;
        do {
-               unsigned long long esize;
+               unsigned long long esize = e[i].start - pos;
 
-               esize = e[i].start - pos;
-               if (esize >= maxsize) {
-                       maxsize = esize;
+               if (expanding ? pos_vol_idx == super->current_vol : esize >= free_size) {
+                       free_size = esize;
                        start = pos;
-                       start_extent = i;
+                       extent_idx = i;
                }
+
                pos = e[i].start + e[i].size;
+               pos_vol_idx = e[i].vol;
+
                i++;
        } while (e[i-1].size);
-       free(e);
 
-       if (maxsize == 0)
+       if (free_size == 0) {
+               dprintf("imsm: Cannot find free size.\n");
+               free(e);
                return 0;
+       }
 
-       /* FIXME assumes volume at offset 0 is the first volume in a
-        * container
-        */
-       if (start_extent > 0)
-               reserve = IMSM_RESERVED_SECTORS; /* gap between raid regions */
-       else
-               reserve = 0;
+       if (!expanding && extent_idx != 0)
+               /*
+                * Not a real first volume in a container is created, pre_reservation is needed.
+                */
+               pre_reservation = IMSM_RESERVED_SECTORS;
 
-       if (maxsize < reserve)
-               return 0;
+       if (e[extent_idx].size == 0)
+               /*
+                * extent_idx points to the metadata, post_reservation is allready done.
+                */
+               post_reservation = 0;
+       free(e);
 
-       super->create_offset = ~((unsigned long long) 0);
-       if (start + reserve > super->create_offset)
-               return 0; /* start overflows create_offset */
-       super->create_offset = start + reserve;
+       reservation_size = pre_reservation + post_reservation;
+
+       if (free_size < reservation_size) {
+               dprintf("imsm: Reservation size is greater than free space.\n");
+               return 0;
+       }
 
-       return maxsize - reserve;
+       super->create_offset = start + pre_reservation;
+       return free_size - reservation_size;
 }
 
 static int is_raid_level_supported(const struct imsm_orom *orom, int level, int raiddisks)
@@ -6766,12 +7006,12 @@ active_arrays_by_format(char *name, char* hba, struct md_list **devlist,
                    memb->members) {
                        struct dev_member *dev = memb->members;
                        int fd = -1;
-                       while(dev && (fd < 0)) {
+                       while (dev && !is_fd_valid(fd)) {
                                char *path = xmalloc(strlen(dev->name) + strlen("/dev/") + 1);
-                               num = sprintf(path, "%s%s", "/dev/", dev->name);
+                               num = snprintf(path, PATH_MAX, "%s%s", "/dev/", dev->name);
                                if (num > 0)
                                        fd = open(path, O_RDONLY, 0);
-                               if (num <= 0 || fd < 0) {
+                               if (num <= 0 || !is_fd_valid(fd)) {
                                        pr_vrb("Cannot open %s: %s\n",
                                               dev->name, strerror(errno));
                                }
@@ -6779,7 +7019,7 @@ active_arrays_by_format(char *name, char* hba, struct md_list **devlist,
                                dev = dev->next;
                        }
                        found = 0;
-                       if (fd >= 0 && disk_attached_to_hba(fd, hba)) {
+                       if (is_fd_valid(fd) && disk_attached_to_hba(fd, hba)) {
                                struct mdstat_ent *vol;
                                for (vol = mdstat ; vol ; vol = vol->next) {
                                        if (vol->active > 0 &&
@@ -6799,8 +7039,7 @@ active_arrays_by_format(char *name, char* hba, struct md_list **devlist,
                                        *devlist = dv;
                                }
                        }
-                       if (fd >= 0)
-                               close(fd);
+                       close_fd(&fd);
                }
        }
        free_mdstat(mdstat);
@@ -6850,7 +7089,7 @@ get_devices(const char *hba_path)
                char *path = NULL;
                if (sscanf(ent->d_name, "%d:%d", &major, &minor) != 2)
                        continue;
-               path = devt_to_devpath(makedev(major, minor));
+               path = devt_to_devpath(makedev(major, minor), 1, NULL);
                if (!path)
                        continue;
                if (!path_attached_to_hba(path, hba_path)) {
@@ -6861,7 +7100,7 @@ get_devices(const char *hba_path)
                free(path);
                path = NULL;
                fd = dev_open(ent->d_name, O_RDONLY);
-               if (fd >= 0) {
+               if (is_fd_valid(fd)) {
                        fd2devname(fd, buf);
                        close(fd);
                } else {
@@ -6920,7 +7159,7 @@ count_volumes_list(struct md_list *devlist, char *homehost,
                }
                tmpdev->container = 0;
                dfd = dev_open(devname, O_RDONLY|O_EXCL);
-               if (dfd < 0) {
+               if (!is_fd_valid(dfd)) {
                        dprintf("cannot open device %s: %s\n",
                                devname, strerror(errno));
                        tmpdev->used = 2;
@@ -6957,8 +7196,8 @@ count_volumes_list(struct md_list *devlist, char *homehost,
                                tmpdev->used = 2;
                        }
                }
-               if (dfd >= 0)
-                       close(dfd);
+               close_fd(&dfd);
+
                if (tmpdev->used == 2 || tmpdev->used == 4) {
                        /* Ignore unrecognised devices during auto-assembly */
                        goto loop;
@@ -6983,7 +7222,7 @@ count_volumes_list(struct md_list *devlist, char *homehost,
 
                        if (st->ss != tst->ss ||
                            st->minor_version != tst->minor_version ||
-                           st->ss->compare_super(st, tst) != 0) {
+                           st->ss->compare_super(st, tst, 1) != 0) {
                                /* Some mismatch. If exactly one array matches this host,
                                 * we can resolve on that one.
                                 * Or, if we are auto assembling, we just ignore the second
@@ -7321,13 +7560,7 @@ static int validate_geometry_imsm_volume(struct supertype *st, int level,
                return 0;
        }
 
-       /* count total number of extents for merge */
-       i = 0;
-       for (dl = super->disks; dl; dl = dl->next)
-               if (dl->e)
-                       i += dl->extent_cnt;
-
-       maxsize = merge_extents(super, i);
+       maxsize = merge_extents(super, false);
 
        if (mpb->num_raid_devs > 0 && size && size != maxsize)
                pr_err("attempting to create a second volume with size less then remaining space.\n");
@@ -7357,25 +7590,41 @@ static int validate_geometry_imsm_volume(struct supertype *st, int level,
        return 1;
 }
 
-static int imsm_get_free_size(struct supertype *st, int raiddisks,
-                        unsigned long long size, int chunk,
-                        unsigned long long *freesize)
+/**
+ * imsm_get_free_size() - get the biggest, common free space from members.
+ * @super: &intel_super pointer, not NULL.
+ * @raiddisks: number of raid disks.
+ * @size: requested size, could be 0 (means max size).
+ * @chunk: requested chunk size in KiB.
+ * @freesize: pointer for returned size value.
+ *
+ * Return: &IMSM_STATUS_OK or &IMSM_STATUS_ERROR.
+ *
+ * @freesize is set to meaningful value, this can be @size, or calculated
+ * max free size.
+ * super->create_offset value is modified and set appropriately in
+ * merge_extends() for further creation.
+ */
+static imsm_status_t imsm_get_free_size(struct intel_super *super,
+                                       const int raiddisks,
+                                       unsigned long long size,
+                                       const int chunk,
+                                       unsigned long long *freesize,
+                                       bool expanding)
 {
-       struct intel_super *super = st->sb;
        struct imsm_super *mpb = super->anchor;
        struct dl *dl;
        int i;
-       int extent_cnt;
        struct extent *e;
+       int cnt = 0;
+       int used = 0;
        unsigned long long maxsize;
-       unsigned long long minsize;
-       int cnt;
-       int used;
+       unsigned long long minsize = size;
+
+       if (minsize == 0)
+               minsize = chunk * 2;
 
        /* find the largest common start free region of the possible disks */
-       used = 0;
-       extent_cnt = 0;
-       cnt = 0;
        for (dl = super->disks; dl; dl = dl->next) {
                dl->raiddisk = -1;
 
@@ -7395,22 +7644,19 @@ static int imsm_get_free_size(struct supertype *st, int raiddisks,
                        ;
                dl->e = e;
                dl->extent_cnt = i;
-               extent_cnt += i;
                cnt++;
        }
 
-       maxsize = merge_extents(super, extent_cnt);
-       minsize = size;
-       if (size == 0)
-               /* chunk is in K */
-               minsize = chunk * 2;
+       maxsize = merge_extents(super, expanding);
+       if (maxsize < minsize)  {
+               pr_err("imsm: Free space is %llu but must be equal or larger than %llu.\n",
+                      maxsize, minsize);
+               return IMSM_STATUS_ERROR;
+       }
 
-       if (cnt < raiddisks ||
-           (super->orom && used && used != raiddisks) ||
-           maxsize < minsize ||
-           maxsize == 0) {
-               pr_err("not enough devices with space to create array.\n");
-               return 0; /* No enough free spaces large enough */
+       if (cnt < raiddisks || (super->orom && used && used != raiddisks)) {
+               pr_err("imsm: Not enough devices with space to create array.\n");
+               return IMSM_STATUS_ERROR;
        }
 
        if (size == 0) {
@@ -7423,37 +7669,69 @@ static int imsm_get_free_size(struct supertype *st, int raiddisks,
        }
        if (mpb->num_raid_devs > 0 && size && size != maxsize)
                pr_err("attempting to create a second volume with size less then remaining space.\n");
-       cnt = 0;
-       for (dl = super->disks; dl; dl = dl->next)
-               if (dl->e)
-                       dl->raiddisk = cnt++;
-
        *freesize = size;
 
        dprintf("imsm: imsm_get_free_size() returns : %llu\n", size);
 
-       return 1;
+       return IMSM_STATUS_OK;
 }
 
-static int reserve_space(struct supertype *st, int raiddisks,
-                        unsigned long long size, int chunk,
-                        unsigned long long *freesize)
+/**
+ * autolayout_imsm() - automatically layout a new volume.
+ * @super: &intel_super pointer, not NULL.
+ * @raiddisks: number of raid disks.
+ * @size: requested size, could be 0 (means max size).
+ * @chunk: requested chunk.
+ * @freesize: pointer for returned size value.
+ *
+ * We are being asked to automatically layout a new volume based on the current
+ * contents of the container. If the parameters can be satisfied autolayout_imsm
+ * will record the disks, start offset, and will return size of the volume to
+ * be created. See imsm_get_free_size() for details.
+ * add_to_super() and getinfo_super() detect when autolayout is in progress.
+ * If first volume exists, slots are set consistently to it.
+ *
+ * Return: &IMSM_STATUS_OK on success, &IMSM_STATUS_ERROR otherwise.
+ *
+ * Disks are marked for creation via dl->raiddisk.
+ */
+static imsm_status_t autolayout_imsm(struct intel_super *super,
+                                    const int raiddisks,
+                                    unsigned long long size, const int chunk,
+                                    unsigned long long *freesize)
 {
-       struct intel_super *super = st->sb;
-       struct dl *dl;
-       int cnt;
-       int rv = 0;
+       int curr_slot = 0;
+       struct dl *disk;
+       int vol_cnt = super->anchor->num_raid_devs;
+       imsm_status_t rv;
 
-       rv = imsm_get_free_size(st, raiddisks, size, chunk, freesize);
-       if (rv) {
-               cnt = 0;
-               for (dl = super->disks; dl; dl = dl->next)
-                       if (dl->e)
-                               dl->raiddisk = cnt++;
-               rv = 1;
+       rv = imsm_get_free_size(super, raiddisks, size, chunk, freesize, false);
+       if (rv != IMSM_STATUS_OK)
+               return IMSM_STATUS_ERROR;
+
+       for (disk = super->disks; disk; disk = disk->next) {
+               if (!disk->e)
+                       continue;
+
+               if (curr_slot == raiddisks)
+                       break;
+
+               if (vol_cnt == 0) {
+                       disk->raiddisk = curr_slot;
+               } else {
+                       int _slot = get_disk_slot_in_dev(super, 0, disk->index);
+
+                       if (_slot == -1) {
+                               pr_err("Disk %s is not used in first volume, aborting\n",
+                                      disk->devname);
+                               return IMSM_STATUS_ERROR;
+                       }
+                       disk->raiddisk = _slot;
+               }
+               curr_slot++;
        }
 
-       return rv;
+       return IMSM_STATUS_OK;
 }
 
 static int validate_geometry_imsm(struct supertype *st, int level, int layout,
@@ -7470,17 +7748,16 @@ static int validate_geometry_imsm(struct supertype *st, int level, int layout,
         * if given unused devices create a container
         * if given given devices in a container create a member volume
         */
-       if (level == LEVEL_CONTAINER) {
+       if (is_container(level))
                /* Must be a fresh device to add to a container */
-               return validate_geometry_imsm_container(st, level, layout,
-                                                       raiddisks,
-                                                       *chunk,
-                                                       size, data_offset,
-                                                       dev, freesize,
-                                                       verbose);
-       }
+               return validate_geometry_imsm_container(st, level, raiddisks,
+                                                       data_offset, dev,
+                                                       freesize, verbose);
 
-       if (size && (size < 1024)) {
+       /*
+        * Size is given in sectors.
+        */
+       if (size && (size < 2048)) {
                pr_err("Given size must be greater than 1M.\n");
                /* Depends on algorithm in Create.c :
                 * if container was given (dev == NULL) return -1,
@@ -7490,35 +7767,35 @@ static int validate_geometry_imsm(struct supertype *st, int level, int layout,
        }
 
        if (!dev) {
-               if (st->sb) {
-                       struct intel_super *super = st->sb;
-                       if (!validate_geometry_imsm_orom(st->sb, level, layout,
-                                                        raiddisks, chunk, size,
-                                                        verbose))
+               struct intel_super *super = st->sb;
+
+               /*
+                * Autolayout mode, st->sb must be set.
+                */
+               if (!super) {
+                       pr_vrb("superblock must be set for autolayout, aborting\n");
+                       return 0;
+               }
+
+               if (!validate_geometry_imsm_orom(st->sb, level, layout,
+                                                raiddisks, chunk, size,
+                                                verbose))
+                       return 0;
+
+               if (super->orom && freesize) {
+                       imsm_status_t rv;
+                       int count = count_volumes(super->hba, super->orom->dpa,
+                                             verbose);
+                       if (super->orom->vphba <= count) {
+                               pr_vrb("platform does not support more than %d raid volumes.\n",
+                                      super->orom->vphba);
                                return 0;
-                       /* we are being asked to automatically layout a
-                        * new volume based on the current contents of
-                        * the container.  If the the parameters can be
-                        * satisfied reserve_space will record the disks,
-                        * start offset, and size of the volume to be
-                        * created.  add_to_super and getinfo_super
-                        * detect when autolayout is in progress.
-                        */
-                       /* assuming that freesize is always given when array is
-                          created */
-                       if (super->orom && freesize) {
-                               int count;
-                               count = count_volumes(super->hba,
-                                                     super->orom->dpa, verbose);
-                               if (super->orom->vphba <= count) {
-                                       pr_vrb("platform does not support more than %d raid volumes.\n",
-                                              super->orom->vphba);
-                                       return 0;
-                               }
                        }
-                       if (freesize)
-                               return reserve_space(st, raiddisks, size,
-                                                    *chunk, freesize);
+
+                       rv = autolayout_imsm(super, raiddisks, size, *chunk,
+                                            freesize);
+                       if (rv != IMSM_STATUS_OK)
+                               return 0;
                }
                return 1;
        }
@@ -7532,26 +7809,26 @@ static int validate_geometry_imsm(struct supertype *st, int level, int layout,
 
        /* This device needs to be a device in an 'imsm' container */
        fd = open(dev, O_RDONLY|O_EXCL, 0);
-       if (fd >= 0) {
-               if (verbose)
-                       pr_err("Cannot create this array on device %s\n",
-                              dev);
+
+       if (is_fd_valid(fd)) {
+               pr_vrb("Cannot create this array on device %s\n", dev);
                close(fd);
                return 0;
        }
-       if (errno != EBUSY || (fd = open(dev, O_RDONLY, 0)) < 0) {
-               if (verbose)
-                       pr_err("Cannot open %s: %s\n",
-                               dev, strerror(errno));
+       if (errno == EBUSY)
+               fd = open(dev, O_RDONLY, 0);
+
+       if (!is_fd_valid(fd)) {
+               pr_vrb("Cannot open %s: %s\n", dev, strerror(errno));
                return 0;
        }
+
        /* Well, it is in use by someone, maybe an 'imsm' container. */
        cfd = open_container(fd);
-       close(fd);
-       if (cfd < 0) {
-               if (verbose)
-                       pr_err("Cannot use %s: It is busy\n",
-                               dev);
+       close_fd(&fd);
+
+       if (!is_fd_valid(cfd)) {
+               pr_vrb("Cannot use %s: It is busy\n", dev);
                return 0;
        }
        sra = sysfs_read(cfd, NULL, GET_VERSION);
@@ -7623,7 +7900,7 @@ static int kill_subarray_imsm(struct supertype *st, char *subarray_id)
 
                if (i < current_vol)
                        continue;
-               sprintf(subarray, "%u", i);
+               snprintf(subarray, sizeof(subarray), "%u", i);
                if (is_subarray_active(subarray, st->devnm)) {
                        pr_err("deleting subarray-%d would change the UUID of active subarray-%d, aborting\n",
                               current_vol, i);
@@ -7668,24 +7945,40 @@ static int kill_subarray_imsm(struct supertype *st, char *subarray_id)
        return 0;
 }
 
+/**
+ * get_rwh_policy_from_update() - Get the rwh policy for update option.
+ * @update: Update option.
+ */
+static int get_rwh_policy_from_update(enum update_opt update)
+{
+       switch (update) {
+       case UOPT_PPL:
+               return RWH_MULTIPLE_DISTRIBUTED;
+       case UOPT_NO_PPL:
+               return RWH_MULTIPLE_OFF;
+       case UOPT_BITMAP:
+               return RWH_BITMAP;
+       case UOPT_NO_BITMAP:
+               return RWH_OFF;
+       default:
+               break;
+       }
+       return UOPT_UNDEFINED;
+}
+
 static int update_subarray_imsm(struct supertype *st, char *subarray,
-                               char *update, struct mddev_ident *ident)
+                               enum update_opt update, struct mddev_ident *ident)
 {
        /* update the subarray currently referenced by ->current_vol */
        struct intel_super *super = st->sb;
        struct imsm_super *mpb = super->anchor;
 
-       if (strcmp(update, "name") == 0) {
+       if (update == UOPT_NAME) {
                char *name = ident->name;
                char *ep;
                int vol;
 
-               if (is_subarray_active(subarray, st->devnm)) {
-                       pr_err("Unable to update name of active subarray\n");
-                       return 2;
-               }
-
-               if (!check_name(super, name, 0))
+               if (imsm_is_name_allowed(super, name, 1) == false)
                        return 2;
 
                vol = strtoul(subarray, &ep, 10);
@@ -7714,8 +8007,7 @@ static int update_subarray_imsm(struct supertype *st, char *subarray,
                        }
                        super->updates_pending++;
                }
-       } else if (strcmp(update, "ppl") == 0 ||
-                  strcmp(update, "no-ppl") == 0) {
+       } else if (get_rwh_policy_from_update(update) != UOPT_UNDEFINED) {
                int new_policy;
                char *ep;
                int vol = strtoul(subarray, &ep, 10);
@@ -7723,10 +8015,7 @@ static int update_subarray_imsm(struct supertype *st, char *subarray,
                if (*ep != '\0' || vol >= super->anchor->num_raid_devs)
                        return 2;
 
-               if (strcmp(update, "ppl") == 0)
-                       new_policy = RWH_MULTIPLE_DISTRIBUTED;
-               else
-                       new_policy = RWH_MULTIPLE_OFF;
+               new_policy = get_rwh_policy_from_update(update);
 
                if (st->update_tail) {
                        struct imsm_update_rwh_policy *u = xmalloc(sizeof(*u));
@@ -7742,24 +8031,21 @@ static int update_subarray_imsm(struct supertype *st, char *subarray,
                        dev->rwh_policy = new_policy;
                        super->updates_pending++;
                }
+               if (new_policy == RWH_BITMAP)
+                       return write_init_bitmap_imsm_vol(st, vol);
        } else
                return 2;
 
        return 0;
 }
 
-static int is_gen_migration(struct imsm_dev *dev)
+static bool is_gen_migration(struct imsm_dev *dev)
 {
-       if (dev == NULL)
-               return 0;
-
-       if (!dev->vol.migr_state)
-               return 0;
+       if (dev && dev->vol.migr_state &&
+           migr_type(dev) == MIGR_GEN_MIGR)
+               return true;
 
-       if (migr_type(dev) == MIGR_GEN_MIGR)
-               return 1;
-
-       return 0;
+       return false;
 }
 
 static int is_rebuilding(struct imsm_dev *dev)
@@ -7826,7 +8112,7 @@ static void update_recovery_start(struct intel_super *super,
                return;
        }
 
-       units = __le32_to_cpu(dev->vol.curr_migr_unit);
+       units = vol_curr_migr_unit(dev);
        rebuild->recovery_start = units * blocks_per_migr_unit(super, dev);
 }
 
@@ -7959,10 +8245,6 @@ static struct mdinfo *container_content_imsm(struct supertype *st, char *subarra
                                if ((!able_to_resync(level, missing) ||
                                     recovery_start == 0))
                                        this->resync_start = MaxSector;
-                       } else {
-                               /*
-                                * FIXME handle dirty degraded
-                                */
                        }
 
                        if (skip)
@@ -8145,19 +8427,19 @@ static int imsm_count_failed(struct intel_super *super, struct imsm_dev *dev,
 }
 
 static int imsm_open_new(struct supertype *c, struct active_array *a,
-                        char *inst)
+                        int inst)
 {
        struct intel_super *super = c->sb;
        struct imsm_super *mpb = super->anchor;
        struct imsm_update_prealloc_bb_mem u;
 
-       if (atoi(inst) >= mpb->num_raid_devs) {
-               pr_err("subarry index %d, out of range\n", atoi(inst));
+       if (inst >= mpb->num_raid_devs) {
+               pr_err("subarry index %d, out of range\n", inst);
                return -ENODEV;
        }
 
-       dprintf("imsm: open_new %s\n", inst);
-       a->info.container_member = atoi(inst);
+       dprintf("imsm: open_new %d\n", inst);
+       a->info.container_member = inst;
 
        u.type = update_prealloc_badblocks_mem;
        imsm_update_metadata_locally(c, &u, sizeof(u));
@@ -8267,7 +8549,7 @@ static void handle_missing(struct intel_super *super, struct imsm_dev *dev)
        dprintf("imsm: mark missing\n");
        /* end process for initialization and rebuild only
         */
-       if (is_gen_migration(dev) == 0) {
+       if (is_gen_migration(dev) == false) {
                int failed = imsm_count_failed(super, dev, MAP_0);
 
                if (failed) {
@@ -8373,7 +8655,7 @@ static void imsm_progress_container_reshape(struct intel_super *super)
                prev_num_members = map->num_members;
                map->num_members = prev_disks;
                dev->vol.migr_state = 1;
-               dev->vol.curr_migr_unit = 0;
+               set_vol_curr_migr_unit(dev, 0);
                set_migr_type(dev, MIGR_GEN_MIGR);
                for (i = prev_num_members;
                     i < map->num_members; i++)
@@ -8410,29 +8692,12 @@ static int imsm_set_array_state(struct active_array *a, int consistent)
                 * We might need to
                 * - abort the reshape (if last_checkpoint is 0 and action!= reshape)
                 * - finish the reshape (if last_checkpoint is big and action != reshape)
-                * - update curr_migr_unit
+                * - update vol_curr_migr_unit
                 */
                if (a->curr_action == reshape) {
-                       /* still reshaping, maybe update curr_migr_unit */
+                       /* still reshaping, maybe update vol_curr_migr_unit */
                        goto mark_checkpoint;
                } else {
-                       if (a->last_checkpoint == 0 && a->prev_action == reshape) {
-                               /* for some reason we aborted the reshape.
-                                *
-                                * disable automatic metadata rollback
-                                * user action is required to recover process
-                                */
-                               if (0) {
-                                       struct imsm_map *map2 =
-                                               get_imsm_map(dev, MAP_1);
-                                       dev->vol.migr_state = 0;
-                                       set_migr_type(dev, 0);
-                                       dev->vol.curr_migr_unit = 0;
-                                       memcpy(map, map2,
-                                              sizeof_imsm_map(map2));
-                                       super->updates_pending++;
-                               }
-                       }
                        if (a->last_checkpoint >= a->info.component_size) {
                                unsigned long long array_blocks;
                                int used_disks;
@@ -8496,6 +8761,9 @@ static int imsm_set_array_state(struct active_array *a, int consistent)
                super->updates_pending++;
        }
 
+       if (a->prev_action == idle)
+               goto skip_mark_checkpoint;
+
 mark_checkpoint:
        /* skip checkpointing for general migration,
         * it is controlled in mdadm
@@ -8503,25 +8771,16 @@ mark_checkpoint:
        if (is_gen_migration(dev))
                goto skip_mark_checkpoint;
 
-       /* check if we can update curr_migr_unit from resync_start, recovery_start */
+       /* check if we can update vol_curr_migr_unit from resync_start,
+        * recovery_start
+        */
        blocks_per_unit = blocks_per_migr_unit(super, dev);
        if (blocks_per_unit) {
-               __u32 units32;
-               __u64 units;
-
-               units = a->last_checkpoint / blocks_per_unit;
-               units32 = units;
-
-               /* check that we did not overflow 32-bits, and that
-                * curr_migr_unit needs updating
-                */
-               if (units32 == units &&
-                   units32 != 0 &&
-                   __le32_to_cpu(dev->vol.curr_migr_unit) != units32) {
-                       dprintf("imsm: mark checkpoint (%u)\n", units32);
-                       dev->vol.curr_migr_unit = __cpu_to_le32(units32);
-                       super->updates_pending++;
-               }
+               set_vol_curr_migr_unit(dev,
+                                      a->last_checkpoint / blocks_per_unit);
+               dprintf("imsm: mark checkpoint (%llu)\n",
+                       vol_curr_migr_unit(dev));
+               super->updates_pending++;
        }
 
 skip_mark_checkpoint:
@@ -8625,7 +8884,6 @@ static void imsm_set_disk(struct active_array *a, int n, int state)
                                break;
                        }
                        end_migration(dev, super, map_state);
-                       map = get_imsm_map(dev, MAP_0);
                        map->failed_disk_num = ~0;
                        super->updates_pending++;
                        a->last_checkpoint = 0;
@@ -8637,7 +8895,6 @@ static void imsm_set_disk(struct active_array *a, int n, int state)
                                end_migration(dev, super, map_state);
                        else
                                map->map_state = map_state;
-                       map = get_imsm_map(dev, MAP_0);
                        map->failed_disk_num = ~0;
                        super->updates_pending++;
                        break;
@@ -8749,7 +9006,8 @@ static int store_imsm_mpb(int fd, struct imsm_super *mpb)
        unsigned long long sectors;
        unsigned int sector_size;
 
-       get_dev_sector_size(fd, NULL, &sector_size);
+       if (!get_dev_sector_size(fd, NULL, &sector_size))
+               return 1;
        get_dev_size(fd, NULL, &dsize);
 
        if (mpb_size > sector_size) {
@@ -8829,7 +9087,7 @@ static struct dl *imsm_add_spare(struct intel_super *super, int slot,
        for (dl = super->disks; dl; dl = dl->next) {
                /* If in this array, skip */
                for (d = a->info.devs ; d ; d = d->next)
-                       if (d->state_fd >= 0 &&
+                       if (is_fd_valid(d->state_fd) &&
                            d->disk.major == dl->major &&
                            d->disk.minor == dl->minor) {
                                dprintf("%x:%x already in array\n",
@@ -8932,29 +9190,26 @@ static int imsm_rebuild_allowed(struct supertype *cont, int dev_idx, int failed)
        __u8 state;
 
        dev2 = get_imsm_dev(cont->sb, dev_idx);
-       if (dev2) {
-               state = imsm_check_degraded(cont->sb, dev2, failed, MAP_0);
-               if (state == IMSM_T_STATE_FAILED) {
-                       map = get_imsm_map(dev2, MAP_0);
-                       if (!map)
-                               return 1;
-                       for (slot = 0; slot < map->num_members; slot++) {
-                               /*
-                                * Check if failed disks are deleted from intel
-                                * disk list or are marked to be deleted
-                                */
-                               idx = get_imsm_disk_idx(dev2, slot, MAP_X);
-                               idisk = get_imsm_dl_disk(cont->sb, idx);
-                               /*
-                                * Do not rebuild the array if failed disks
-                                * from failed sub-array are not removed from
-                                * container.
-                                */
-                               if (idisk &&
-                                   is_failed(&idisk->disk) &&
-                                   (idisk->action != DISK_REMOVE))
-                                       return 0;
-                       }
+
+       state = imsm_check_degraded(cont->sb, dev2, failed, MAP_0);
+       if (state == IMSM_T_STATE_FAILED) {
+               map = get_imsm_map(dev2, MAP_0);
+               for (slot = 0; slot < map->num_members; slot++) {
+                       /*
+                        * Check if failed disks are deleted from intel
+                        * disk list or are marked to be deleted
+                        */
+                       idx = get_imsm_disk_idx(dev2, slot, MAP_X);
+                       idisk = get_imsm_dl_disk(cont->sb, idx);
+                       /*
+                        * Do not rebuild the array if failed disks
+                        * from failed sub-array are not removed from
+                        * container.
+                        */
+                       if (idisk &&
+                           is_failed(&idisk->disk) &&
+                           (idisk->action != DISK_REMOVE))
+                               return 0;
                }
        }
        return 1;
@@ -8989,13 +9244,15 @@ static struct mdinfo *imsm_activate_spare(struct active_array *a,
        int i;
        int allowed;
 
-       for (d = a->info.devs ; d ; d = d->next) {
-               if ((d->curr_state & DS_FAULTY) &&
-                       d->state_fd >= 0)
+       for (d = a->info.devs ; d; d = d->next) {
+               if (!is_fd_valid(d->state_fd))
+                       continue;
+
+               if (d->curr_state & DS_FAULTY)
                        /* wait for Removal to happen */
                        return NULL;
-               if (d->state_fd >= 0)
-                       failed--;
+
+               failed--;
        }
 
        dprintf("imsm: activate spare: inst=%d failed=%d (%d) level=%d\n",
@@ -9051,7 +9308,7 @@ static struct mdinfo *imsm_activate_spare(struct active_array *a,
                        if (d->disk.raid_disk == i)
                                break;
                dprintf("found %d: %p %x\n", i, d, d?d->curr_state:0);
-               if (d && (d->state_fd >= 0))
+               if (d && is_fd_valid(d->state_fd))
                        continue;
 
                /*
@@ -9181,7 +9438,7 @@ static int remove_disk_super(struct intel_super *super, int major, int minor)
                        else
                                super->disks = dl->next;
                        dl->next = NULL;
-                       __free_imsm_disk(dl);
+                       __free_imsm_disk(dl, 1);
                        dprintf("removed %x:%x\n", major, minor);
                        break;
                }
@@ -9231,7 +9488,7 @@ static int add_remove_disk_update(struct intel_super *super)
                                }
                        }
                        /* release allocate disk structure */
-                       __free_imsm_disk(disk_cfg);
+                       __free_imsm_disk(disk_cfg, 1);
                }
        }
        return check_degraded;
@@ -9306,7 +9563,6 @@ static int apply_reshape_migration_update(struct imsm_update_reshape_migration *
                        /* update chunk size
                         */
                        if (u->new_chunksize > 0) {
-                               unsigned long long num_data_stripes;
                                struct imsm_map *dest_map =
                                        get_imsm_map(dev, MAP_0);
                                int used_disks =
@@ -9317,11 +9573,7 @@ static int apply_reshape_migration_update(struct imsm_update_reshape_migration *
 
                                map->blocks_per_strip =
                                        __cpu_to_le16(u->new_chunksize * 2);
-                               num_data_stripes =
-                                       imsm_dev_size(dev) / used_disks;
-                               num_data_stripes /= map->blocks_per_strip;
-                               num_data_stripes /= map->num_domains;
-                               set_num_data_stripes(map, num_data_stripes);
+                               update_num_data_stripes(map, imsm_dev_size(dev));
                        }
 
                        /* ensure blocks_per_member has valid value
@@ -9395,7 +9647,6 @@ static int apply_size_change_update(struct imsm_update_size_change *u,
                        struct imsm_map *map = get_imsm_map(dev, MAP_0);
                        int used_disks = imsm_num_data_members(map);
                        unsigned long long blocks_per_member;
-                       unsigned long long num_data_stripes;
                        unsigned long long new_size_per_disk;
 
                        if (used_disks == 0)
@@ -9406,16 +9657,10 @@ static int apply_size_change_update(struct imsm_update_size_change *u,
                        new_size_per_disk = u->new_size / used_disks;
                        blocks_per_member = new_size_per_disk +
                                            NUM_BLOCKS_DIRTY_STRIPE_REGION;
-                       num_data_stripes = new_size_per_disk /
-                                          map->blocks_per_strip;
-                       num_data_stripes /= map->num_domains;
-                       dprintf("(size: %llu, blocks per member: %llu, num_data_stipes: %llu)\n",
-                               u->new_size, new_size_per_disk,
-                               num_data_stripes);
-                       set_blocks_per_member(map, blocks_per_member);
-                       set_num_data_stripes(map, num_data_stripes);
-                       imsm_set_array_size(dev, u->new_size);
 
+                       imsm_set_array_size(dev, u->new_size);
+                       set_blocks_per_member(map, blocks_per_member);
+                       update_num_data_stripes(map, u->new_size);
                        ret_val = 1;
                        break;
                }
@@ -9424,6 +9669,39 @@ static int apply_size_change_update(struct imsm_update_size_change *u,
        return ret_val;
 }
 
+static int prepare_spare_to_activate(struct supertype *st,
+                                    struct imsm_update_activate_spare *u)
+{
+       struct intel_super *super = st->sb;
+       int prev_current_vol = super->current_vol;
+       struct active_array *a;
+       int ret = 1;
+
+       for (a = st->arrays; a; a = a->next)
+               /*
+                * Additional initialization (adding bitmap header, filling
+                * the bitmap area with '1's to force initial rebuild for a whole
+                * data-area) is required when adding the spare to the volume
+                * with write-intent bitmap.
+                */
+               if (a->info.container_member == u->array &&
+                   a->info.consistency_policy == CONSISTENCY_POLICY_BITMAP) {
+                       struct dl *dl;
+
+                       for (dl = super->disks; dl; dl = dl->next)
+                               if (dl == u->dl)
+                                       break;
+                       if (!dl)
+                               break;
+
+                       super->current_vol = u->array;
+                       if (st->ss->write_bitmap(st, dl->fd, NoUpdate))
+                               ret = 0;
+                       super->current_vol = prev_current_vol;
+               }
+       return ret;
+}
+
 static int apply_update_activate_spare(struct imsm_update_activate_spare *u,
                                       struct intel_super *super,
                                       struct active_array *active_array)
@@ -9505,10 +9783,9 @@ static int apply_update_activate_spare(struct imsm_update_activate_spare *u,
                /* count arrays using the victim in the metadata */
                found = 0;
                for (a = active_array; a ; a = a->next) {
-                       dev = get_imsm_dev(super, a->info.container_member);
-                       map = get_imsm_map(dev, MAP_0);
+                       int dev_idx = a->info.container_member;
 
-                       if (get_imsm_disk_slot(map, victim) >= 0)
+                       if (get_disk_slot_in_dev(super, dev_idx, victim) >= 0)
                                found++;
                }
 
@@ -9608,7 +9885,7 @@ static int apply_reshape_container_disks_update(struct imsm_update_reshape *u,
                                id->index);
                        devices_to_reshape--;
                        newdev->vol.migr_state = 1;
-                       newdev->vol.curr_migr_unit = 0;
+                       set_vol_curr_migr_unit(newdev, 0);
                        set_migr_type(newdev, MIGR_GEN_MIGR);
                        newmap->num_members = u->new_raid_disks;
                        for (i = 0; i < delta_disks; i++) {
@@ -9664,8 +9941,6 @@ static int apply_takeover_update(struct imsm_update_takeover *u,
        map = get_imsm_map(dev, MAP_0);
 
        if (u->direction == R10_TO_R0) {
-               unsigned long long num_data_stripes;
-
                /* Number of failed disks must be half of initial disk number */
                if (imsm_count_failed(super, dev, MAP_0) !=
                                (map->num_members / 2))
@@ -9686,19 +9961,16 @@ static int apply_takeover_update(struct imsm_update_takeover *u,
                        }
                }
                /* update map */
-               map->num_members = map->num_members / 2;
+               map->num_members /= map->num_domains;
                map->map_state = IMSM_T_STATE_NORMAL;
-               map->num_domains = 1;
                map->raid_level = 0;
+               set_num_domains(map);
+               update_num_data_stripes(map, imsm_dev_size(dev));
                map->failed_disk_num = -1;
-               num_data_stripes = imsm_dev_size(dev) / 2;
-               num_data_stripes /= map->blocks_per_strip;
-               set_num_data_stripes(map, num_data_stripes);
        }
 
        if (u->direction == R0_TO_R10) {
                void **space;
-               unsigned long long num_data_stripes;
 
                /* update slots in current disk list */
                for (dm = super->disks; dm; dm = dm->next) {
@@ -9733,14 +10005,12 @@ 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, MAP_0);
-               map->num_members = map->num_members * 2;
+
                map->map_state = IMSM_T_STATE_DEGRADED;
-               map->num_domains = 2;
                map->raid_level = 1;
-               num_data_stripes = imsm_dev_size(dev) / 2;
-               num_data_stripes /= map->blocks_per_strip;
-               num_data_stripes /= map->num_domains;
-               set_num_data_stripes(map, num_data_stripes);
+               set_num_domains(map);
+               map->num_members = map->num_members * map->num_domains;
+               update_num_data_stripes(map, imsm_dev_size(dev));
 
                /* replace dev<->dev_new */
                dv->dev = dev_new;
@@ -9810,8 +10080,8 @@ static void imsm_process_update(struct supertype *st,
                /* find device under general migration */
                for (id = super->devlist ; id; id = id->next) {
                        if (is_gen_migration(id->dev)) {
-                               id->dev->vol.curr_migr_unit =
-                                       __cpu_to_le32(u->curr_migr_unit);
+                               set_vol_curr_migr_unit(id->dev,
+                                                  u->curr_migr_unit);
                                super->updates_pending++;
                        }
                }
@@ -9848,7 +10118,9 @@ static void imsm_process_update(struct supertype *st,
        }
        case update_activate_spare: {
                struct imsm_update_activate_spare *u = (void *) update->buf;
-               if (apply_update_activate_spare(u, super, st->arrays))
+
+               if (prepare_spare_to_activate(st, u) &&
+                   apply_update_activate_spare(u, super, st->arrays))
                        super->updates_pending++;
                break;
        }
@@ -9970,7 +10242,6 @@ static void imsm_process_update(struct supertype *st,
                int victim = u->dev_idx;
                struct active_array *a;
                struct intel_dev **dp;
-               struct imsm_dev *dev;
 
                /* sanity check that we are not affecting the uuid of
                 * active arrays, or deleting an active array
@@ -9986,8 +10257,7 @@ static void imsm_process_update(struct supertype *st,
                 * is active in the container, so checking
                 * mpb->num_raid_devs is just extra paranoia
                 */
-               dev = get_imsm_dev(super, victim);
-               if (a || !dev || mpb->num_raid_devs == 1) {
+               if (a || mpb->num_raid_devs == 1 || victim >= super->anchor->num_raid_devs) {
                        dprintf("failed to delete subarray-%d\n", victim);
                        break;
                }
@@ -10021,7 +10291,8 @@ static void imsm_process_update(struct supertype *st,
                        if (a->info.container_member == target)
                                break;
                dev = get_imsm_dev(super, u->dev_idx);
-               if (a || !dev || !check_name(super, name, 1)) {
+
+               if (a || !dev || imsm_is_name_allowed(super, name, 0) == false) {
                        dprintf("failed to rename subarray-%d\n", target);
                        break;
                }
@@ -10050,10 +10321,6 @@ static void imsm_process_update(struct supertype *st,
                struct imsm_update_rwh_policy *u = (void *)update->buf;
                int target = u->dev_idx;
                struct imsm_dev *dev = get_imsm_dev(super, target);
-               if (!dev) {
-                       dprintf("could not find subarray-%d\n", target);
-                       break;
-               }
 
                if (dev->rwh_policy != u->new_policy) {
                        dev->rwh_policy = u->new_policy;
@@ -10397,22 +10664,7 @@ static void imsm_delete(struct intel_super *super, struct dl **dlp, unsigned ind
                struct dl *dl = *dlp;
 
                *dlp = (*dlp)->next;
-               __free_imsm_disk(dl);
-       }
-}
-
-static void close_targets(int *targets, int new_disks)
-{
-       int i;
-
-       if (!targets)
-               return;
-
-       for (i = 0; i < new_disks; i++) {
-               if (targets[i] >= 0) {
-                       close(targets[i]);
-                       targets[i] = -1;
-               }
+               __free_imsm_disk(dl, 1);
        }
 }
 
@@ -10469,62 +10721,6 @@ static int imsm_get_allowed_degradation(int level, int raid_disks,
        }
 }
 
-/*******************************************************************************
- * Function:   open_backup_targets
- * Description:        Function opens file descriptors for all devices given in
- *             info->devs
- * Parameters:
- *     info            : general array info
- *     raid_disks      : number of disks
- *     raid_fds        : table of device's file descriptors
- *     super           : intel super for raid10 degradation check
- *     dev             : intel device for raid10 degradation check
- * Returns:
- *      0 : success
- *     -1 : fail
- ******************************************************************************/
-int open_backup_targets(struct mdinfo *info, int raid_disks, int *raid_fds,
-                       struct intel_super *super, struct imsm_dev *dev)
-{
-       struct mdinfo *sd;
-       int i;
-       int opened = 0;
-
-       for (i = 0; i < raid_disks; i++)
-               raid_fds[i] = -1;
-
-       for (sd = info->devs ; sd ; sd = sd->next) {
-               char *dn;
-
-               if (sd->disk.state & (1<<MD_DISK_FAULTY)) {
-                       dprintf("disk is faulty!!\n");
-                       continue;
-               }
-
-               if (sd->disk.raid_disk >= raid_disks || sd->disk.raid_disk < 0)
-                       continue;
-
-               dn = map_dev(sd->disk.major,
-                            sd->disk.minor, 1);
-               raid_fds[sd->disk.raid_disk] = dev_open(dn, O_RDWR);
-               if (raid_fds[sd->disk.raid_disk] < 0) {
-                       pr_err("cannot open component\n");
-                       continue;
-               }
-               opened++;
-       }
-       /* check if maximum array degradation level is not exceeded
-       */
-       if ((raid_disks - opened) >
-           imsm_get_allowed_degradation(info->new_level, raid_disks,
-                                        super, dev)) {
-               pr_err("Not enough disks can be opened.\n");
-               close_targets(raid_fds, raid_disks);
-               return -2;
-       }
-       return 0;
-}
-
 /*******************************************************************************
  * Function:   validate_container_imsm
  * Description: This routine validates container after assemble,
@@ -10538,14 +10734,14 @@ int open_backup_targets(struct mdinfo *info, int raid_disks, int *raid_fds,
  ******************************************************************************/
 int validate_container_imsm(struct mdinfo *info)
 {
-       if (check_env("IMSM_NO_PLATFORM"))
+       if (check_no_platform())
                return 0;
 
        struct sys_dev *idev;
        struct sys_dev *hba = NULL;
        struct sys_dev *intel_devices = find_intel_devices();
        char *dev_path = devt_to_devpath(makedev(info->disk.major,
-                                                                       info->disk.minor));
+                                                info->disk.minor), 1, NULL);
 
        for (idev = intel_devices; idev; idev = idev->next) {
                if (dev_path && strstr(dev_path, idev->path)) {
@@ -10566,7 +10762,8 @@ int validate_container_imsm(struct mdinfo *info)
        struct mdinfo *dev;
 
        for (dev = info->next; dev; dev = dev->next) {
-               dev_path = devt_to_devpath(makedev(dev->disk.major, dev->disk.minor));
+               dev_path = devt_to_devpath(makedev(dev->disk.major,
+                                                  dev->disk.minor), 1, NULL);
 
                struct sys_dev *hba2 = NULL;
                for (idev = intel_devices; idev; idev = idev->next) {
@@ -10765,13 +10962,11 @@ void init_migr_record_imsm(struct supertype *st, struct imsm_dev *dev,
        int new_data_disks;
        unsigned long long dsize, dev_sectors;
        long long unsigned min_dev_sectors = -1LLU;
-       struct mdinfo *sd;
-       char nm[30];
-       int fd;
        struct imsm_map *map_dest = get_imsm_map(dev, MAP_0);
        struct imsm_map *map_src = get_imsm_map(dev, MAP_1);
        unsigned long long num_migr_units;
        unsigned long long array_blocks;
+       struct dl *dl_disk = NULL;
 
        memset(migr_rec, 0, sizeof(struct migr_record));
        migr_rec->family_num = __cpu_to_le32(super->anchor->family_num);
@@ -10800,16 +10995,14 @@ void init_migr_record_imsm(struct supertype *st, struct imsm_dev *dev,
        migr_rec->post_migr_vol_cap_hi = dev->size_high;
 
        /* Find the smallest dev */
-       for (sd = info->devs ; sd ; sd = sd->next) {
-               sprintf(nm, "%d:%d", sd->disk.major, sd->disk.minor);
-               fd = dev_open(nm, O_RDONLY);
-               if (fd < 0)
+       for (dl_disk =  super->disks; dl_disk ; dl_disk = dl_disk->next) {
+               /* ignore spares in container */
+               if (dl_disk->index < 0)
                        continue;
-               get_dev_size(fd, NULL, &dsize);
+               get_dev_size(dl_disk->fd, NULL, &dsize);
                dev_sectors = dsize / 512;
                if (dev_sectors < min_dev_sectors)
                        min_dev_sectors = dev_sectors;
-               close(fd);
        }
        set_migr_chkp_area_pba(migr_rec, min_dev_sectors -
                                        RAID_DISK_RESERVED_BLOCKS_IMSM_HI);
@@ -10843,22 +11036,21 @@ int save_backup_imsm(struct supertype *st,
 {
        int rv = -1;
        struct intel_super *super = st->sb;
-       unsigned long long *target_offsets;
-       int *targets;
        int i;
        struct imsm_map *map_dest = get_imsm_map(dev, MAP_0);
        int new_disks = map_dest->num_members;
        int dest_layout = 0;
-       int dest_chunk;
-       unsigned long long start;
+       int dest_chunk, targets[new_disks];
+       unsigned long long start, target_offsets[new_disks];
        int data_disks = imsm_num_data_members(map_dest);
 
-       targets = xmalloc(new_disks * sizeof(int));
-
-       for (i = 0; i < new_disks; i++)
-               targets[i] = -1;
-
-       target_offsets = xcalloc(new_disks, sizeof(unsigned long long));
+       for (i = 0; i < new_disks; i++) {
+               struct dl *dl_disk = get_imsm_dl_disk(super, i);
+               if (dl_disk && is_fd_valid(dl_disk->fd))
+                       targets[i] = dl_disk->fd;
+               else
+                       goto abort;
+       }
 
        start = info->reshape_progress * 512;
        for (i = 0; i < new_disks; i++) {
@@ -10869,10 +11061,6 @@ int save_backup_imsm(struct supertype *st,
                target_offsets[i] -= start/data_disks;
        }
 
-       if (open_backup_targets(info, new_disks, targets,
-                               super, dev))
-               goto abort;
-
        dest_layout = imsm_level_to_layout(map_dest->raid_level);
        dest_chunk = __le16_to_cpu(map_dest->blocks_per_strip) * 512;
 
@@ -10895,12 +11083,6 @@ int save_backup_imsm(struct supertype *st,
        rv = 0;
 
 abort:
-       if (targets) {
-               close_targets(targets, new_disks);
-               free(targets);
-       }
-       free(target_offsets);
-
        return rv;
 }
 
@@ -10923,7 +11105,7 @@ int save_checkpoint_imsm(struct supertype *st, struct mdinfo *info, int state)
        unsigned long long blocks_per_unit;
        unsigned long long curr_migr_unit;
 
-       if (load_imsm_migr_rec(super, info) != 0) {
+       if (load_imsm_migr_rec(super) != 0) {
                dprintf("imsm: ERROR: Cannot read migration record for checkpoint save.\n");
                return 1;
        }
@@ -10974,17 +11156,17 @@ int recover_backup_imsm(struct supertype *st, struct mdinfo *info)
        unsigned long long read_offset;
        unsigned long long write_offset;
        unsigned unit_len;
-       int *targets = NULL;
-       int new_disks, i, err;
+       int new_disks, err;
        char *buf = NULL;
        int retval = 1;
        unsigned int sector_size = super->sector_size;
-       unsigned long curr_migr_unit = current_migr_unit(migr_rec);
-       unsigned long num_migr_units = get_num_migr_units(migr_rec);
-       char buffer[20];
+       unsigned long long curr_migr_unit = current_migr_unit(migr_rec);
+       unsigned long long num_migr_units = get_num_migr_units(migr_rec);
+       char buffer[SYSFS_MAX_BUF_SIZE];
        int skipped_disks = 0;
+       struct dl *dl_disk;
 
-       err = sysfs_get_str(info, NULL, "array_state", (char *)buffer, 20);
+       err = sysfs_get_str(info, NULL, "array_state", (char *)buffer, sizeof(buffer));
        if (err < 1)
                return 1;
 
@@ -11015,37 +11197,34 @@ int recover_backup_imsm(struct supertype *st, struct mdinfo *info)
        unit_len = __le32_to_cpu(migr_rec->dest_depth_per_unit) * 512;
        if (posix_memalign((void **)&buf, sector_size, unit_len) != 0)
                goto abort;
-       targets = xcalloc(new_disks, sizeof(int));
 
-       if (open_backup_targets(info, new_disks, targets, super, id->dev)) {
-               pr_err("Cannot open some devices belonging to array.\n");
-               goto abort;
-       }
+       for (dl_disk = super->disks; dl_disk; dl_disk = dl_disk->next) {
+               if (dl_disk->index < 0)
+                       continue;
 
-       for (i = 0; i < new_disks; i++) {
-               if (targets[i] < 0) {
+               if (!is_fd_valid(dl_disk->fd)) {
                        skipped_disks++;
                        continue;
                }
-               if (lseek64(targets[i], read_offset, SEEK_SET) < 0) {
+               if (lseek64(dl_disk->fd, read_offset, SEEK_SET) < 0) {
                        pr_err("Cannot seek to block: %s\n",
                               strerror(errno));
                        skipped_disks++;
                        continue;
                }
-               if ((unsigned)read(targets[i], buf, unit_len) != unit_len) {
+               if (read(dl_disk->fd, buf, unit_len) != (ssize_t)unit_len) {
                        pr_err("Cannot read copy area block: %s\n",
                               strerror(errno));
                        skipped_disks++;
                        continue;
                }
-               if (lseek64(targets[i], write_offset, SEEK_SET) < 0) {
+               if (lseek64(dl_disk->fd, write_offset, SEEK_SET) < 0) {
                        pr_err("Cannot seek to block: %s\n",
                               strerror(errno));
                        skipped_disks++;
                        continue;
                }
-               if ((unsigned)write(targets[i], buf, unit_len) != unit_len) {
+               if (write(dl_disk->fd, buf, unit_len) != (ssize_t)unit_len) {
                        pr_err("Cannot restore block: %s\n",
                               strerror(errno));
                        skipped_disks++;
@@ -11069,78 +11248,280 @@ int recover_backup_imsm(struct supertype *st, struct mdinfo *info)
                retval = 0;
 
 abort:
-       if (targets) {
-               for (i = 0; i < new_disks; i++)
-                       if (targets[i])
-                               close(targets[i]);
-               free(targets);
-       }
        free(buf);
        return retval;
 }
 
-static char disk_by_path[] = "/dev/disk/by-path/";
-
-static const char *imsm_get_disk_controller_domain(const char *path)
-{
-       char disk_path[PATH_MAX];
-       char *drv=NULL;
-       struct stat st;
-
-       strcpy(disk_path, disk_by_path);
-       strncat(disk_path, path, PATH_MAX - strlen(disk_path) - 1);
-       if (stat(disk_path, &st) == 0) {
-               struct sys_dev* hba;
-               char *path;
-
-               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 if (hba && hba->type == SYS_DEV_VMD)
-                       drv = "vmd";
-               else if (hba && hba->type == SYS_DEV_NVME)
-                       drv = "nvme";
-               else
-                       drv = "unknown";
-               dprintf("path: %s hba: %s attached: %s\n",
-                       path, (hba) ? hba->path : "NULL", drv);
-               free(path);
+/**
+ * test_and_add_drive_controller_policy_imsm() - add disk controller to policies list.
+ * @type: Policy type to search on list.
+ * @pols: List of currently recorded policies.
+ * @disk_fd: File descriptor of the device to check.
+ * @hba: The hba disk is attached, could be NULL if verification is disabled.
+ * @verbose: verbose flag.
+ *
+ * IMSM cares about drive physical placement. If @hba is not set, it adds unknown policy.
+ * If there is no controller policy on pols we are free to add first one. If there is a policy then,
+ * new must be the same - no controller mixing allowed.
+ */
+static mdadm_status_t
+test_and_add_drive_controller_policy_imsm(const char * const type, dev_policy_t **pols, int disk_fd,
+                                         struct sys_dev *hba, const int verbose)
+{
+       const char *controller_policy = get_sys_dev_type(SYS_DEV_UNKNOWN);
+       struct dev_policy *pol = pol_find(*pols, (char *)type);
+       char devname[MAX_RAID_SERIAL_LEN];
+
+       if (hba)
+               controller_policy = get_sys_dev_type(hba->type);
+
+       if (!pol) {
+               pol_add(pols, (char *)type, (char *)controller_policy, "imsm");
+               return MDADM_STATUS_SUCCESS;
        }
-       return drv;
-}
 
-static char *imsm_find_array_devnm_by_subdev(int subdev, char *container)
-{
-       static char devnm[32];
-       char subdev_name[20];
-       struct mdstat_ent *mdstat;
+       if (strcmp(pol->value, controller_policy) == 0)
+               return MDADM_STATUS_SUCCESS;
 
-       sprintf(subdev_name, "%d", subdev);
-       mdstat = mdstat_by_subdev(subdev_name, container);
-       if (!mdstat)
-               return NULL;
+       fd2devname(disk_fd, devname);
+       pr_vrb("Intel(R) raid controller \"%s\" found for %s, but \"%s\" was detected earlier\n",
+              controller_policy, devname, pol->value);
+       pr_vrb("Disks under different controllers cannot be used, aborting\n");
 
-       strcpy(devnm, mdstat->devnm);
-       free_mdstat(mdstat);
-       return devnm;
+       return MDADM_STATUS_ERROR;
 }
 
-static int imsm_reshape_is_allowed_on_container(struct supertype *st,
-                                               struct geo_params *geo,
-                                               int *old_raid_disks,
-                                               int direction)
+/**
+ * test_and_add_drive_encryption_policy_imsm() - add disk encryption to policies list.
+ * @type: policy type to search in the list.
+ * @pols: list of currently recorded policies.
+ * @disk_fd: file descriptor of the device to check.
+ * @hba: The hba to which the drive is attached, could be NULL if verification is disabled.
+ * @verbose: verbose flag.
+ *
+ * IMSM cares about drive encryption state. It is not allowed to mix disks with different
+ * encryption state within one md device.
+ * If there is no encryption policy on pols we are free to add first one.
+ * If there is a policy then, new must be the same.
+ */
+static mdadm_status_t
+test_and_add_drive_encryption_policy_imsm(const char * const type, dev_policy_t **pols, int disk_fd,
+                                         struct sys_dev *hba, const int verbose)
 {
-       /* currently we only support increasing the number of devices
-        * for a container.  This increases the number of device for each
-        * member array.  They must all be RAID0 or RAID5.
-        */
-       int ret_val = 0;
-       struct mdinfo *info, *member;
+       struct dev_policy *expected_policy = pol_find(*pols, (char *)type);
+       struct encryption_information information = {0};
+       char *encryption_state = "Unknown";
+       int status = MDADM_STATUS_SUCCESS;
+       bool encryption_checked = true;
+       char devname[PATH_MAX];
+
+       if (!hba)
+               goto check_policy;
+
+       switch (hba->type) {
+       case SYS_DEV_NVME:
+       case SYS_DEV_VMD:
+               status = get_nvme_opal_encryption_information(disk_fd, &information, verbose);
+               break;
+       case SYS_DEV_SATA:
+       case SYS_DEV_SATA_VMD:
+               status = get_ata_encryption_information(disk_fd, &information, verbose);
+               break;
+       default:
+               encryption_checked = false;
+       }
+
+       if (status) {
+               fd2devname(disk_fd, devname);
+               pr_vrb("Failed to read encryption information of device %s\n", devname);
+               return MDADM_STATUS_ERROR;
+       }
+
+       if (encryption_checked) {
+               if (information.status == ENC_STATUS_LOCKED) {
+                       fd2devname(disk_fd, devname);
+                       pr_vrb("Device %s is in Locked state, cannot use. Aborting.\n", devname);
+                       return MDADM_STATUS_ERROR;
+               }
+               encryption_state = (char *)get_encryption_status_string(information.status);
+       }
+
+check_policy:
+       if (expected_policy) {
+               if (strcmp(expected_policy->value, encryption_state) == 0)
+                       return MDADM_STATUS_SUCCESS;
+
+               fd2devname(disk_fd, devname);
+               pr_vrb("Encryption status \"%s\" detected for disk %s, but \"%s\" status was detected eariler.\n",
+                      encryption_state, devname, expected_policy->value);
+               pr_vrb("Disks with different encryption status cannot be used.\n");
+               return MDADM_STATUS_ERROR;
+       }
+
+       pol_add(pols, (char *)type, encryption_state, "imsm");
+
+       return MDADM_STATUS_SUCCESS;
+}
+
+struct imsm_drive_policy {
+       char *type;
+       mdadm_status_t (*test_and_add_drive_policy)(const char * const type,
+                                                   struct dev_policy **pols, int disk_fd,
+                                                   struct sys_dev *hba, const int verbose);
+};
+
+struct imsm_drive_policy imsm_policies[] = {
+       {"controller", test_and_add_drive_controller_policy_imsm},
+       {"encryption", test_and_add_drive_encryption_policy_imsm}
+};
+
+mdadm_status_t test_and_add_drive_policies_imsm(struct dev_policy **pols, int disk_fd,
+                                               const int verbose)
+{
+       struct imsm_drive_policy *imsm_pol;
+       struct sys_dev *hba = NULL;
+       char path[PATH_MAX];
+       mdadm_status_t ret;
+       unsigned int i;
+
+       /* If imsm platform verification is disabled, do not search for hba. */
+       if (check_no_platform() != 1) {
+               if (!diskfd_to_devpath(disk_fd, 1, path)) {
+                       pr_vrb("IMSM: Failed to retrieve device path by file descriptor.\n");
+                       return MDADM_STATUS_ERROR;
+               }
+
+               hba = find_disk_attached_hba(disk_fd, path);
+               if (!hba) {
+                       pr_vrb("IMSM: Failed to find hba for %s\n", path);
+                       return MDADM_STATUS_ERROR;
+               }
+       }
+
+       for (i = 0; i < ARRAY_SIZE(imsm_policies); i++) {
+               imsm_pol = &imsm_policies[i];
+
+               ret = imsm_pol->test_and_add_drive_policy(imsm_pol->type, pols, disk_fd, hba,
+                                                         verbose);
+               if (ret != MDADM_STATUS_SUCCESS)
+                       /* Inherit error code */
+                       return ret;
+       }
+
+       return MDADM_STATUS_SUCCESS;
+}
+
+/**
+ * get_spare_criteria_imsm() - set spare criteria.
+ * @st: supertype.
+ * @mddev_path: path to md device devnode, it must be container.
+ * @c: spare_criteria struct to fill, not NULL.
+ *
+ * If superblock is not loaded, use mddev_path to load_container. It must be given in this case.
+ * Filles size and sector size accordingly to superblock.
+ */
+mdadm_status_t get_spare_criteria_imsm(struct supertype *st, char *mddev_path,
+                                      struct spare_criteria *c)
+{
+       mdadm_status_t ret = MDADM_STATUS_ERROR;
+       bool free_superblock = false;
+       unsigned long long size = 0;
+       struct intel_super *super;
+       struct extent *e;
+       struct dl *dl;
+       int i;
+
+       /* If no superblock and no mddev_path, we cannot load superblock. */
+       assert(st->sb || mddev_path);
+
+       if (mddev_path) {
+               int fd = open(mddev_path, O_RDONLY);
+               mdadm_status_t rv;
+
+               if (!is_fd_valid(fd))
+                       return MDADM_STATUS_ERROR;
+
+               if (!st->sb) {
+                       if (load_container_imsm(st, fd, st->devnm)) {
+                               close(fd);
+                               return MDADM_STATUS_ERROR;
+                       }
+                       free_superblock = true;
+               }
+
+               rv = mddev_test_and_add_drive_policies(st, &c->pols, fd, 0);
+               close(fd);
+
+               if (rv != MDADM_STATUS_SUCCESS)
+                       goto out;
+       }
+
+       super = st->sb;
+
+       /* find first active disk in array */
+       dl = super->disks;
+       while (dl && (is_failed(&dl->disk) || dl->index == -1))
+               dl = dl->next;
+
+       if (!dl)
+               goto out;
+
+       /* find last lba used by subarrays */
+       e = get_extents(super, dl, 0);
+       if (!e)
+               goto out;
+
+       for (i = 0; e[i].size; i++)
+               continue;
+       if (i > 0)
+               size = e[i - 1].start + e[i - 1].size;
+       free(e);
+
+       /* add the amount of space needed for metadata */
+       size += imsm_min_reserved_sectors(super);
+
+       c->min_size = size * 512;
+       c->sector_size = super->sector_size;
+       c->criteria_set = true;
+       ret = MDADM_STATUS_SUCCESS;
+
+out:
+       if (free_superblock)
+               free_super_imsm(st);
+
+       if (ret != MDADM_STATUS_SUCCESS)
+               c->criteria_set = false;
+
+       return ret;
+}
+
+static char *imsm_find_array_devnm_by_subdev(int subdev, char *container)
+{
+       static char devnm[32];
+       char subdev_name[20];
+       struct mdstat_ent *mdstat;
+
+       sprintf(subdev_name, "%d", subdev);
+       mdstat = mdstat_by_subdev(subdev_name, container);
+       if (!mdstat)
+               return NULL;
+
+       strcpy(devnm, mdstat->devnm);
+       free_mdstat(mdstat);
+       return devnm;
+}
+
+static int imsm_reshape_is_allowed_on_container(struct supertype *st,
+                                               struct geo_params *geo,
+                                               int *old_raid_disks,
+                                               int direction)
+{
+       /* currently we only support increasing the number of devices
+        * for a container.  This increases the number of device for each
+        * member array.  They must all be RAID0 or RAID5.
+        */
+       int ret_val = 0;
+       struct mdinfo *info, *member;
        int devices_that_can_grow = 0;
 
        dprintf("imsm: imsm_reshape_is_allowed_on_container(ENTER): st->devnm = (%s)\n", st->devnm);
@@ -11242,10 +11623,15 @@ static int imsm_reshape_is_allowed_on_container(struct supertype *st,
  */
 static struct mdinfo *get_spares_for_grow(struct supertype *st)
 {
-       struct spare_criteria sc;
+       struct spare_criteria sc = {0};
+       struct mdinfo *spares;
 
-       get_spare_criteria_imsm(st, &sc);
-       return container_choose_spares(st, &sc, NULL, NULL, NULL, 0);
+       get_spare_criteria_imsm(st, NULL, &sc);
+       spares = container_choose_spares(st, &sc, NULL, NULL, NULL, 0);
+
+       dev_policy_free(sc.pols);
+
+       return spares;
 }
 
 /******************************************************************************
@@ -11372,8 +11758,10 @@ static int imsm_create_metadata_update_for_migration(
 {
        struct intel_super *super = st->sb;
        int update_memory_size;
+       int current_chunk_size;
        struct imsm_update_reshape_migration *u;
-       struct imsm_dev *dev;
+       struct imsm_dev *dev = get_imsm_dev(super, super->current_vol);
+       struct imsm_map *map = get_imsm_map(dev, MAP_0);
        int previous_level = -1;
 
        dprintf("(enter) New Level = %i\n", geo->level);
@@ -11390,23 +11778,15 @@ static int imsm_create_metadata_update_for_migration(
        u->new_disks[0] = -1;
        u->new_chunksize = -1;
 
-       dev = get_imsm_dev(super, u->subdev);
-       if (dev) {
-               struct imsm_map *map;
+       current_chunk_size = __le16_to_cpu(map->blocks_per_strip) / 2;
 
-               map = get_imsm_map(dev, MAP_0);
-               if (map) {
-                       int current_chunk_size =
-                               __le16_to_cpu(map->blocks_per_strip) / 2;
-
-                       if (geo->chunksize != current_chunk_size) {
-                               u->new_chunksize = geo->chunksize / 1024;
-                               dprintf("imsm: chunk size change from %i to %i\n",
-                                       current_chunk_size, u->new_chunksize);
-                       }
-                       previous_level = map->raid_level;
-               }
+       if (geo->chunksize != current_chunk_size) {
+               u->new_chunksize = geo->chunksize / 1024;
+               dprintf("imsm: chunk size change from %i to %i\n",
+                       current_chunk_size, u->new_chunksize);
        }
+       previous_level = map->raid_level;
+
        if (geo->level == 5 && previous_level == 0) {
                struct mdinfo *spares = NULL;
 
@@ -11447,6 +11827,96 @@ static void imsm_update_metadata_locally(struct supertype *st,
        }
 }
 
+/**
+ * imsm_analyze_expand() - check expand properties and calculate new size.
+ * @st: imsm supertype.
+ * @geo: new geometry params.
+ * @array: array info.
+ * @direction: reshape direction.
+ *
+ * Obtain free space after the &array and verify if expand to requested size is
+ * possible. If geo->size is set to %MAX_SIZE, assume that max free size is
+ * requested.
+ *
+ * Return:
+ * On success %IMSM_STATUS_OK is returned, geo->size and geo->raid_disks are
+ * updated.
+ * On error, %IMSM_STATUS_ERROR is returned.
+ */
+static imsm_status_t imsm_analyze_expand(struct supertype *st,
+                                        struct geo_params *geo,
+                                        struct mdinfo *array,
+                                        int direction)
+{
+       struct intel_super *super = st->sb;
+       struct imsm_dev *dev = get_imsm_dev(super, super->current_vol);
+       struct imsm_map *map = get_imsm_map(dev, MAP_0);
+       int data_disks = imsm_num_data_members(map);
+
+       unsigned long long current_size;
+       unsigned long long free_size;
+       unsigned long long new_size;
+       unsigned long long max_size;
+
+       const int chunk_kib = geo->chunksize / 1024;
+       imsm_status_t rv;
+
+       if (direction == ROLLBACK_METADATA_CHANGES) {
+               /**
+                * Accept size for rollback only.
+                */
+               new_size = geo->size * 2;
+               goto success;
+       }
+
+       if (data_disks == 0) {
+               pr_err("imsm: Cannot retrieve data disks.\n");
+               return IMSM_STATUS_ERROR;
+       }
+       current_size = array->custom_array_size / data_disks;
+
+       rv = imsm_get_free_size(super, dev->vol.map->num_members, 0, chunk_kib, &free_size, true);
+       if (rv != IMSM_STATUS_OK) {
+               pr_err("imsm: Cannot find free space for expand.\n");
+               return IMSM_STATUS_ERROR;
+       }
+       max_size = round_member_size_to_mb(free_size + current_size);
+
+       if (geo->size == MAX_SIZE)
+               new_size = max_size;
+       else
+               new_size = round_member_size_to_mb(geo->size * 2);
+
+       if (new_size == 0) {
+               pr_err("imsm: Rounded requested size is 0.\n");
+               return IMSM_STATUS_ERROR;
+       }
+
+       if (new_size > max_size) {
+               pr_err("imsm: Rounded requested size (%llu) is larger than free space available (%llu).\n",
+                      new_size, max_size);
+               return IMSM_STATUS_ERROR;
+       }
+
+       if (new_size == current_size) {
+               pr_err("imsm: Rounded requested size (%llu) is same as current size (%llu).\n",
+                      new_size, current_size);
+               return IMSM_STATUS_ERROR;
+       }
+
+       if (new_size < current_size) {
+               pr_err("imsm: Size reduction is not supported, rounded requested size (%llu) is smaller than current (%llu).\n",
+                      new_size, current_size);
+               return IMSM_STATUS_ERROR;
+       }
+
+success:
+       dprintf("imsm: New size per member is %llu.\n", new_size);
+       geo->size = data_disks * new_size;
+       geo->raid_disks = dev->vol.map->num_members;
+       return IMSM_STATUS_OK;
+}
+
 /***************************************************************************
 * Function:    imsm_analyze_change
 * Description: Function analyze change for single volume
@@ -11467,14 +11937,7 @@ enum imsm_reshape_type imsm_analyze_change(struct supertype *st,
        int devNumChange = 0;
        /* imsm compatible layout value for array geometry verification */
        int imsm_layout = -1;
-       int data_disks;
-       struct imsm_dev *dev;
-       struct imsm_map *map;
-       struct intel_super *super;
-       unsigned long long current_size;
-       unsigned long long free_size;
-       unsigned long long max_size;
-       int rv;
+       imsm_status_t rv;
 
        getinfo_super_imsm_volume(st, &info, NULL);
        if (geo->level != info.array.level && geo->level >= 0 &&
@@ -11556,94 +12019,20 @@ enum imsm_reshape_type imsm_analyze_change(struct supertype *st,
                geo->chunksize = info.array.chunk_size;
        }
 
-       chunk = geo->chunksize / 1024;
-
-       super = st->sb;
-       dev = get_imsm_dev(super, super->current_vol);
-       map = get_imsm_map(dev, MAP_0);
-       data_disks = imsm_num_data_members(map);
-       /* compute current size per disk member
-        */
-       current_size = info.custom_array_size / data_disks;
-
-       if (geo->size > 0 && geo->size != MAX_SIZE) {
-               /* align component size
-                */
-               geo->size = imsm_component_size_alignment_check(
-                                   get_imsm_raid_level(dev->vol.map),
-                                   chunk * 1024, super->sector_size,
-                                   geo->size * 2);
-               if (geo->size == 0) {
-                       pr_err("Error. Size expansion is supported only (current size is %llu, requested size /rounded/ is 0).\n",
-                                  current_size);
-                       goto analyse_change_exit;
-               }
-       }
-
-       if (current_size != geo->size && geo->size > 0) {
+       if (geo->size > 0) {
                if (change != -1) {
                        pr_err("Error. Size change should be the only one at a time.\n");
                        change = -1;
                        goto analyse_change_exit;
                }
-               if ((super->current_vol + 1) != super->anchor->num_raid_devs) {
-                       pr_err("Error. The last volume in container can be expanded only (%i/%s).\n",
-                              super->current_vol, st->devnm);
-                       goto analyse_change_exit;
-               }
-               /* check the maximum available size
-                */
-               rv =  imsm_get_free_size(st, dev->vol.map->num_members,
-                                        0, chunk, &free_size);
-               if (rv == 0)
-                       /* Cannot find maximum available space
-                        */
-                       max_size = 0;
-               else {
-                       max_size = free_size + current_size;
-                       /* align component size
-                        */
-                       max_size = imsm_component_size_alignment_check(
-                                       get_imsm_raid_level(dev->vol.map),
-                                       chunk * 1024, super->sector_size,
-                                       max_size);
-               }
-               if (geo->size == MAX_SIZE) {
-                       /* requested size change to the maximum available size
-                        */
-                       if (max_size == 0) {
-                               pr_err("Error. Cannot find maximum available space.\n");
-                               change = -1;
-                               goto analyse_change_exit;
-                       } else
-                               geo->size = max_size;
-               }
 
-               if (direction == ROLLBACK_METADATA_CHANGES) {
-                       /* accept size for rollback only
-                       */
-               } else {
-                       /* round size due to metadata compatibility
-                       */
-                       geo->size = (geo->size >> SECT_PER_MB_SHIFT)
-                                   << SECT_PER_MB_SHIFT;
-                       dprintf("Prepare update for size change to %llu\n",
-                               geo->size );
-                       if (current_size >= geo->size) {
-                               pr_err("Error. Size expansion is supported only (current size is %llu, requested size /rounded/ is %llu).\n",
-                                      current_size, geo->size);
-                               goto analyse_change_exit;
-                       }
-                       if (max_size && geo->size > max_size) {
-                               pr_err("Error. Requested size is larger than maximum available size (maximum available size is %llu, requested size /rounded/ is %llu).\n",
-                                      max_size, geo->size);
-                               goto analyse_change_exit;
-                       }
-               }
-               geo->size *= data_disks;
-               geo->raid_disks = dev->vol.map->num_members;
+               rv = imsm_analyze_expand(st, geo, &info, direction);
+               if (rv != IMSM_STATUS_OK)
+                       goto analyse_change_exit;
                change = CH_ARRAY_SIZE;
        }
+
+       chunk = geo->chunksize / 1024;
        if (!validate_geometry_imsm(st,
                                    geo->level,
                                    imsm_layout,
@@ -11658,8 +12047,8 @@ enum imsm_reshape_type imsm_analyze_change(struct supertype *st,
                struct imsm_super *mpb = super->anchor;
 
                if (mpb->num_raid_devs > 1) {
-                       pr_err("Error. Cannot perform operation on %s- for this operation it MUST be single array in container\n",
-                              geo->dev_name);
+                       pr_err("Error. Cannot perform operation on %s- for this operation "
+                              "it MUST be single array in container\n", geo->dev_name);
                        change = -1;
                }
        }
@@ -11704,6 +12093,65 @@ int imsm_takeover(struct supertype *st, struct geo_params *geo)
        return 0;
 }
 
+/* Flush size update if size calculated by num_data_stripes is higher than
+ * imsm_dev_size to eliminate differences during reshape.
+ * Mdmon will recalculate them correctly.
+ * If subarray index is not set then check whole container.
+ * Returns:
+ *     0 - no error occurred
+ *     1 - error detected
+ */
+static int imsm_fix_size_mismatch(struct supertype *st, int subarray_index)
+{
+       struct intel_super *super = st->sb;
+       int tmp = super->current_vol;
+       int ret_val = 1;
+       int i;
+
+       for (i = 0; i < super->anchor->num_raid_devs; i++) {
+               if (subarray_index >= 0 && i != subarray_index)
+                       continue;
+               super->current_vol = i;
+               struct imsm_dev *dev = get_imsm_dev(super, super->current_vol);
+               struct imsm_map *map = get_imsm_map(dev, MAP_0);
+               unsigned int disc_count = imsm_num_data_members(map);
+               struct geo_params geo;
+               struct imsm_update_size_change *update;
+               unsigned long long calc_size = per_dev_array_size(map) * disc_count;
+               unsigned long long d_size = imsm_dev_size(dev);
+               int u_size;
+
+               if (calc_size == d_size)
+                       continue;
+
+               /* There is a difference, confirm that imsm_dev_size is
+                * smaller and push update.
+                */
+               if (d_size > calc_size) {
+                       pr_err("imsm: dev size of subarray %d is incorrect\n",
+                               i);
+                       goto exit;
+               }
+               memset(&geo, 0, sizeof(struct geo_params));
+               geo.size = d_size;
+               u_size = imsm_create_metadata_update_for_size_change(st, &geo,
+                                                                    &update);
+               imsm_update_metadata_locally(st, update, u_size);
+               if (st->update_tail) {
+                       append_metadata_update(st, update, u_size);
+                       flush_metadata_updates(st);
+                       st->update_tail = &st->updates;
+               } else {
+                       imsm_sync_metadata(st);
+                       free(update);
+               }
+       }
+       ret_val = 0;
+exit:
+       super->current_vol = tmp;
+       return ret_val;
+}
+
 static int imsm_reshape_super(struct supertype *st, unsigned long long size,
                              int level,
                              int layout, int chunksize, int raid_disks,
@@ -11740,6 +12188,11 @@ static int imsm_reshape_super(struct supertype *st, unsigned long long size,
                        struct imsm_update_reshape *u = NULL;
                        int len;
 
+                       if (imsm_fix_size_mismatch(st, -1)) {
+                               dprintf("imsm: Cannot fix size mismatch\n");
+                               goto exit_imsm_reshape_super;
+                       }
+
                        len = imsm_create_metadata_update_for_reshape(
                                st, &geo, old_raid_disks, &u);
 
@@ -11845,14 +12298,14 @@ exit_imsm_reshape_super:
 static int read_completed(int fd, unsigned long long *val)
 {
        int ret;
-       char buf[50];
+       char buf[SYSFS_MAX_BUF_SIZE];
 
-       ret = sysfs_fd_get_str(fd, buf, 50);
+       ret = sysfs_fd_get_str(fd, buf, sizeof(buf));
        if (ret < 0)
                return ret;
 
        ret = COMPLETED_OK;
-       if (strncmp(buf, "none", 4) == 0) {
+       if (str_is_none(buf) == true) {
                ret = COMPLETED_NONE;
        } else if (strncmp(buf, "delayed", 7) == 0) {
                ret = COMPLETED_DELAYED;
@@ -11886,7 +12339,7 @@ int wait_for_reshape_imsm(struct mdinfo *sra, int ndata)
        unsigned long long to_complete = sra->reshape_progress;
        unsigned long long position_to_set = to_complete / ndata;
 
-       if (fd < 0) {
+       if (!is_fd_valid(fd)) {
                dprintf("cannot open reshape_position\n");
                return 1;
        }
@@ -11898,7 +12351,7 @@ int wait_for_reshape_imsm(struct mdinfo *sra, int ndata)
                                close(fd);
                                return 1;
                        }
-                       usleep(30000);
+                       sleep_for(0, MSEC_TO_NSEC(30), true);
                } else
                        break;
        } while (retry--);
@@ -11920,12 +12373,12 @@ int wait_for_reshape_imsm(struct mdinfo *sra, int ndata)
 
        do {
                int rc;
-               char action[20];
+               char action[SYSFS_MAX_BUF_SIZE];
                int timeout = 3000;
 
                sysfs_wait(fd, &timeout);
                if (sysfs_get_str(sra, NULL, "sync_action",
-                                 action, 20) > 0 &&
+                                 action, sizeof(action)) > 0 &&
                                strncmp(action, "reshape", 7) != 0) {
                        if (strncmp(action, "idle", 4) == 0)
                                break;
@@ -11972,7 +12425,8 @@ int check_degradation_change(struct mdinfo *info,
                        if (sd->disk.state & (1<<MD_DISK_FAULTY))
                                continue;
                        if (sd->disk.state & (1<<MD_DISK_SYNC)) {
-                               char sbuf[100];
+                               char sbuf[SYSFS_MAX_BUF_SIZE];
+                               int raid_disk = sd->disk.raid_disk;
 
                                if (sysfs_get_str(info,
                                        sd, "state", sbuf, sizeof(sbuf)) < 0 ||
@@ -11980,13 +12434,8 @@ int check_degradation_change(struct mdinfo *info,
                                        strstr(sbuf, "in_sync") == NULL) {
                                        /* this device is dead */
                                        sd->disk.state = (1<<MD_DISK_FAULTY);
-                                       if (sd->disk.raid_disk >= 0 &&
-                                           sources[sd->disk.raid_disk] >= 0) {
-                                               close(sources[
-                                                       sd->disk.raid_disk]);
-                                               sources[sd->disk.raid_disk] =
-                                                       -1;
-                                       }
+                                       if (raid_disk >= 0)
+                                               close_fd(&sources[raid_disk]);
                                        new_degraded++;
                                }
                        }
@@ -12042,6 +12491,7 @@ static int imsm_manage_reshape(
        unsigned long long start_buf_shift; /* [bytes] */
        int degraded = 0;
        int source_layout = 0;
+       int subarray_index = -1;
 
        if (!sra)
                return ret_val;
@@ -12055,6 +12505,7 @@ static int imsm_manage_reshape(
                    dv->dev->vol.migr_state == 1) {
                        dev = dv->dev;
                        migr_vol_qan++;
+                       subarray_index = dv->index;
                }
        }
        /* Only one volume can migrate at the same time */
@@ -12239,6 +12690,14 @@ static int imsm_manage_reshape(
 
        /* return '1' if done */
        ret_val = 1;
+
+       /* After the reshape eliminate size mismatch in metadata.
+        * Don't update md/component_size here, volume hasn't
+        * to take whole space. It is allowed by kernel.
+        * md/component_size will be set propoperly after next assembly.
+        */
+       imsm_fix_size_mismatch(st, subarray_index);
+
 abort:
        free(buf);
        /* See Grow.c: abort_reshape() for further explanation */
@@ -12249,6 +12708,474 @@ abort:
        return ret_val;
 }
 
+/*******************************************************************************
+ * Function:   calculate_bitmap_min_chunksize
+ * Description:        Calculates the minimal valid bitmap chunk size
+ * Parameters:
+ *     max_bits        : indicate how many bits can be used for the bitmap
+ *     data_area_size  : the size of the data area covered by the bitmap
+ *
+ * Returns:
+ *      The bitmap chunk size
+ ******************************************************************************/
+static unsigned long long
+calculate_bitmap_min_chunksize(unsigned long long max_bits,
+                              unsigned long long data_area_size)
+{
+       unsigned long long min_chunk =
+               4096; /* sub-page chunks don't work yet.. */
+       unsigned long long bits = data_area_size / min_chunk + 1;
+
+       while (bits > max_bits) {
+               min_chunk *= 2;
+               bits = (bits + 1) / 2;
+       }
+       return min_chunk;
+}
+
+/*******************************************************************************
+ * Function:   calculate_bitmap_chunksize
+ * Description:        Calculates the bitmap chunk size for the given device
+ * Parameters:
+ *     st      : supertype information
+ *     dev     : device for the bitmap
+ *
+ * Returns:
+ *      The bitmap chunk size
+ ******************************************************************************/
+static unsigned long long calculate_bitmap_chunksize(struct supertype *st,
+                                                    struct imsm_dev *dev)
+{
+       struct intel_super *super = st->sb;
+       unsigned long long min_chunksize;
+       unsigned long long result = IMSM_DEFAULT_BITMAP_CHUNKSIZE;
+       size_t dev_size = imsm_dev_size(dev);
+
+       min_chunksize = calculate_bitmap_min_chunksize(
+               IMSM_BITMAP_AREA_SIZE * super->sector_size, dev_size);
+
+       if (result < min_chunksize)
+               result = min_chunksize;
+
+       return result;
+}
+
+/*******************************************************************************
+ * Function:   init_bitmap_header
+ * Description:        Initialize the bitmap header structure
+ * Parameters:
+ *     st      : supertype information
+ *     bms     : bitmap header struct to initialize
+ *     dev     : device for the bitmap
+ *
+ * Returns:
+ *      0 : success
+ *     -1 : fail
+ ******************************************************************************/
+static int init_bitmap_header(struct supertype *st, struct bitmap_super_s *bms,
+                             struct imsm_dev *dev)
+{
+       int vol_uuid[4];
+
+       if (!bms || !dev)
+               return -1;
+
+       bms->magic = __cpu_to_le32(BITMAP_MAGIC);
+       bms->version = __cpu_to_le32(BITMAP_MAJOR_HI);
+       bms->daemon_sleep = __cpu_to_le32(IMSM_DEFAULT_BITMAP_DAEMON_SLEEP);
+       bms->sync_size = __cpu_to_le64(IMSM_BITMAP_AREA_SIZE);
+       bms->write_behind = __cpu_to_le32(0);
+
+       uuid_from_super_imsm(st, vol_uuid);
+       memcpy(bms->uuid, vol_uuid, 16);
+
+       bms->chunksize = calculate_bitmap_chunksize(st, dev);
+
+       return 0;
+}
+
+/*******************************************************************************
+ * Function:   validate_internal_bitmap_for_drive
+ * Description:        Verify if the bitmap header for a given drive.
+ * Parameters:
+ *     st      : supertype information
+ *     offset  : The offset from the beginning of the drive where to look for
+ *               the bitmap header.
+ *     d       : the drive info
+ *
+ * Returns:
+ *      0 : success
+ *     -1 : fail
+ ******************************************************************************/
+static int validate_internal_bitmap_for_drive(struct supertype *st,
+                                             unsigned long long offset,
+                                             struct dl *d)
+{
+       struct intel_super *super = st->sb;
+       int ret = -1;
+       int vol_uuid[4];
+       bitmap_super_t *bms;
+       int fd;
+
+       if (!d)
+               return -1;
+
+       void *read_buf;
+
+       if (posix_memalign(&read_buf, MAX_SECTOR_SIZE, IMSM_BITMAP_HEADER_SIZE))
+               return -1;
+
+       fd = d->fd;
+       if (!is_fd_valid(fd)) {
+               fd = open(d->devname, O_RDONLY, 0);
+
+               if (!is_fd_valid(fd)) {
+                       dprintf("cannot open the device %s\n", d->devname);
+                       goto abort;
+               }
+       }
+
+       if (lseek64(fd, offset * super->sector_size, SEEK_SET) < 0)
+               goto abort;
+       if (read(fd, read_buf, IMSM_BITMAP_HEADER_SIZE) !=
+           IMSM_BITMAP_HEADER_SIZE)
+               goto abort;
+
+       uuid_from_super_imsm(st, vol_uuid);
+
+       bms = read_buf;
+       if ((bms->magic != __cpu_to_le32(BITMAP_MAGIC)) ||
+           (bms->version != __cpu_to_le32(BITMAP_MAJOR_HI)) ||
+           (!same_uuid((int *)bms->uuid, vol_uuid, st->ss->swapuuid))) {
+               dprintf("wrong bitmap header detected\n");
+               goto abort;
+       }
+
+       ret = 0;
+abort:
+       if (!is_fd_valid(d->fd))
+               close_fd(&fd);
+
+       if (read_buf)
+               free(read_buf);
+
+       return ret;
+}
+
+/*******************************************************************************
+ * Function:   validate_internal_bitmap_imsm
+ * Description:        Verify if the bitmap header is in place and with proper data.
+ * Parameters:
+ *     st      : supertype information
+ *
+ * Returns:
+ *      0 : success or device w/o RWH_BITMAP
+ *     -1 : fail
+ ******************************************************************************/
+static int validate_internal_bitmap_imsm(struct supertype *st)
+{
+       struct intel_super *super = st->sb;
+       struct imsm_dev *dev = get_imsm_dev(super, super->current_vol);
+       unsigned long long offset;
+       struct dl *d;
+
+       if (dev->rwh_policy != RWH_BITMAP)
+               return 0;
+
+       offset = get_bitmap_header_sector(super, super->current_vol);
+       for (d = super->disks; d; d = d->next) {
+               if (d->index < 0 || is_failed(&d->disk))
+                       continue;
+
+               if (validate_internal_bitmap_for_drive(st, offset, d)) {
+                       pr_err("imsm: bitmap validation failed\n");
+                       return -1;
+               }
+       }
+       return 0;
+}
+
+/*******************************************************************************
+ * Function:   add_internal_bitmap_imsm
+ * Description:        Mark the volume to use the bitmap and updates the chunk size value.
+ * Parameters:
+ *     st              : supertype information
+ *     chunkp          : bitmap chunk size
+ *     delay           : not used for imsm
+ *     write_behind    : not used for imsm
+ *     size            : not used for imsm
+ *     may_change      : not used for imsm
+ *     amajor          : not used for imsm
+ *
+ * Returns:
+ *      0 : success
+ *     -1 : fail
+ ******************************************************************************/
+static int add_internal_bitmap_imsm(struct supertype *st, int *chunkp,
+                                   int delay, int write_behind,
+                                   unsigned long long size, int may_change,
+                                   int amajor)
+{
+       struct intel_super *super = st->sb;
+       int vol_idx = super->current_vol;
+       struct imsm_dev *dev;
+
+       if (!super->devlist || vol_idx == -1 || !chunkp)
+               return -1;
+
+       dev = get_imsm_dev(super, vol_idx);
+       dev->rwh_policy = RWH_BITMAP;
+       *chunkp = calculate_bitmap_chunksize(st, dev);
+       return 0;
+}
+
+/*******************************************************************************
+ * Function:   locate_bitmap_imsm
+ * Description:        Seek 'fd' to start of write-intent-bitmap.
+ * Parameters:
+ *     st              : supertype information
+ *     fd              : file descriptor for the device
+ *     node_num        : not used for imsm
+ *
+ * Returns:
+ *      0 : success
+ *     -1 : fail
+ ******************************************************************************/
+static int locate_bitmap_imsm(struct supertype *st, int fd, int node_num)
+{
+       struct intel_super *super = st->sb;
+       unsigned long long offset;
+       int vol_idx = super->current_vol;
+
+       if (!super->devlist || vol_idx == -1)
+               return -1;
+
+       offset = get_bitmap_header_sector(super, super->current_vol);
+       dprintf("bitmap header offset is %llu\n", offset);
+
+       lseek64(fd, offset << 9, 0);
+
+       return 0;
+}
+
+/*******************************************************************************
+ * Function:   write_init_bitmap_imsm
+ * Description:        Write a bitmap header and prepares the area for the bitmap.
+ * Parameters:
+ *     st      : supertype information
+ *     fd      : file descriptor for the device
+ *     update  : not used for imsm
+ *
+ * Returns:
+ *      0 : success
+ *     -1 : fail
+ ******************************************************************************/
+static int write_init_bitmap_imsm(struct supertype *st, int fd,
+                                 enum bitmap_update update)
+{
+       struct intel_super *super = st->sb;
+       int vol_idx = super->current_vol;
+       int ret = 0;
+       unsigned long long offset;
+       bitmap_super_t bms = { 0 };
+       size_t written = 0;
+       size_t to_write;
+       ssize_t rv_num;
+       void *buf;
+
+       if (!super->devlist || !super->sector_size || vol_idx == -1)
+               return -1;
+
+       struct imsm_dev *dev = get_imsm_dev(super, vol_idx);
+
+       /* first clear the space for bitmap header */
+       unsigned long long bitmap_area_start =
+               get_bitmap_header_sector(super, vol_idx);
+
+       dprintf("zeroing area start (%llu) and size (%u)\n", bitmap_area_start,
+               IMSM_BITMAP_AND_HEADER_SIZE / super->sector_size);
+       if (zero_disk_range(fd, bitmap_area_start,
+                           IMSM_BITMAP_HEADER_SIZE / super->sector_size)) {
+               pr_err("imsm: cannot zeroing the space for the bitmap\n");
+               return -1;
+       }
+
+       /* The bitmap area should be filled with "1"s to perform initial
+        * synchronization.
+        */
+       if (posix_memalign(&buf, MAX_SECTOR_SIZE, MAX_SECTOR_SIZE))
+               return -1;
+       memset(buf, 0xFF, MAX_SECTOR_SIZE);
+       offset = get_bitmap_sector(super, vol_idx);
+       lseek64(fd, offset << 9, 0);
+       while (written < IMSM_BITMAP_AREA_SIZE) {
+               to_write = IMSM_BITMAP_AREA_SIZE - written;
+               if (to_write > MAX_SECTOR_SIZE)
+                       to_write = MAX_SECTOR_SIZE;
+               rv_num = write(fd, buf, MAX_SECTOR_SIZE);
+               if (rv_num != MAX_SECTOR_SIZE) {
+                       ret = -1;
+                       dprintf("cannot initialize bitmap area\n");
+                       goto abort;
+               }
+               written += rv_num;
+       }
+
+       /* write a bitmap header */
+       init_bitmap_header(st, &bms, dev);
+       memset(buf, 0, MAX_SECTOR_SIZE);
+       memcpy(buf, &bms, sizeof(bitmap_super_t));
+       if (locate_bitmap_imsm(st, fd, 0)) {
+               ret = -1;
+               dprintf("cannot locate the bitmap\n");
+               goto abort;
+       }
+       if (write(fd, buf, MAX_SECTOR_SIZE) != MAX_SECTOR_SIZE) {
+               ret = -1;
+               dprintf("cannot write the bitmap header\n");
+               goto abort;
+       }
+       fsync(fd);
+
+abort:
+       free(buf);
+
+       return ret;
+}
+
+/*******************************************************************************
+ * Function:   is_vol_to_setup_bitmap
+ * Description:        Checks if a bitmap should be activated on the dev.
+ * Parameters:
+ *     info    : info about the volume to setup the bitmap
+ *     dev     : the device to check against bitmap creation
+ *
+ * Returns:
+ *      0 : bitmap should be set up on the device
+ *     -1 : otherwise
+ ******************************************************************************/
+static int is_vol_to_setup_bitmap(struct mdinfo *info, struct imsm_dev *dev)
+{
+       if (!dev || !info)
+               return -1;
+
+       if ((strcmp((char *)dev->volume, info->name) == 0) &&
+           (dev->rwh_policy == RWH_BITMAP))
+               return -1;
+
+       return 0;
+}
+
+/*******************************************************************************
+ * Function:   set_bitmap_sysfs
+ * Description:        Set the sysfs atributes of a given volume to activate the bitmap.
+ * Parameters:
+ *     info            : info about the volume where the bitmap should be setup
+ *     chunksize       : bitmap chunk size
+ *     location        : location of the bitmap
+ *
+ * Returns:
+ *      0 : success
+ *     -1 : fail
+ ******************************************************************************/
+static int set_bitmap_sysfs(struct mdinfo *info, unsigned long long chunksize,
+                           char *location)
+{
+       /* The bitmap/metadata is set to external to allow changing of value for
+        * bitmap/location. When external is used, the kernel will treat an offset
+        * related to the device's first lba (in opposition to the "internal" case
+        * when this value is related to the beginning of the superblock).
+        */
+       if (sysfs_set_str(info, NULL, "bitmap/metadata", "external")) {
+               dprintf("failed to set bitmap/metadata\n");
+               return -1;
+       }
+
+       /* It can only be changed when no bitmap is active.
+        * Should be bigger than 512 and must be power of 2.
+        * It is expecting the value in bytes.
+        */
+       if (sysfs_set_num(info, NULL, "bitmap/chunksize",
+                                         __cpu_to_le32(chunksize))) {
+               dprintf("failed to set bitmap/chunksize\n");
+               return -1;
+       }
+
+       /* It is expecting the value in sectors. */
+       if (sysfs_set_num(info, NULL, "bitmap/space",
+                                         __cpu_to_le64(IMSM_BITMAP_AREA_SIZE))) {
+               dprintf("failed to set bitmap/space\n");
+               return -1;
+       }
+
+       /* Determines the delay between the bitmap updates.
+        * It is expecting the value in seconds.
+        */
+       if (sysfs_set_num(info, NULL, "bitmap/time_base",
+                                         __cpu_to_le64(IMSM_DEFAULT_BITMAP_DAEMON_SLEEP))) {
+               dprintf("failed to set bitmap/time_base\n");
+               return -1;
+       }
+
+       /* It is expecting the value in sectors with a sign at the beginning. */
+       if (sysfs_set_str(info, NULL, "bitmap/location", location)) {
+               dprintf("failed to set bitmap/location\n");
+               return -1;
+       }
+
+       return 0;
+}
+
+/*******************************************************************************
+ * Function:   set_bitmap_imsm
+ * Description:        Setup the bitmap for the given volume
+ * Parameters:
+ *     st      : supertype information
+ *     info    : info about the volume where the bitmap should be setup
+ *
+ * Returns:
+ *      0 : success
+ *     -1 : fail
+ ******************************************************************************/
+static int set_bitmap_imsm(struct supertype *st, struct mdinfo *info)
+{
+       struct intel_super *super = st->sb;
+       int prev_current_vol = super->current_vol;
+       struct imsm_dev *dev;
+       int ret = -1;
+       char location[16] = "";
+       unsigned long long chunksize;
+       struct intel_dev *dev_it;
+
+       for (dev_it = super->devlist; dev_it; dev_it = dev_it->next) {
+               super->current_vol = dev_it->index;
+               dev = get_imsm_dev(super, super->current_vol);
+
+               if (is_vol_to_setup_bitmap(info, dev)) {
+                       if (validate_internal_bitmap_imsm(st)) {
+                               dprintf("bitmap header validation failed\n");
+                               goto abort;
+                       }
+
+                       chunksize = calculate_bitmap_chunksize(st, dev);
+                       dprintf("chunk size is %llu\n", chunksize);
+
+                       snprintf(location, sizeof(location), "+%llu",
+                                get_bitmap_sector(super, super->current_vol));
+                       dprintf("bitmap offset is %s\n", location);
+
+                       if (set_bitmap_sysfs(info, chunksize, location)) {
+                               dprintf("cannot setup the bitmap\n");
+                               goto abort;
+                       }
+               }
+       }
+       ret = 0;
+abort:
+       super->current_vol = prev_current_vol;
+       return ret;
+}
+
 struct superswitch super_imsm = {
        .examine_super  = examine_super_imsm,
        .brief_examine_super = brief_examine_super_imsm,
@@ -12266,11 +13193,10 @@ struct superswitch super_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,
+       .test_and_add_drive_policies = test_and_add_drive_policies_imsm,
        .reshape_super  = imsm_reshape_super,
        .manage_reshape = imsm_manage_reshape,
        .recover_backup = recover_backup_imsm,
-       .copy_metadata = copy_metadata_imsm,
        .examine_badblocks = examine_badblocks_imsm,
        .match_home     = match_home_imsm,
        .uuid_from_super= uuid_from_super_imsm,
@@ -12291,10 +13217,16 @@ struct superswitch super_imsm = {
        .container_content = container_content_imsm,
        .validate_container = validate_container_imsm,
 
+       .add_internal_bitmap = add_internal_bitmap_imsm,
+       .locate_bitmap = locate_bitmap_imsm,
+       .write_bitmap = write_init_bitmap_imsm,
+       .set_bitmap = set_bitmap_imsm,
+
        .write_init_ppl = write_init_ppl_imsm,
        .validate_ppl   = validate_ppl_imsm,
 
        .external       = 1,
+       .swapuuid       = 0,
        .name = "imsm",
 
 /* for mdmon */