]> git.ipfire.org Git - thirdparty/mdadm.git/blobdiff - super-intel.c
mdadm: define DEV_MD_DIR
[thirdparty/mdadm.git] / super-intel.c
index ce08af4714d187e5f485a4a48d8309d0923ae643..aaf6659ef505f63472f4fdc3b78a18f2b8a6ec7f 100644 (file)
@@ -20,6 +20,7 @@
 #define HAVE_STDINT_H 1
 #include "mdadm.h"
 #include "mdmon.h"
+#include "dlink.h"
 #include "sha1.h"
 #include "platform-intel.h"
 #include <values.h>
                                                   * 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
+ */
+#define ASSERT_SIZE(_struct, size) \
+static inline void __assert_size_##_struct(void)       \
+{                                                      \
+       switch (0) {                                    \
+       case 0: break;                                  \
+       case (sizeof(struct _struct) == size): break;   \
+       }                                               \
+}
+
 /* Disk configuration info. */
 #define IMSM_MAX_DEVICES 255
 struct imsm_disk {
@@ -112,6 +143,7 @@ struct imsm_disk {
 #define        IMSM_DISK_FILLERS       3
        __u32 filler[IMSM_DISK_FILLERS]; /* 0xF5 - 0x107 MPB_DISK_FILLERS for future expansion */
 };
+ASSERT_SIZE(imsm_disk, 48)
 
 /* map selector for map managment
  */
@@ -146,10 +178,11 @@ struct imsm_map {
        __u32 disk_ord_tbl[1];  /* disk_ord_tbl[num_members],
                                 * top byte contains some flags
                                 */
-} __attribute__ ((packed));
+};
+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
@@ -166,10 +199,12 @@ 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 */
-} __attribute__ ((packed));
+};
+ASSERT_SIZE(imsm_vol, 84)
 
 struct imsm_dev {
        __u8  volume[MAX_RAID_SERIAL_LEN];
@@ -213,6 +248,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;
@@ -220,7 +256,8 @@ struct imsm_dev {
 #define IMSM_DEV_FILLERS 3
        __u32 filler[IMSM_DEV_FILLERS];
        struct imsm_vol vol;
-} __attribute__ ((packed));
+};
+ASSERT_SIZE(imsm_dev, 164)
 
 struct imsm_super {
        __u8 sig[MAX_SIGNATURE_LENGTH]; /* 0x00 - 0x1F */
@@ -243,12 +280,14 @@ 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 */
-} __attribute__ ((packed));
+};
+ASSERT_SIZE(imsm_super, 264)
 
 #define BBM_LOG_MAX_ENTRIES 254
 #define BBM_LOG_MAX_LBA_ENTRY_VAL 256          /* Represents 256 LBAs */
@@ -269,7 +308,8 @@ struct bbm_log {
        __u32 signature; /* 0xABADB10C */
        __u32 entry_count;
        struct bbm_log_entry marked_block_entries[BBM_LOG_MAX_ENTRIES];
-} __attribute__ ((__packed__));
+};
+ASSERT_SIZE(bbm_log, 2040)
 
 static char *map_state_str[] = { "normal", "uninitialized", "degraded", "failed" };
 
@@ -296,7 +336,7 @@ struct migr_record {
        __u32 rec_status;           /* Status used to determine how to restart
                                     * migration in case it aborts
                                     * in some fashion */
-       __u32 curr_migr_unit;       /* 0..numMigrUnits-1 */
+       __u32 curr_migr_unit_lo;    /* 0..numMigrUnits-1 */
        __u32 family_num;           /* Family number of MPB
                                     * containing the RaidDev
                                     * that is migrating */
@@ -306,17 +346,38 @@ struct migr_record {
        __u32 dest_depth_per_unit;  /* Num member blocks each destMap
                                     * member disk
                                     * advances per unit-of-operation */
-       __u32 ckpt_area_pba;        /* Pba of first block of ckpt copy area */
-       __u32 dest_1st_member_lba;  /* First member lba on first
-                                    * stripe of destination */
-       __u32 num_migr_units;       /* Total num migration units-of-op */
+       __u32 ckpt_area_pba_lo;     /* Pba of first block of ckpt copy area */
+       __u32 dest_1st_member_lba_lo;   /* First member lba on first
+                                        * stripe of destination */
+       __u32 num_migr_units_lo;    /* Total num migration units-of-op */
        __u32 post_migr_vol_cap;    /* Size of volume after
                                     * migration completes */
        __u32 post_migr_vol_cap_hi; /* Expansion space for LBA64 */
        __u32 ckpt_read_disk_num;   /* Which member disk in destSubMap[0] the
                                     * migration ckpt record was read from
                                     * (for recovered migrations) */
-} __attribute__ ((__packed__));
+       __u32 curr_migr_unit_hi;    /* 0..numMigrUnits-1 high order 32 bits */
+       __u32 ckpt_area_pba_hi;     /* Pba of first block of ckpt copy area
+                                    * high order 32 bits */
+       __u32 dest_1st_member_lba_hi; /* First member lba on first stripe of
+                                      * 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, 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:
@@ -523,7 +584,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 {
@@ -569,6 +630,44 @@ static const char *_sys_dev_type[] = {
        [SYS_DEV_VMD] = "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;
+
+               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)
@@ -643,22 +742,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,
@@ -803,6 +902,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;
@@ -810,30 +924,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
@@ -1103,7 +1234,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;
@@ -1114,7 +1245,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)
@@ -1129,6 +1260,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;
@@ -1145,25 +1293,21 @@ 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;
 }
 
 static __u32 imsm_min_reserved_sectors(struct intel_super *super);
 
-static int split_ull(unsigned long long n, __u32 *lo, __u32 *hi)
+static int split_ull(unsigned long long n, void *lo, void *hi)
 {
        if (lo == 0 || hi == 0)
                return 1;
-       *lo = __le32_to_cpu((unsigned)n);
-       *hi = __le32_to_cpu((unsigned)(n >> 32));
+       __put_unaligned32(__cpu_to_le32((__u32)n), lo);
+       __put_unaligned32(__cpu_to_le32((n >> 32)), hi);
        return 0;
 }
 
@@ -1180,6 +1324,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)
@@ -1201,6 +1372,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)
@@ -1208,11 +1387,61 @@ static unsigned long long imsm_dev_size(struct imsm_dev *dev)
        return join_u32(dev->size_low, dev->size_high);
 }
 
+static unsigned long long migr_chkp_area_pba(struct migr_record *migr_rec)
+{
+       if (migr_rec == NULL)
+               return 0;
+       return join_u32(migr_rec->ckpt_area_pba_lo,
+                       migr_rec->ckpt_area_pba_hi);
+}
+
+static unsigned long long current_migr_unit(struct migr_record *migr_rec)
+{
+       if (migr_rec == NULL)
+               return 0;
+       return join_u32(migr_rec->curr_migr_unit_lo,
+                       migr_rec->curr_migr_unit_hi);
+}
+
+static unsigned long long migr_dest_1st_member_lba(struct migr_record *migr_rec)
+{
+       if (migr_rec == NULL)
+               return 0;
+       return join_u32(migr_rec->dest_1st_member_lba_lo,
+                       migr_rec->dest_1st_member_lba_hi);
+}
+
+static unsigned long long get_num_migr_units(struct migr_record *migr_rec)
+{
+       if (migr_rec == NULL)
+               return 0;
+       return join_u32(migr_rec->num_migr_units_lo,
+                       migr_rec->num_migr_units_hi);
+}
+
 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);
@@ -1228,11 +1457,64 @@ 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);
 }
 
+static void set_migr_chkp_area_pba(struct migr_record *migr_rec,
+                                  unsigned long long n)
+{
+       split_ull(n, &migr_rec->ckpt_area_pba_lo, &migr_rec->ckpt_area_pba_hi);
+}
+
+static void set_current_migr_unit(struct migr_record *migr_rec,
+                                 unsigned long long n)
+{
+       split_ull(n, &migr_rec->curr_migr_unit_lo,
+                 &migr_rec->curr_migr_unit_hi);
+}
+
+static void set_migr_dest_1st_member_lba(struct migr_record *migr_rec,
+                                        unsigned long long n)
+{
+       split_ull(n, &migr_rec->dest_1st_member_lba_lo,
+                 &migr_rec->dest_1st_member_lba_hi);
+}
+
+static void set_num_migr_units(struct migr_record *migr_rec,
+                              unsigned long long n)
+{
+       split_ull(n, &migr_rec->num_migr_units_lo,
+                 &migr_rec->num_migr_units_hi);
+}
+
 static unsigned long long per_dev_array_size(struct imsm_map *map)
 {
        unsigned long long array_size = 0;
@@ -1247,7 +1529,8 @@ static unsigned long long per_dev_array_size(struct imsm_map *map)
        return array_size;
 }
 
-static struct extent *get_extents(struct intel_super *super, struct dl *dl)
+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;
@@ -1259,7 +1542,7 @@ static struct extent *get_extents(struct intel_super *super, struct dl *dl)
         * regardless of whether the OROM has assigned sectors from the
         * IMSM_RESERVED_SECTORS region
         */
-       if (dl->index == -1)
+       if (dl->index == -1 || get_minimal_reservation)
                reservation = imsm_min_reserved_sectors(super);
        else
                reservation = MPB_SECTOR_CNT + IMSM_RESERVED_SECTORS;
@@ -1320,7 +1603,7 @@ static __u32 imsm_reserved_sectors(struct intel_super *super, struct dl *dl)
        if (dl->index == -1)
                return MPB_SECTOR_CNT;
 
-       e = get_extents(super, dl);
+       e = get_extents(super, dl, 0);
        if (!e)
                return MPB_SECTOR_CNT + IMSM_RESERVED_SECTORS;
 
@@ -1412,7 +1695,7 @@ static __u32 imsm_min_reserved_sectors(struct intel_super *super)
                return rv;
 
        /* find last lba used by subarrays on the smallest active disk */
-       e = get_extents(super, dl_min);
+       e = get_extents(super, dl_min, 0);
        if (!e)
                return rv;
        for (i = 0; e[i].size; i++)
@@ -1453,7 +1736,7 @@ int get_spare_criteria_imsm(struct supertype *st, struct spare_criteria *c)
        if (!dl)
                return -EINVAL;
        /* find last lba used by subarrays */
-       e = get_extents(super, dl);
+       e = get_extents(super, dl, 0);
        if (!e)
                return -EINVAL;
        for (i = 0; e[i].size; i++)
@@ -1471,7 +1754,7 @@ int get_spare_criteria_imsm(struct supertype *st, struct spare_criteria *c)
        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
 
@@ -1491,6 +1774,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)
@@ -1538,7 +1822,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",
@@ -1571,8 +1855,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
@@ -1593,8 +1876,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,
@@ -1629,12 +1916,14 @@ void convert_to_4k_imsm_migr_rec(struct intel_super *super)
        struct migr_record *migr_rec = super->migr_rec;
 
        migr_rec->blocks_per_unit /= IMSM_4K_DIV;
-       migr_rec->ckpt_area_pba /= IMSM_4K_DIV;
-       migr_rec->dest_1st_member_lba /= IMSM_4K_DIV;
        migr_rec->dest_depth_per_unit /= IMSM_4K_DIV;
        split_ull((join_u32(migr_rec->post_migr_vol_cap,
                 migr_rec->post_migr_vol_cap_hi) / IMSM_4K_DIV),
                 &migr_rec->post_migr_vol_cap, &migr_rec->post_migr_vol_cap_hi);
+       set_migr_chkp_area_pba(migr_rec,
+                migr_chkp_area_pba(migr_rec) / IMSM_4K_DIV);
+       set_migr_dest_1st_member_lba(migr_rec,
+                migr_dest_1st_member_lba(migr_rec) / IMSM_4K_DIV);
 }
 
 void convert_to_4k_imsm_disk(struct imsm_disk *disk)
@@ -1659,7 +1948,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);
@@ -1708,13 +1998,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) {
@@ -1727,8 +2018,8 @@ void examine_migr_rec_imsm(struct intel_super *super)
                        printf("Normal\n");
                else
                        printf("Contains Data\n");
-               printf("               Current Unit : %u\n",
-                      __le32_to_cpu(migr_rec->curr_migr_unit));
+               printf("               Current Unit : %llu\n",
+                      current_migr_unit(migr_rec));
                printf("                     Family : %u\n",
                       __le32_to_cpu(migr_rec->family_num));
                printf("                  Ascending : %u\n",
@@ -1737,16 +2028,15 @@ void examine_migr_rec_imsm(struct intel_super *super)
                       __le32_to_cpu(migr_rec->blocks_per_unit));
                printf("       Dest. Depth Per Unit : %u\n",
                       __le32_to_cpu(migr_rec->dest_depth_per_unit));
-               printf("        Checkpoint Area pba : %u\n",
-                      __le32_to_cpu(migr_rec->ckpt_area_pba));
-               printf("           First member lba : %u\n",
-                      __le32_to_cpu(migr_rec->dest_1st_member_lba));
-               printf("      Total Number of Units : %u\n",
-                      __le32_to_cpu(migr_rec->num_migr_units));
-               printf("             Size of volume : %u\n",
-                      __le32_to_cpu(migr_rec->post_migr_vol_cap));
-               printf("  Expansion space for LBA64 : %u\n",
-                      __le32_to_cpu(migr_rec->post_migr_vol_cap_hi));
+               printf("        Checkpoint Area pba : %llu\n",
+                      migr_chkp_area_pba(migr_rec));
+               printf("           First member lba : %llu\n",
+                      migr_dest_1st_member_lba(migr_rec));
+               printf("      Total Number of Units : %llu\n",
+                      get_num_migr_units(migr_rec));
+               printf("             Size of volume : %llu\n",
+                      join_u32(migr_rec->post_migr_vol_cap,
+                               migr_rec->post_migr_vol_cap_hi));
                printf("       Record was read from : %u\n",
                       __le32_to_cpu(migr_rec->ckpt_read_disk_num));
 
@@ -1759,13 +2049,15 @@ void convert_from_4k_imsm_migr_rec(struct intel_super *super)
        struct migr_record *migr_rec = super->migr_rec;
 
        migr_rec->blocks_per_unit *= IMSM_4K_DIV;
-       migr_rec->ckpt_area_pba *= IMSM_4K_DIV;
-       migr_rec->dest_1st_member_lba *= IMSM_4K_DIV;
        migr_rec->dest_depth_per_unit *= IMSM_4K_DIV;
        split_ull((join_u32(migr_rec->post_migr_vol_cap,
                 migr_rec->post_migr_vol_cap_hi) * IMSM_4K_DIV),
                 &migr_rec->post_migr_vol_cap,
                 &migr_rec->post_migr_vol_cap_hi);
+       set_migr_chkp_area_pba(migr_rec,
+                migr_chkp_area_pba(migr_rec) * IMSM_4K_DIV);
+       set_migr_dest_1st_member_lba(migr_rec,
+                migr_dest_1st_member_lba(migr_rec) * IMSM_4K_DIV);
 }
 
 void convert_from_4k(struct intel_super *super)
@@ -1786,7 +2078,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);
@@ -1924,15 +2217,18 @@ 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';
        printf("          Magic : %s\n", str);
-       snprintf(str, strlen(MPB_VERSION_RAID0), "%s", get_imsm_version(mpb));
        printf("        Version : %s\n", get_imsm_version(mpb));
        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");
@@ -1987,12 +2283,6 @@ 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, ':');
@@ -2019,7 +2309,7 @@ static void brief_examine_subarrays_imsm(struct supertype *st, int verbose)
                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",
+               printf("ARRAY " DEV_MD_DIR "%.16s container=%s member=%d UUID=%s\n",
                       dev->volume, nbuf + 5, i, nbuf1 + 5);
        }
 }
@@ -2037,83 +2327,46 @@ static void export_examine_super_imsm(struct supertype *st)
        printf("MD_LEVEL=container\n");
        printf("MD_UUID=%s\n", nbuf+5);
        printf("MD_DEVICES=%u\n", mpb->num_disks);
+       printf("MD_CREATION_TIME=%llu\n", __le64_to_cpu(mpb->creation_time));
 }
 
-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;
-}
-
-static void detail_super_imsm(struct supertype *st, char *homehost)
+static void detail_super_imsm(struct supertype *st, char *homehost,
+                             char *subarray)
 {
        struct mdinfo info;
        char nbuf[64];
+       struct intel_super *super = st->sb;
+       int temp_vol = super->current_vol;
+
+       if (subarray)
+               super->current_vol = strtoul(subarray, NULL, 10);
 
        getinfo_super_imsm(st, &info, NULL);
        fname_from_uuid(st, &info, nbuf, ':');
        printf("\n              UUID : %s\n", nbuf + 5);
+
+       super->current_vol = temp_vol;
 }
 
-static void brief_detail_super_imsm(struct supertype *st)
+static void brief_detail_super_imsm(struct supertype *st, char *subarray)
 {
        struct mdinfo info;
        char nbuf[64];
+       struct intel_super *super = st->sb;
+       int temp_vol = super->current_vol;
+
+       if (subarray)
+               super->current_vol = strtoul(subarray, NULL, 10);
+
        getinfo_super_imsm(st, &info, NULL);
        fname_from_uuid(st, &info, nbuf, ':');
        printf(" UUID=%s", nbuf + 5);
+
+       super->current_vol = temp_vol;
 }
 
-static int imsm_read_serial(int fd, char *devname, __u8 *serial);
+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)
@@ -2147,14 +2400,14 @@ 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)) {
@@ -2163,20 +2416,15 @@ static int ahci_enumerate_ports(const char *hba_path, int port_count, int host_b
                        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);
@@ -2185,8 +2433,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];
@@ -2194,8 +2443,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];
@@ -2220,7 +2470,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], '/');
@@ -2254,13 +2503,14 @@ 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);
                        printf("          Port%d : %s", port, buf);
-                       if (imsm_read_serial(fd, NULL, (__u8 *) buf) == 0)
-                               printf(" (%.*s)\n", MAX_RAID_SERIAL_LEN, buf);
+                       if (imsm_read_serial(fd, NULL, (__u8 *)buf,
+                                            sizeof(buf)) == 0)
+                               printf(" (%s)\n", buf);
                        else
                                printf(" ()\n");
                        close(fd);
@@ -2283,52 +2533,52 @@ static int ahci_enumerate_ports(const char *hba_path, int port_count, int host_b
        return err;
 }
 
-static int print_vmd_attached_devs(struct sys_dev *hba)
+static int print_nvme_info(struct sys_dev *hba)
 {
        struct dirent *ent;
        DIR *dir;
-       char path[292];
-       char link[256];
-       char *c, *rp;
 
-       if (hba->type != SYS_DEV_VMD)
-               return 1;
-
-       /* scroll through /sys/dev/block looking for devices attached to
-        * this hba
-        */
-       dir = opendir("/sys/bus/pci/drivers/nvme");
+       dir = opendir("/sys/block/");
        if (!dir)
                return 1;
 
        for (ent = readdir(dir); ent; ent = readdir(dir)) {
-               int n;
+               char ns_path[PATH_MAX];
+               char cntrl_path[PATH_MAX];
+               char buf[PATH_MAX];
+               int fd = -1;
 
-               /* is 'ent' a device? check that the 'subsystem' link exists and
-                * that its target matches 'bus'
-                */
-               sprintf(path, "/sys/bus/pci/drivers/nvme/%s/subsystem",
-                       ent->d_name);
-               n = readlink(path, link, sizeof(link));
-               if (n < 0 || n >= (int)sizeof(link))
-                       continue;
-               link[n] = '\0';
-               c = strrchr(link, '/');
-               if (!c)
-                       continue;
-               if (strncmp("pci", c+1, strlen("pci")) != 0)
-                       continue;
+               if (!strstr(ent->d_name, "nvme"))
+                       goto skip;
 
-               sprintf(path, "/sys/bus/pci/drivers/nvme/%s", ent->d_name);
+               fd = open_dev(ent->d_name);
+               if (!is_fd_valid(fd))
+                       goto skip;
 
-               rp = realpath(path, NULL);
-               if (!rp)
-                       continue;
+               if (!diskfd_to_devpath(fd, 0, ns_path) ||
+                   !diskfd_to_devpath(fd, 1, cntrl_path))
+                       goto skip;
 
-               if (path_attached_to_hba(rp, hba->path)) {
-                       printf(" NVMe under VMD : %s\n", rp);
-               }
-               free(rp);
+               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");
+
+skip:
+               close_fd(&fd);
        }
 
        closedir(dir);
@@ -2488,7 +2738,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)
@@ -2543,7 +2793,7 @@ static int detail_platform_imsm(int verbose, int enumerate_only, char *controlle
                                        char buf[PATH_MAX];
                                        printf(" I/O Controller : %s (%s)\n",
                                                vmd_domain_to_controller(hba, buf), get_sys_dev_type(hba->type));
-                                       if (print_vmd_attached_devs(hba)) {
+                                       if (print_nvme_info(hba)) {
                                                if (verbose > 0)
                                                        pr_err("failed to get devices attached to VMD domain.\n");
                                                result |= 2;
@@ -2558,7 +2808,7 @@ static int detail_platform_imsm(int verbose, int enumerate_only, char *controlle
                if (entry->type == SYS_DEV_NVME) {
                        for (hba = list; hba; hba = hba->next) {
                                if (hba->type == SYS_DEV_NVME)
-                                       printf("    NVMe Device : %s\n", hba->path);
+                                       print_nvme_info(hba);
                        }
                        printf("\n");
                        continue;
@@ -2781,32 +3031,12 @@ 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)
 {
        unsigned long long component_size;
        unsigned long long dev_size = imsm_dev_size(dev);
-       unsigned long long calc_dev_size = 0;
+       long long calc_dev_size = 0;
        unsigned int member_disks = imsm_num_data_members(map);
 
        if (member_disks == 0)
@@ -2820,7 +3050,7 @@ static unsigned long long calc_component_size(struct imsm_map *map,
         * 2048 blocks per each device. If the difference is higher it means
         * that array size was expanded and num_data_stripes was not updated.
         */
-       if ((unsigned int)abs(calc_dev_size - dev_size) >
+       if (llabs(calc_dev_size - (long long)dev_size) >
            (1 << SECT_PER_MB_SHIFT) * member_disks) {
                component_size = dev_size / member_disks;
                dprintf("Invalid num_data_stripes in metadata; expected=%llu, found=%llu\n",
@@ -2999,15 +3229,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;
@@ -3015,6 +3243,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);
@@ -3023,44 +3252,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;
 
-                       sprintf(nm, "%d:%d", sd->disk.major, sd->disk.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;
+       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;
+
+               if (!is_fd_valid(dl->fd)) {
                        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;
 }
 
@@ -3097,8 +3323,8 @@ static int imsm_create_metadata_checkpoint_update(
                return 0;
        }
        (*u)->type = update_general_migration_checkpoint;
-       (*u)->curr_migr_unit = __le32_to_cpu(super->migr_rec->curr_migr_unit);
-       dprintf("prepared for %u\n", (*u)->curr_migr_unit);
+       (*u)->curr_migr_unit = current_migr_unit(super->migr_rec);
+       dprintf("prepared for %llu\n", (unsigned long long)(*u)->curr_migr_unit);
 
        return update_memory_size;
 }
@@ -3121,8 +3347,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;
@@ -3155,26 +3379,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);
@@ -3200,8 +3419,6 @@ static int write_imsm_migr_rec(struct supertype *st)
 
        retval = 0;
  out:
-       if (fd >= 0)
-               close(fd);
        return retval;
 }
 
@@ -3254,14 +3471,61 @@ static unsigned long long imsm_component_size_alignment_check(int level,
        return component_size;
 }
 
-static unsigned long long get_ppl_sector(struct intel_super *super, int dev_idx)
+/*******************************************************************************
+ * 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);
 
-       return pba_of_lba0(map) +
-              (num_data_stripes(map) * map->blocks_per_strip);
-}
+       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);
+       struct imsm_map *map = get_imsm_map(dev, MAP_0);
+
+       return pba_of_lba0(map) +
+              (num_data_stripes(map) * map->blocks_per_strip);
+}
 
 static void getinfo_super_imsm_volume(struct supertype *st, struct mdinfo *info, char *dmap)
 {
@@ -3294,6 +3558,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);
@@ -3374,7 +3644,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;
@@ -3390,7 +3665,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;
@@ -3398,13 +3673,12 @@ static void getinfo_super_imsm_volume(struct supertype *st, struct mdinfo *info,
                case MIGR_GEN_MIGR: {
                        __u64 blocks_per_unit = blocks_per_migr_unit(super,
                                                                     dev);
-                       __u64 units = __le32_to_cpu(migr_rec->curr_migr_unit);
-                       unsigned long long array_blocks;
+                       __u64 units = current_migr_unit(migr_rec);
                        int used_disks;
 
                        if (__le32_to_cpu(migr_rec->ascending_migr) &&
                            (units <
-                               (__le32_to_cpu(migr_rec->num_migr_units)-1)) &&
+                               (get_num_migr_units(migr_rec)-1)) &&
                            (super->migr_rec->rec_status ==
                                        __cpu_to_le32(UNIT_SRC_IN_CP_AREA)))
                                units++;
@@ -3418,12 +3692,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:
@@ -3662,8 +3932,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.
@@ -3699,7 +3969,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
@@ -3723,10 +3994,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)
@@ -3778,14 +4053,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;
@@ -3795,29 +4068,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
@@ -3829,7 +4103,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;
@@ -3837,76 +4111,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)
@@ -3915,19 +4148,20 @@ 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);
 
 static int imsm_read_serial(int fd, char *devname,
-                           __u8 serial[MAX_RAID_SERIAL_LEN])
+                           __u8 *serial, size_t serial_buf_len)
 {
        char buf[50];
        int rv;
-       int len;
+       size_t len;
        char *dest;
        char *src;
        unsigned int i;
@@ -3970,13 +4204,13 @@ static int imsm_read_serial(int fd, char *devname,
        len = dest - buf;
        dest = buf;
 
-       /* truncate leading characters */
-       if (len > MAX_RAID_SERIAL_LEN) {
-               dest += len - MAX_RAID_SERIAL_LEN;
-               len = MAX_RAID_SERIAL_LEN;
+       if (len > serial_buf_len) {
+               /* truncate leading characters */
+               dest += len - serial_buf_len;
+               len = serial_buf_len;
        }
 
-       memset(serial, 0, MAX_RAID_SERIAL_LEN);
+       memset(serial, 0, serial_buf_len);
        memcpy(serial, dest, len);
 
        return 0;
@@ -4031,7 +4265,7 @@ load_imsm_disk(int fd, struct intel_super *super, char *devname, int keep_fd)
        char name[40];
        __u8 serial[MAX_RAID_SERIAL_LEN];
 
-       rv = imsm_read_serial(fd, devname, serial);
+       rv = imsm_read_serial(fd, devname, serial, MAX_RAID_SERIAL_LEN);
 
        if (rv != 0)
                return 2;
@@ -4094,7 +4328,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] */
@@ -4129,7 +4363,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)
@@ -4154,7 +4388,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;
 }
 
@@ -4232,8 +4466,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);
@@ -4321,6 +4554,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;
@@ -4382,8 +4616,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)
@@ -4416,10 +4648,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)
@@ -4435,17 +4667,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);
        }
 
 }
@@ -4524,7 +4756,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 || check_env("IMSM_NO_PLATFORM")) {
+       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 (!is_fd_valid(fd) || check_no_platform()) {
                super->orom = NULL;
                super->hba = NULL;
                return 0;
@@ -4933,7 +5170,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
@@ -4955,7 +5192,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,
@@ -4990,7 +5227,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;
@@ -5016,7 +5253,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;
@@ -5068,19 +5305,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;
@@ -5092,11 +5332,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;
 
 }
@@ -5152,9 +5391,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.
@@ -5179,7 +5422,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;
@@ -5204,7 +5447,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");
@@ -5286,10 +5529,22 @@ static int check_name(struct intel_super *super, char *name, int quiet)
 {
        struct imsm_super *mpb = super->anchor;
        char *reason = NULL;
+       char *start = name;
+       size_t len = strlen(name);
        int i;
 
-       if (strlen(name) > MAX_RAID_SERIAL_LEN)
+       if (len > 0) {
+               while (isspace(start[len - 1]))
+                       start[--len] = 0;
+               while (*start && isspace(*start))
+                       ++start, --len;
+               memmove(name, start, len + 1);
+       }
+
+       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);
@@ -5323,9 +5578,9 @@ static int init_super_imsm_volume(struct supertype *st, mdu_array_info_t *info,
        struct imsm_map *map;
        int idx = mpb->num_raid_devs;
        int i;
+       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;
 
@@ -5402,7 +5657,12 @@ static int init_super_imsm_volume(struct supertype *st, mdu_array_info_t *info,
                return 0;
        dv = xmalloc(sizeof(*dv));
        dev = xcalloc(1, sizeof(*dev) + sizeof(__u32) * (info->raid_disks - 1));
-       strncpy((char *) dev->volume, name, MAX_RAID_SERIAL_LEN);
+       /*
+        * Explicitly allow truncating to not confuse gcc's
+        * -Werror=stringop-truncation
+        */
+       namelen = min((int) strlen(name), MAX_RAID_SERIAL_LEN);
+       memcpy(dev->volume, name, namelen);
        array_blocks = calc_array_size(info->level, info->raid_disks,
                                               info->layout, info->chunk_size,
                                               s->size * BLOCKS_PER_KB);
@@ -5417,7 +5677,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));
@@ -5438,18 +5698,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,
@@ -5457,6 +5708,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);
@@ -5473,7 +5725,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;
        }
 
@@ -5560,7 +5812,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;
        }
@@ -5582,6 +5834,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);
@@ -5592,25 +5848,37 @@ 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))
+                       return 1;
+
        if (!drive_validate_sector_size(super, dl)) {
                pr_err("Combining drives of different sector size in one volume is not allowed\n");
                return 1;
@@ -5679,7 +5947,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;
                }
@@ -5689,6 +5957,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;
@@ -5711,7 +5980,7 @@ int mark_spare(struct dl *disk)
                return ret_val;
 
        ret_val = 0;
-       if (!imsm_read_serial(disk->fd, NULL, serial)) {
+       if (!imsm_read_serial(disk->fd, NULL, serial, MAX_RAID_SERIAL_LEN)) {
                /* Restore disk serial number, because takeover marks disk
                 * as failed and adds to serial ':0' before it becomes
                 * a spare disk.
@@ -5726,6 +5995,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)
@@ -5762,32 +6034,33 @@ static int add_to_super_imsm(struct supertype *st, mdu_disk_info_t *dk,
        dd->fd = fd;
        dd->e = NULL;
        dd->action = DISK_ADD;
-       rv = imsm_read_serial(fd, devname, dd->serial);
+       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"
@@ -5814,14 +6087,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 */
@@ -5855,9 +6130,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;
@@ -5896,15 +6175,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);
@@ -5917,32 +6196,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;
@@ -5992,10 +6282,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;
        }
@@ -6056,10 +6346,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)
@@ -6290,7 +6578,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 {
@@ -6313,7 +6601,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
@@ -6358,6 +6646,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;
@@ -6381,7 +6723,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);
                }
@@ -6389,8 +6734,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);
        }
@@ -6412,8 +6761,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,
@@ -6421,39 +6769,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) {
@@ -6464,32 +6804,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)
@@ -6638,12 +6988,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);
                                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));
                                }
@@ -6651,7 +7001,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 &&
@@ -6671,8 +7021,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);
@@ -6722,7 +7071,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)) {
@@ -6733,7 +7082,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 {
@@ -6792,7 +7141,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;
@@ -6829,8 +7178,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;
@@ -6855,7 +7204,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
@@ -7107,7 +7456,7 @@ static int validate_geometry_imsm_volume(struct supertype *st, int level,
 
                        pos = 0;
                        i = 0;
-                       e = get_extents(super, dl);
+                       e = get_extents(super, dl, 0);
                        if (!e) continue;
                        do {
                                unsigned long long esize;
@@ -7165,7 +7514,7 @@ static int validate_geometry_imsm_volume(struct supertype *st, int level,
        }
 
        /* retrieve the largest free space block */
-       e = get_extents(super, dl);
+       e = get_extents(super, dl, 0);
        maxsize = 0;
        i = 0;
        if (e) {
@@ -7201,11 +7550,8 @@ static int validate_geometry_imsm_volume(struct supertype *st, int level,
 
        maxsize = merge_extents(super, i);
 
-       if (!check_env("IMSM_NO_PLATFORM") &&
-           mpb->num_raid_devs > 0 && size && size != maxsize) {
-               pr_err("attempting to create a second volume with size less then remaining space. Aborting...\n");
-               return 0;
-       }
+       if (mpb->num_raid_devs > 0 && size && size != maxsize)
+               pr_err("attempting to create a second volume with size less then remaining space.\n");
 
        if (maxsize < size || maxsize == 0) {
                if (verbose) {
@@ -7232,11 +7578,27 @@ 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.
+ * @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)
 {
-       struct intel_super *super = st->sb;
        struct imsm_super *mpb = super->anchor;
        struct dl *dl;
        int i;
@@ -7263,7 +7625,7 @@ static int imsm_get_free_size(struct supertype *st, int raiddisks,
                if (super->orom && dl->index < 0 && mpb->num_raid_devs)
                        continue;
 
-               e = get_extents(super, dl);
+               e = get_extents(super, dl, 0);
                if (!e)
                        continue;
                for (i = 1; e[i-1].size; i++)
@@ -7280,12 +7642,10 @@ static int imsm_get_free_size(struct supertype *st, int raiddisks,
                /* chunk is in K */
                minsize = chunk * 2;
 
-       if (cnt < raiddisks ||
-           (super->orom && used && used != raiddisks) ||
-           maxsize < minsize ||
-           maxsize == 0) {
+       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 */
+               return IMSM_STATUS_ERROR;
        }
 
        if (size == 0) {
@@ -7296,48 +7656,77 @@ static int imsm_get_free_size(struct supertype *st, int raiddisks,
                }
                maxsize = size;
        }
-       if (!check_env("IMSM_NO_PLATFORM") &&
-           mpb->num_raid_devs > 0 && size && size != maxsize) {
-               pr_err("attempting to create a second volume with size less then remaining space. Aborting...\n");
-               return 0;
-       }
-       cnt = 0;
-       for (dl = super->disks; dl; dl = dl->next)
-               if (dl->e)
-                       dl->raiddisk = cnt++;
-
+       if (mpb->num_raid_devs > 0 && size && size != maxsize)
+               pr_err("attempting to create a second volume with size less then remaining space.\n");
        *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);
+       if (rv != IMSM_STATUS_OK)
+               return IMSM_STATUS_ERROR;
 
-       return rv;
-}
+       for (disk = super->disks; disk; disk = disk->next) {
+               if (!disk->e)
+                       continue;
 
-static int validate_geometry_imsm(struct supertype *st, int level, int layout,
-                                 int raiddisks, int *chunk, unsigned long long size,
-                                 unsigned long long data_offset,
-                                 char *dev, unsigned long long *freesize,
+               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 IMSM_STATUS_OK;
+}
+
+static int validate_geometry_imsm(struct supertype *st, int level, int layout,
+                                 int raiddisks, int *chunk, unsigned long long size,
+                                 unsigned long long data_offset,
+                                 char *dev, unsigned long long *freesize,
                                  int consistency_policy, int verbose)
 {
        int fd, cfd;
@@ -7348,19 +7737,17 @@ 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) || (*chunk != UnSet &&
-           size < (unsigned long long) *chunk))) {
-               pr_err("Given size must be greater than 1M and chunk size.\n");
+       /*
+        * 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,
                 * if block device was given ( dev != NULL) return 0.
@@ -7369,35 +7756,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;
        }
@@ -7411,26 +7798,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);
@@ -7479,18 +7866,17 @@ static void default_geometry_imsm(struct supertype *st, int *level, int *layout,
 
 static void handle_missing(struct intel_super *super, struct imsm_dev *dev);
 
-static int kill_subarray_imsm(struct supertype *st)
+static int kill_subarray_imsm(struct supertype *st, char *subarray_id)
 {
-       /* remove the subarray currently referenced by ->current_vol */
+       /* remove the subarray currently referenced by subarray_id */
        __u8 i;
        struct intel_dev **dp;
        struct intel_super *super = st->sb;
-       __u8 current_vol = super->current_vol;
+       __u8 current_vol = strtoul(subarray_id, NULL, 10);
        struct imsm_super *mpb = super->anchor;
 
-       if (super->current_vol < 0)
+       if (mpb->num_raid_devs == 0)
                return 2;
-       super->current_vol = -1; /* invalidate subarray cursor */
 
        /* block deletions that would change the uuid of active subarrays
         *
@@ -7548,23 +7934,39 @@ static int kill_subarray_imsm(struct supertype *st)
        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))
                        return 2;
 
@@ -7582,19 +7984,19 @@ static int update_subarray_imsm(struct supertype *st, char *subarray,
                        append_metadata_update(st, u, sizeof(*u));
                } else {
                        struct imsm_dev *dev;
-                       int i;
+                       int i, namelen;
 
                        dev = get_imsm_dev(super, vol);
-                       strncpy((char *) dev->volume, name, MAX_RAID_SERIAL_LEN);
-                       dev->volume[MAX_RAID_SERIAL_LEN-1] = '\0';
+                       memset(dev->volume, '\0', MAX_RAID_SERIAL_LEN);
+                       namelen = min((int)strlen(name), MAX_RAID_SERIAL_LEN);
+                       memcpy(dev->volume, name, namelen);
                        for (i = 0; i < mpb->num_raid_devs; i++) {
                                dev = get_imsm_dev(super, i);
                                handle_missing(super, dev);
                        }
                        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);
@@ -7602,10 +8004,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));
@@ -7621,24 +8020,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 (migr_type(dev) == MIGR_GEN_MIGR)
-               return 1;
+       if (dev && dev->vol.migr_state &&
+           migr_type(dev) == MIGR_GEN_MIGR)
+               return true;
 
-       return 0;
+       return false;
 }
 
 static int is_rebuilding(struct imsm_dev *dev)
@@ -7705,7 +8101,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);
 }
 
@@ -7729,6 +8125,7 @@ static struct mdinfo *container_content_imsm(struct supertype *st, char *subarra
        int sb_errors = 0;
        struct dl *d;
        int spare_disks = 0;
+       int current_vol = super->current_vol;
 
        /* do not assemble arrays when not all attributes are supported */
        if (imsm_check_attributes(mpb->attributes) == 0) {
@@ -7823,7 +8220,8 @@ static struct mdinfo *container_content_imsm(struct supertype *st, char *subarra
                                skip = 1;
                        if (!skip && (ord & IMSM_ORD_REBUILD))
                                recovery_start = 0;
-
+                       if (!(ord & IMSM_ORD_REBUILD))
+                               this->array.working_disks++;
                        /*
                         * if we skip some disks the array will be assmebled degraded;
                         * reset resync start to avoid a dirty-degraded
@@ -7836,10 +8234,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)
@@ -7865,8 +8259,6 @@ static struct mdinfo *container_content_imsm(struct supertype *st, char *subarra
                                else
                                        this->array.spare_disks++;
                        }
-                       if (info_d->recovery_start == MaxSector)
-                               this->array.working_disks++;
 
                        info_d->events = __le32_to_cpu(mpb->generation_num);
                        info_d->data_offset = pba_of_lba0(map);
@@ -7896,6 +8288,7 @@ static struct mdinfo *container_content_imsm(struct supertype *st, char *subarra
                rest = this;
        }
 
+       super->current_vol = current_vol;
        return rest;
 }
 
@@ -8023,19 +8416,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));
@@ -8092,7 +8485,7 @@ static int mark_failure(struct intel_super *super,
        strcat(buf, ":0");
        if ((len = strlen(buf)) >= MAX_RAID_SERIAL_LEN)
                shift = len - MAX_RAID_SERIAL_LEN + 1;
-       strncpy((char *)disk->serial, &buf[shift], MAX_RAID_SERIAL_LEN);
+       memcpy(disk->serial, &buf[shift], len + 1 - shift);
 
        disk->status |= FAILED_DISK;
        set_imsm_ord_tbl_ent(map, slot, idx | IMSM_ORD_REBUILD);
@@ -8108,7 +8501,8 @@ static int mark_failure(struct intel_super *super,
                        set_imsm_ord_tbl_ent(map2, slot2,
                                             idx | IMSM_ORD_REBUILD);
        }
-       if (map->failed_disk_num == 0xff)
+       if (map->failed_disk_num == 0xff ||
+               (!is_rebuilding(dev) && map->failed_disk_num > slot))
                map->failed_disk_num = slot;
 
        clear_disk_badblocks(super->bbm_log, ord_to_idx(ord));
@@ -8144,7 +8538,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) {
@@ -8250,7 +8644,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++)
@@ -8287,10 +8681,10 @@ 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) {
@@ -8304,7 +8698,7 @@ static int imsm_set_array_state(struct active_array *a, int consistent)
                                                get_imsm_map(dev, MAP_1);
                                        dev->vol.migr_state = 0;
                                        set_migr_type(dev, 0);
-                                       dev->vol.curr_migr_unit = 0;
+                                       set_vol_curr_migr_unit(dev, 0);
                                        memcpy(map, map2,
                                               sizeof_imsm_map(map2));
                                        super->updates_pending++;
@@ -8380,25 +8774,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:
@@ -8462,7 +8847,7 @@ static void imsm_set_disk(struct active_array *a, int n, int state)
        disk = get_imsm_disk(super, ord_to_idx(ord));
 
        /* check for new failures */
-       if (state & DS_FAULTY) {
+       if (disk && (state & DS_FAULTY)) {
                if (mark_failure(super, dev, disk, ord_to_idx(ord)))
                        super->updates_pending++;
        }
@@ -8502,7 +8887,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;
@@ -8514,7 +8898,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;
@@ -8530,15 +8913,23 @@ static void imsm_set_disk(struct active_array *a, int n, int state)
                        break;
                }
                if (is_rebuilding(dev)) {
-                       dprintf_cont("while rebuilding.");
-                       if (map->map_state != map_state)  {
-                               dprintf_cont(" Map state change");
-                               end_migration(dev, super, map_state);
+                       dprintf_cont("while rebuilding ");
+                       if (state & DS_FAULTY)  {
+                               dprintf_cont("removing failed drive ");
+                               if (n == map->failed_disk_num) {
+                                       dprintf_cont("end migration");
+                                       end_migration(dev, super, map_state);
+                                       a->last_checkpoint = 0;
+                               } else {
+                                       dprintf_cont("fail detected during rebuild, changing map state");
+                                       map->map_state = map_state;
+                               }
                                super->updates_pending++;
-                       } else if (!rebuild_done) {
-                               break;
                        }
 
+                       if (!rebuild_done)
+                               break;
+
                        /* check if recovery is really finished */
                        for (mdi = a->info.devs; mdi ; mdi = mdi->next)
                                if (mdi->recovery_start != MaxSector) {
@@ -8547,7 +8938,7 @@ static void imsm_set_disk(struct active_array *a, int n, int state)
                                }
                        if (recovery_not_finished) {
                                dprintf_cont("\n");
-                               dprintf("Rebuild has not finished yet, state not changed");
+                               dprintf_cont("Rebuild has not finished yet");
                                if (a->last_checkpoint < mdi->recovery_start) {
                                        a->last_checkpoint =
                                                mdi->recovery_start;
@@ -8557,9 +8948,9 @@ static void imsm_set_disk(struct active_array *a, int n, int state)
                        }
 
                        dprintf_cont(" Rebuild done, still degraded");
-                       dev->vol.migr_state = 0;
-                       set_migr_type(dev, 0);
-                       dev->vol.curr_migr_unit = 0;
+                       end_migration(dev, super, map_state);
+                       a->last_checkpoint = 0;
+                       super->updates_pending++;
 
                        for (i = 0; i < map->num_members; i++) {
                                int idx = get_imsm_ord_tbl_ent(dev, i, MAP_0);
@@ -8618,7 +9009,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) {
@@ -8698,7 +9090,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",
@@ -8740,7 +9132,7 @@ static struct dl *imsm_add_spare(struct intel_super *super, int slot,
                /* Does this unused device have the requisite free space?
                 * It needs to be able to cover all member volumes
                 */
-               ex = get_extents(super, dl);
+               ex = get_extents(super, dl, 1);
                if (!ex) {
                        dprintf("cannot get extents\n");
                        continue;
@@ -8801,29 +9193,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;
@@ -8858,13 +9247,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",
@@ -8920,7 +9311,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;
 
                /*
@@ -9050,7 +9441,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;
                }
@@ -9094,10 +9485,13 @@ static int add_remove_disk_update(struct intel_super *super)
                                        remove_disk_super(super,
                                                          disk_cfg->major,
                                                          disk_cfg->minor);
+                               } else {
+                                       disk_cfg->fd = disk->fd;
+                                       disk->fd = -1;
                                }
                        }
                        /* release allocate disk structure */
-                       __free_imsm_disk(disk_cfg);
+                       __free_imsm_disk(disk_cfg, 1);
                }
        }
        return check_degraded;
@@ -9172,7 +9566,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 =
@@ -9183,11 +9576,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
@@ -9261,7 +9650,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)
@@ -9272,16 +9660,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;
                }
@@ -9290,6 +9672,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)
@@ -9371,10 +9786,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++;
                }
 
@@ -9474,7 +9888,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++) {
@@ -9530,14 +9944,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;
-
-               map->num_domains = 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);
-
                /* Number of failed disks must be half of initial disk number */
                if (imsm_count_failed(super, dev, MAP_0) !=
                                (map->num_members / 2))
@@ -9558,15 +9964,17 @@ 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;
        }
 
        if (u->direction == R0_TO_R10) {
                void **space;
+
                /* update slots in current disk list */
                for (dm = super->disks; dm; dm = dm->next) {
                        if (dm->index >= 0)
@@ -9600,10 +10008,13 @@ 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;
+               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;
        }
@@ -9672,8 +10083,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++;
                        }
                }
@@ -9710,7 +10121,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;
        }
@@ -9832,7 +10245,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
@@ -9848,8 +10260,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;
                }
@@ -9876,18 +10287,19 @@ static void imsm_process_update(struct supertype *st,
                /* sanity check that we are not affecting the uuid of
                 * an active array
                 */
+               memset(name, 0, sizeof(name));
                snprintf(name, MAX_RAID_SERIAL_LEN, "%s", (char *) u->name);
                name[MAX_RAID_SERIAL_LEN] = '\0';
                for (a = st->arrays; a; a = a->next)
                        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 || !check_name(super, name, 1)) {
                        dprintf("failed to rename subarray-%d\n", target);
                        break;
                }
 
-               snprintf((char *) dev->volume, MAX_RAID_SERIAL_LEN, "%s", name);
+               memcpy(dev->volume, name, MAX_RAID_SERIAL_LEN);
                super->updates_pending++;
                break;
        }
@@ -9911,10 +10323,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;
@@ -9923,7 +10331,7 @@ static void imsm_process_update(struct supertype *st,
                break;
        }
        default:
-               pr_err("error: unsuported process update type:(type: %d)\n",    type);
+               pr_err("error: unsupported process update type:(type: %d)\n",   type);
        }
 }
 
@@ -10258,22 +10666,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);
        }
 }
 
@@ -10330,62 +10723,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,
@@ -10399,14 +10736,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)) {
@@ -10427,7 +10764,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) {
@@ -10626,13 +10964,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);
@@ -10655,24 +10991,22 @@ void init_migr_record_imsm(struct supertype *st, struct imsm_dev *dev,
 
        if (array_blocks % __le32_to_cpu(migr_rec->blocks_per_unit))
                num_migr_units++;
-       migr_rec->num_migr_units = __cpu_to_le32(num_migr_units);
+       set_num_migr_units(migr_rec, num_migr_units);
 
        migr_rec->post_migr_vol_cap =  dev->size_low;
        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);
        }
-       migr_rec->ckpt_area_pba = __cpu_to_le32(min_dev_sectors -
+       set_migr_chkp_area_pba(migr_rec, min_dev_sectors -
                                        RAID_DISK_RESERVED_BLOCKS_IMSM_HI);
 
        write_imsm_migr_rec(st);
@@ -10704,37 +11038,31 @@ 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++) {
-               target_offsets[i] = (unsigned long long)
-                 __le32_to_cpu(super->migr_rec->ckpt_area_pba) * 512;
+               target_offsets[i] = migr_chkp_area_pba(super->migr_rec) * 512;
                /* move back copy area adderss, it will be moved forward
                 * in restore_stripes() using start input variable
                 */
                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;
 
@@ -10757,12 +11085,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;
 }
 
@@ -10785,7 +11107,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;
        }
@@ -10803,12 +11125,11 @@ int save_checkpoint_imsm(struct supertype *st, struct mdinfo *info, int state)
        if (info->reshape_progress % blocks_per_unit)
                curr_migr_unit++;
 
-       super->migr_rec->curr_migr_unit =
-               __cpu_to_le32(curr_migr_unit);
+       set_current_migr_unit(super->migr_rec, curr_migr_unit);
        super->migr_rec->rec_status = __cpu_to_le32(state);
-       super->migr_rec->dest_1st_member_lba =
-               __cpu_to_le32(curr_migr_unit *
-                             __le32_to_cpu(super->migr_rec->dest_depth_per_unit));
+       set_migr_dest_1st_member_lba(super->migr_rec,
+                       super->migr_rec->dest_depth_per_unit * curr_migr_unit);
+
        if (write_imsm_migr_rec(st) < 0) {
                dprintf("imsm: Cannot write migration record outside backup area\n");
                return 1;
@@ -10837,15 +11158,15 @@ 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 = __le32_to_cpu(migr_rec->curr_migr_unit);
-       unsigned long num_migr_units = __le32_to_cpu(migr_rec->num_migr_units);
+       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[20];
        int skipped_disks = 0;
+       struct dl *dl_disk;
 
        err = sysfs_get_str(info, NULL, "array_state", (char *)buffer, 20);
        if (err < 1)
@@ -10870,47 +11191,42 @@ int recover_backup_imsm(struct supertype *st, struct mdinfo *info)
        map_dest = get_imsm_map(id->dev, MAP_0);
        new_disks = map_dest->num_members;
 
-       read_offset = (unsigned long long)
-                       __le32_to_cpu(migr_rec->ckpt_area_pba) * 512;
+       read_offset = migr_chkp_area_pba(migr_rec) * 512;
 
-       write_offset = ((unsigned long long)
-                       __le32_to_cpu(migr_rec->dest_1st_member_lba) +
+       write_offset = (migr_dest_1st_member_lba(migr_rec) +
                        pba_of_lba0(map_dest)) * 512;
 
        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++;
@@ -10934,12 +11250,6 @@ 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;
 }
@@ -10958,7 +11268,7 @@ static const char *imsm_get_disk_controller_domain(const char *path)
                struct sys_dev* hba;
                char *path;
 
-               path = devt_to_devpath(st.st_rdev);
+               path = devt_to_devpath(st.st_rdev, 1, NULL);
                if (path == NULL)
                        return "unknown";
                hba = find_disk_attached_hba(-1, path);
@@ -11237,8 +11547,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);
@@ -11255,23 +11567,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;
 
@@ -11339,7 +11643,7 @@ enum imsm_reshape_type imsm_analyze_change(struct supertype *st,
        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 &&
@@ -11458,9 +11762,10 @@ enum imsm_reshape_type imsm_analyze_change(struct supertype *st,
                }
                /* check the maximum available size
                 */
-               rv =  imsm_get_free_size(st, dev->vol.map->num_members,
-                                        0, chunk, &free_size);
-               if (rv == 0)
+               rv = imsm_get_free_size(super, dev->vol.map->num_members,
+                                       0, chunk, &free_size);
+
+               if (rv != IMSM_STATUS_OK)
                        /* Cannot find maximum available space
                         */
                        max_size = 0;
@@ -11523,8 +11828,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;
                }
        }
@@ -11569,6 +11874,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,
@@ -11595,9 +11959,6 @@ static int imsm_reshape_super(struct supertype *st, unsigned long long size,
        dprintf("for level      : %i\n", geo.level);
        dprintf("for raid_disks : %i\n", geo.raid_disks);
 
-       if (experimental() == 0)
-               return ret_val;
-
        if (strcmp(st->container_devnm, st->devnm) == 0) {
                /* On container level we can only increase number of devices. */
                dprintf("imsm: info: Container operation\n");
@@ -11608,6 +11969,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);
 
@@ -11754,7 +12120,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;
        }
@@ -11766,7 +12132,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--);
@@ -11841,6 +12207,7 @@ int check_degradation_change(struct mdinfo *info,
                                continue;
                        if (sd->disk.state & (1<<MD_DISK_SYNC)) {
                                char sbuf[100];
+                               int raid_disk = sd->disk.raid_disk;
 
                                if (sysfs_get_str(info,
                                        sd, "state", sbuf, sizeof(sbuf)) < 0 ||
@@ -11848,13 +12215,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++;
                                }
                        }
@@ -11910,6 +12272,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;
@@ -11923,6 +12286,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 */
@@ -11980,12 +12344,12 @@ static int imsm_manage_reshape(
        max_position = sra->component_size * ndata;
        source_layout = imsm_level_to_layout(map_src->raid_level);
 
-       while (__le32_to_cpu(migr_rec->curr_migr_unit) <
-              __le32_to_cpu(migr_rec->num_migr_units)) {
+       while (current_migr_unit(migr_rec) <
+              get_num_migr_units(migr_rec)) {
                /* current reshape position [blocks] */
                unsigned long long current_position =
                        __le32_to_cpu(migr_rec->blocks_per_unit)
-                       * __le32_to_cpu(migr_rec->curr_migr_unit);
+                       * current_migr_unit(migr_rec);
                unsigned long long border;
 
                /* Check that array hasn't become failed.
@@ -12107,6 +12471,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 */
@@ -12117,6 +12489,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,
@@ -12138,7 +12978,6 @@ struct superswitch super_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,
@@ -12159,6 +12998,11 @@ 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,