]> git.ipfire.org Git - thirdparty/mdadm.git/blobdiff - super-intel.c
util: Introduce md_get_disk_info()
[thirdparty/mdadm.git] / super-intel.c
index 0817a1fe91bcb8c849ab70f2fb2b6c30512df7ac..785488a5dc8611052e9b4190bc8d473f9aacb806 100644 (file)
@@ -102,6 +102,7 @@ struct imsm_disk {
 #define SPARE_DISK      __cpu_to_le32(0x01)  /* Spare */
 #define CONFIGURED_DISK __cpu_to_le32(0x02)  /* Member of some RaidDev */
 #define FAILED_DISK     __cpu_to_le32(0x04)  /* Permanent failure */
+#define JOURNAL_DISK    __cpu_to_le32(0x2000000) /* Device marked as Journaling Drive */
        __u32 status;                    /* 0xF0 - 0xF3 */
        __u32 owner_cfg_num; /* which config 0,1,2... owns this disk */
        __u32 total_blocks_hi;           /* 0xF4 - 0xF5 total blocks hi */
@@ -155,6 +156,9 @@ struct imsm_vol {
 #define MIGR_STATE_CHANGE 4
 #define MIGR_REPAIR 5
        __u8  migr_type;        /* Initializing, Rebuilding, ... */
+#define RAIDVOL_CLEAN          0
+#define RAIDVOL_DIRTY          1
+#define RAIDVOL_DSRECORD_VALID 2
        __u8  dirty;
        __u8  fs_state;         /* fast-sync state for CnG (0xff == disabled) */
        __u16 verify_errors;    /* number of mismatches */
@@ -190,7 +194,24 @@ struct imsm_dev {
        __u16 cache_policy;
        __u8  cng_state;
        __u8  cng_sub_state;
-#define IMSM_DEV_FILLERS 10
+       __u16 my_vol_raid_dev_num; /* Used in Unique volume Id for this RaidDev */
+
+       /* NVM_EN */
+       __u8 nv_cache_mode;
+       __u8 nv_cache_flags;
+
+       /* Unique Volume Id of the NvCache Volume associated with this volume */
+       __u32 nvc_vol_orig_family_num;
+       __u16 nvc_vol_raid_dev_num;
+
+#define RWH_OFF 0
+#define RWH_DISTRIBUTED 1
+#define RWH_JOURNALING_DRIVE 2
+       __u8  rwh_policy; /* Raid Write Hole Policy */
+       __u8  jd_serial[MAX_RAID_SERIAL_LEN]; /* Journal Drive serial number */
+       __u8  filler1;
+
+#define IMSM_DEV_FILLERS 3
        __u32 filler[IMSM_DEV_FILLERS];
        struct imsm_vol vol;
 } __attribute__ ((packed));
@@ -257,6 +278,9 @@ static char *map_state_str[] = { "normal", "uninitialized", "degraded", "failed"
 #define UNIT_SRC_IN_CP_AREA 1   /* Source data for curr_migr_unit has
                                 *  already been migrated and must
                                 *  be recovered from checkpoint area */
+
+#define PPL_ENTRY_SPACE (128 * 1024) /* Size of the PPL, without the header */
+
 struct migr_record {
        __u32 rec_status;           /* Status used to determine how to restart
                                     * migration in case it aborts
@@ -427,6 +451,7 @@ enum imsm_update_type {
        update_general_migration_checkpoint,
        update_size_change,
        update_prealloc_badblocks_mem,
+       update_rwh_policy,
 };
 
 struct imsm_update_activate_spare {
@@ -519,6 +544,12 @@ struct imsm_update_prealloc_bb_mem {
        enum imsm_update_type type;
 };
 
+struct imsm_update_rwh_policy {
+       enum imsm_update_type type;
+       int new_policy;
+       int dev_idx;
+};
+
 static const char *_sys_dev_type[] = {
        [SYS_DEV_UNKNOWN] = "Unknown",
        [SYS_DEV_SAS] = "SAS",
@@ -1288,6 +1319,11 @@ static int is_failed(struct imsm_disk *disk)
        return (disk->status & FAILED_DISK) == FAILED_DISK;
 }
 
+static int is_journal(struct imsm_disk *disk)
+{
+       return (disk->status & JOURNAL_DISK) == JOURNAL_DISK;
+}
+
 /* try to determine how much space is reserved for metadata from
  * the last get_extents() entry on the smallest active disk,
  * otherwise fallback to the default
@@ -1477,11 +1513,23 @@ static void print_imsm_dev(struct intel_super *super,
                                   blocks_per_migr_unit(super, dev));
        }
        printf("\n");
-       printf("    Dirty State : %s\n", dev->vol.dirty ? "dirty" : "clean");
+       printf("    Dirty State : %s\n", (dev->vol.dirty & RAIDVOL_DIRTY) ?
+                                        "dirty" : "clean");
+       printf("     RWH Policy : ");
+       if (dev->rwh_policy == RWH_OFF)
+               printf("off\n");
+       else if (dev->rwh_policy == RWH_DISTRIBUTED)
+               printf("PPL distributed\n");
+       else if (dev->rwh_policy == RWH_JOURNALING_DRIVE)
+               printf("PPL journaling drive\n");
+       else
+               printf("<unknown:%d>\n", dev->rwh_policy);
 }
 
-static void print_imsm_disk(struct imsm_disk *disk, int index, __u32 reserved)
-{
+static void print_imsm_disk(struct imsm_disk *disk,
+                           int index,
+                           __u32 reserved,
+                           unsigned int sector_size) {
        char str[MAX_RAID_SERIAL_LEN + 1];
        __u64 sz;
 
@@ -1494,12 +1542,14 @@ static void print_imsm_disk(struct imsm_disk *disk, int index, __u32 reserved)
                printf("  Disk%02d Serial : %s\n", index, str);
        else
                printf("    Disk Serial : %s\n", str);
-       printf("          State :%s%s%s\n", is_spare(disk) ? " spare" : "",
-                                           is_configured(disk) ? " active" : "",
-                                           is_failed(disk) ? " failed" : "");
+       printf("          State :%s%s%s%s\n", is_spare(disk) ? " spare" : "",
+                                             is_configured(disk) ? " active" : "",
+                                             is_failed(disk) ? " failed" : "",
+                                             is_journal(disk) ? " journal" : "");
        printf("             Id : %08x\n", __le32_to_cpu(disk->scsi_id));
        sz = total_blocks(disk) - reserved;
-       printf("    Usable Size : %llu%s\n", (unsigned long long)sz,
+       printf("    Usable Size : %llu%s\n",
+              (unsigned long long)sz * 512 / sector_size,
               human_size(sz * 512));
 }
 
@@ -1808,7 +1858,8 @@ static void examine_super_imsm(struct supertype *st, char *homehost)
        __u32 reserved = imsm_reserved_sectors(super, super->disks);
        struct dl *dl;
 
-       snprintf(str, MPB_SIG_LEN, "%s", mpb->sig);
+       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));
@@ -1829,7 +1880,8 @@ static void examine_super_imsm(struct supertype *st, char *homehost)
        printf("    MPB Sectors : %d\n", mpb_sectors(mpb, super->sector_size));
        printf("          Disks : %d\n", mpb->num_disks);
        printf("   RAID Devices : %d\n", mpb->num_raid_devs);
-       print_imsm_disk(__get_imsm_disk(mpb, super->disks->index), super->disks->index, reserved);
+       print_imsm_disk(__get_imsm_disk(mpb, super->disks->index),
+                       super->disks->index, reserved, super->sector_size);
        if (get_imsm_bbm_log_size(super->bbm_log)) {
                struct bbm_log *log = super->bbm_log;
 
@@ -1851,12 +1903,14 @@ static void examine_super_imsm(struct supertype *st, char *homehost)
        for (i = 0; i < mpb->num_disks; i++) {
                if (i == super->disks->index)
                        continue;
-               print_imsm_disk(__get_imsm_disk(mpb, i), i, reserved);
+               print_imsm_disk(__get_imsm_disk(mpb, i), i, reserved,
+                               super->sector_size);
        }
 
        for (dl = super->disks; dl; dl = dl->next)
                if (dl->index == -1)
-                       print_imsm_disk(&dl->disk, -1, reserved);
+                       print_imsm_disk(&dl->disk, -1, reserved,
+                                       super->sector_size);
 
        examine_migr_rec_imsm(super);
 }
@@ -1980,7 +2034,7 @@ static void detail_super_imsm(struct supertype *st, char *homehost)
 
        getinfo_super_imsm(st, &info, NULL);
        fname_from_uuid(st, &info, nbuf, ':');
-       printf("\n           UUID : %s\n", nbuf + 5);
+       printf("\n              UUID : %s\n", nbuf + 5);
 }
 
 static void brief_detail_super_imsm(struct supertype *st)
@@ -3107,6 +3161,15 @@ static unsigned long long imsm_component_size_aligment_check(int level,
        return component_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)
 {
        struct intel_super *super = st->sb;
@@ -3133,7 +3196,7 @@ static void getinfo_super_imsm_volume(struct supertype *st, struct mdinfo *info,
        info->array.utime         = 0;
        info->array.chunk_size    =
                __le16_to_cpu(map_to_analyse->blocks_per_strip) << 9;
-       info->array.state         = !dev->vol.dirty;
+       info->array.state         = !(dev->vol.dirty & RAIDVOL_DIRTY);
        info->custom_array_size   = __le32_to_cpu(dev->size_high);
        info->custom_array_size   <<= 32;
        info->custom_array_size   |= __le32_to_cpu(dev->size_low);
@@ -3214,10 +3277,20 @@ static void getinfo_super_imsm_volume(struct supertype *st, struct mdinfo *info,
        memset(info->uuid, 0, sizeof(info->uuid));
        info->recovery_start = MaxSector;
 
+       if (info->array.level == 5 && dev->rwh_policy == RWH_DISTRIBUTED) {
+               info->consistency_policy = CONSISTENCY_POLICY_PPL;
+               info->ppl_sector = get_ppl_sector(super, super->current_vol);
+               info->ppl_size = (PPL_HEADER_SIZE + PPL_ENTRY_SPACE) >> 9;
+       } else if (info->array.level <= 0) {
+               info->consistency_policy = CONSISTENCY_POLICY_NONE;
+       } else {
+               info->consistency_policy = CONSISTENCY_POLICY_RESYNC;
+       }
+
        info->reshape_progress = 0;
        info->resync_start = MaxSector;
        if ((map_to_analyse->map_state == IMSM_T_STATE_UNINITIALIZED ||
-           dev->vol.dirty) &&
+           !(info->array.state & 1)) &&
            imsm_reshape_blocks_arrays_changes(super) == 0) {
                info->resync_start = 0;
        }
@@ -3444,7 +3517,8 @@ static void getinfo_super_imsm(struct supertype *st, struct mdinfo *info, char *
                 * found the 'most fresh' version of the metadata
                 */
                info->disk.state |= is_failed(disk) ? (1 << MD_DISK_FAULTY) : 0;
-               info->disk.state |= is_spare(disk) ? 0 : (1 << MD_DISK_SYNC);
+               info->disk.state |= (is_spare(disk) || is_journal(disk)) ?
+                                   0 : (1 << MD_DISK_SYNC);
        }
 
        /* only call uuid_from_super_imsm when this disk is part of a populated container,
@@ -3899,7 +3973,7 @@ load_imsm_disk(int fd, struct intel_super *super, char *devname, int keep_fd)
                 */
                if (is_failed(&dl->disk))
                        dl->index = -2;
-               else if (is_spare(&dl->disk))
+               else if (is_spare(&dl->disk) || is_journal(&dl->disk))
                        dl->index = -1;
        }
 
@@ -5148,7 +5222,7 @@ static int check_name(struct intel_super *super, char *name, int quiet)
 }
 
 static int init_super_imsm_volume(struct supertype *st, mdu_array_info_t *info,
-                                 unsigned long long size, char *name,
+                                 struct shape *s, char *name,
                                  char *homehost, int *uuid,
                                  long long data_offset)
 {
@@ -5221,7 +5295,7 @@ static int init_super_imsm_volume(struct supertype *st, mdu_array_info_t *info,
                        disk->status = CONFIGURED_DISK | FAILED_DISK;
                        disk->scsi_id = __cpu_to_le32(~(__u32)0);
                        snprintf((char *) disk->serial, MAX_RAID_SERIAL_LEN,
-                                "missing:%d", i);
+                                "missing:%d", (__u8)i);
                }
                find_missing(super);
        } else {
@@ -5243,7 +5317,7 @@ static int init_super_imsm_volume(struct supertype *st, mdu_array_info_t *info,
        strncpy((char *) dev->volume, name, MAX_RAID_SERIAL_LEN);
        array_blocks = calc_array_size(info->level, info->raid_disks,
                                               info->layout, info->chunk_size,
-                                              size * 2);
+                                              s->size * 2);
        /* round array size down to closest MB */
        array_blocks = (array_blocks >> SECT_PER_MB_SHIFT) << SECT_PER_MB_SHIFT;
 
@@ -5257,7 +5331,7 @@ static int init_super_imsm_volume(struct supertype *st, mdu_array_info_t *info,
        vol->curr_migr_unit = 0;
        map = get_imsm_map(dev, MAP_0);
        set_pba_of_lba0(map, super->create_offset);
-       set_blocks_per_member(map, info_to_blocks_per_member(info, size));
+       set_blocks_per_member(map, info_to_blocks_per_member(info, s->size));
        map->blocks_per_strip = __cpu_to_le16(info_to_blocks_per_strip(info));
        map->failed_disk_num = ~0;
        if (info->level > 0)
@@ -5285,7 +5359,7 @@ static int init_super_imsm_volume(struct supertype *st, mdu_array_info_t *info,
                map->num_domains = 1;
 
        /* info->size is only int so use the 'size' parameter instead */
-       num_data_stripes = (size * 2) / info_to_blocks_per_strip(info);
+       num_data_stripes = (s->size * 2) / info_to_blocks_per_strip(info);
        num_data_stripes /= map->num_domains;
        set_num_data_stripes(map, num_data_stripes);
 
@@ -5296,6 +5370,20 @@ static int init_super_imsm_volume(struct supertype *st, mdu_array_info_t *info,
        }
        mpb->num_raid_devs++;
 
+       if (s->consistency_policy == UnSet ||
+           s->consistency_policy == CONSISTENCY_POLICY_RESYNC ||
+           s->consistency_policy == CONSISTENCY_POLICY_NONE) {
+               dev->rwh_policy = RWH_OFF;
+       } else if (s->consistency_policy == CONSISTENCY_POLICY_PPL) {
+               dev->rwh_policy = RWH_DISTRIBUTED;
+       } else {
+               free(dev);
+               free(dv);
+               pr_err("imsm does not support consistency policy %s\n",
+                      map_num(consistency_policies, s->consistency_policy));
+               return 0;
+       }
+
        dv->dev = dev;
        dv->index = super->current_vol;
        dv->next = super->devlist;
@@ -5307,7 +5395,7 @@ static int init_super_imsm_volume(struct supertype *st, mdu_array_info_t *info,
 }
 
 static int init_super_imsm(struct supertype *st, mdu_array_info_t *info,
-                          unsigned long long size, char *name,
+                          struct shape *s, char *name,
                           char *homehost, int *uuid,
                           unsigned long long data_offset)
 {
@@ -5330,7 +5418,7 @@ static int init_super_imsm(struct supertype *st, mdu_array_info_t *info,
        }
 
        if (st->sb)
-               return init_super_imsm_volume(st, info, size, name, homehost, uuid,
+               return init_super_imsm_volume(st, info, s, name, homehost, uuid,
                                              data_offset);
 
        if (info)
@@ -5920,11 +6008,146 @@ static int mgmt_disk(struct supertype *st)
 
        return 0;
 }
+#endif
+
+__u32 crc32c_le(__u32 crc, unsigned char const *p, size_t len);
+
+static int write_init_ppl_imsm(struct supertype *st, struct mdinfo *info, int fd)
+{
+       struct intel_super *super = st->sb;
+       void *buf;
+       struct ppl_header *ppl_hdr;
+       int ret;
+
+       ret = posix_memalign(&buf, 4096, PPL_HEADER_SIZE);
+       if (ret) {
+               pr_err("Failed to allocate PPL header buffer\n");
+               return ret;
+       }
+
+       memset(buf, 0, PPL_HEADER_SIZE);
+       ppl_hdr = buf;
+       memset(ppl_hdr->reserved, 0xff, PPL_HDR_RESERVED);
+       ppl_hdr->signature = __cpu_to_le32(super->anchor->orig_family_num);
+       ppl_hdr->checksum = __cpu_to_le32(~crc32c_le(~0, buf, PPL_HEADER_SIZE));
+
+       if (lseek64(fd, info->ppl_sector * 512, SEEK_SET) < 0) {
+               ret = errno;
+               perror("Failed to seek to PPL header location");
+       }
+
+       if (!ret && write(fd, buf, PPL_HEADER_SIZE) != PPL_HEADER_SIZE) {
+               ret = errno;
+               perror("Write PPL header failed");
+       }
+
+       if (!ret)
+               fsync(fd);
+
+       free(buf);
+       return ret;
+}
+
+static int validate_ppl_imsm(struct supertype *st, struct mdinfo *info,
+                            struct mdinfo *disk)
+{
+       struct intel_super *super = st->sb;
+       struct dl *d;
+       void *buf;
+       int ret = 0;
+       struct ppl_header *ppl_hdr;
+       __u32 crc;
+       struct imsm_dev *dev;
+       struct imsm_map *map;
+       __u32 idx;
+
+       if (disk->disk.raid_disk < 0)
+               return 0;
+
+       if (posix_memalign(&buf, 4096, PPL_HEADER_SIZE)) {
+               pr_err("Failed to allocate PPL header buffer\n");
+               return -1;
+       }
+
+       dev = get_imsm_dev(super, info->container_member);
+       map = get_imsm_map(dev, MAP_X);
+       idx = get_imsm_disk_idx(dev, disk->disk.raid_disk, MAP_X);
+       d = get_imsm_dl_disk(super, idx);
+
+       if (!d || d->index < 0 || is_failed(&d->disk))
+               goto out;
+
+       if (lseek64(d->fd, info->ppl_sector * 512, SEEK_SET) < 0) {
+               perror("Failed to seek to PPL header location");
+               ret = -1;
+               goto out;
+       }
+
+       if (read(d->fd, buf, PPL_HEADER_SIZE) != PPL_HEADER_SIZE) {
+               perror("Read PPL header failed");
+               ret = -1;
+               goto out;
+       }
+
+       ppl_hdr = buf;
+
+       crc = __le32_to_cpu(ppl_hdr->checksum);
+       ppl_hdr->checksum = 0;
+
+       if (crc != ~crc32c_le(~0, buf, PPL_HEADER_SIZE)) {
+               dprintf("Wrong PPL header checksum on %s\n",
+                       d->devname);
+               ret = 1;
+       }
+
+       if (!ret && (__le32_to_cpu(ppl_hdr->signature) !=
+                     super->anchor->orig_family_num)) {
+               dprintf("Wrong PPL header signature on %s\n",
+                       d->devname);
+               ret = 1;
+       }
+
+out:
+       free(buf);
+
+       if (ret == 1 && map->map_state == IMSM_T_STATE_UNINITIALIZED)
+               return st->ss->write_init_ppl(st, info, d->fd);
+
+       return ret;
+}
+
+#ifndef MDASSEMBLE
+
+static int write_init_ppl_imsm_all(struct supertype *st, struct mdinfo *info)
+{
+       struct intel_super *super = st->sb;
+       struct dl *d;
+       int ret = 0;
+
+       if (info->consistency_policy != CONSISTENCY_POLICY_PPL ||
+           info->array.level != 5)
+               return 0;
+
+       for (d = super->disks; d ; d = d->next) {
+               if (d->index < 0 || is_failed(&d->disk))
+                       continue;
+
+               ret = st->ss->write_init_ppl(st, info, d->fd);
+               if (ret)
+                       break;
+       }
+
+       return ret;
+}
 
 static int write_init_super_imsm(struct supertype *st)
 {
        struct intel_super *super = st->sb;
        int current_vol = super->current_vol;
+       int rv = 0;
+       struct mdinfo info;
+
+       getinfo_super_imsm(st, &info, NULL);
 
        /* we are done with current_vol reset it to point st at the container */
        super->current_vol = -1;
@@ -5932,24 +6155,29 @@ static int write_init_super_imsm(struct supertype *st)
        if (st->update_tail) {
                /* queue the recently created array / added disk
                 * as a metadata update */
-               int rv;
 
                /* determine if we are creating a volume or adding a disk */
                if (current_vol < 0) {
                        /* in the mgmt (add/remove) disk case we are running
                         * in mdmon context, so don't close fd's
                         */
-                       return mgmt_disk(st);
-               } else
-                       rv = create_array(st, current_vol);
-
-               return rv;
+                       rv = mgmt_disk(st);
+               } else {
+                       rv = write_init_ppl_imsm_all(st, &info);
+                       if (!rv)
+                               rv = create_array(st, current_vol);
+               }
        } else {
                struct dl *d;
                for (d = super->disks; d; d = d->next)
                        Kill(d->devname, NULL, 0, -1, 1);
-               return write_super_imsm(st, 1);
+               if (current_vol >= 0)
+                       rv = write_init_ppl_imsm_all(st, &info);
+               if (!rv)
+                       rv = write_super_imsm(st, 1);
        }
+
+       return rv;
 }
 #endif
 
@@ -6483,20 +6711,20 @@ count_volumes_list(struct md_list *devlist, char *homehost,
        return count;
 }
 
-static int
-count_volumes(struct intel_hba *hba, int dpa, int verbose)
+static int __count_volumes(char *hba_path, int dpa, int verbose,
+                          int cmp_hba_path)
 {
        struct sys_dev *idev, *intel_devices = find_intel_devices();
        int count = 0;
        const struct orom_entry *entry;
        struct devid_list *dv, *devid_list;
 
-       if (!hba || !hba->path)
+       if (!hba_path)
                return 0;
 
        for (idev = intel_devices; idev; idev = idev->next) {
-               if (strstr(idev->path, hba->path))
-                               break;
+               if (strstr(idev->path, hba_path))
+                       break;
        }
 
        if (!idev || !idev->dev_id)
@@ -6510,22 +6738,28 @@ count_volumes(struct intel_hba *hba, int dpa, int verbose)
        devid_list = entry->devid_list;
        for (dv = devid_list; dv; dv = dv->next) {
                struct md_list *devlist;
-               struct sys_dev *device = device_by_id(dv->devid);
-               char *hba_path;
+               struct sys_dev *device = NULL;
+               char *hpath;
                int found = 0;
 
+               if (cmp_hba_path)
+                       device = device_by_id_and_path(dv->devid, hba_path);
+               else
+                       device = device_by_id(dv->devid);
+
                if (device)
-                       hba_path = device->path;
+                       hpath = device->path;
                else
                        return 0;
 
-               devlist = get_devices(hba_path);
+               devlist = get_devices(hpath);
                /* if no intel devices return zero volumes */
                if (devlist == NULL)
                        return 0;
 
-               count += active_arrays_by_format("imsm", hba_path, &devlist, dpa, verbose);
-               dprintf("path: %s active arrays: %d\n", hba_path, count);
+               count += active_arrays_by_format("imsm", hpath, &devlist, dpa,
+                                                verbose);
+               dprintf("path: %s active arrays: %d\n", hpath, count);
                if (devlist == NULL)
                        return 0;
                do  {
@@ -6537,7 +6771,7 @@ count_volumes(struct intel_hba *hba, int dpa, int verbose)
                        dprintf("found %d count: %d\n", found, count);
                } while (found);
 
-               dprintf("path: %s total number of volumes: %d\n", hba_path, count);
+               dprintf("path: %s total number of volumes: %d\n", hpath, count);
 
                while (devlist) {
                        struct md_list *dv = devlist;
@@ -6549,6 +6783,24 @@ count_volumes(struct intel_hba *hba, int dpa, int verbose)
        return count;
 }
 
+static int count_volumes(struct intel_hba *hba, int dpa, int verbose)
+{
+       if (!hba)
+               return 0;
+       if (hba->type == SYS_DEV_VMD) {
+               struct sys_dev *dev;
+               int count = 0;
+
+               for (dev = find_intel_devices(); dev; dev = dev->next) {
+                       if (dev->type == SYS_DEV_VMD)
+                               count += __count_volumes(dev->path, dpa,
+                                                        verbose, 1);
+               }
+               return count;
+       }
+       return __count_volumes(hba->path, dpa, verbose, 0);
+}
+
 static int imsm_default_chunk(const struct imsm_orom *orom)
 {
        /* up to 512 if the plaform supports it, otherwise the platform max.
@@ -6883,7 +7135,7 @@ 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 verbose)
+                                 int consistency_policy, int verbose)
 {
        int fd, cfd;
        struct mdinfo *sra;
@@ -7112,20 +7364,50 @@ static int update_subarray_imsm(struct supertype *st, char *subarray,
 
                        u->type = update_rename_array;
                        u->dev_idx = vol;
-                       snprintf((char *) u->name, MAX_RAID_SERIAL_LEN, "%s", name);
+                       strncpy((char *) u->name, name, MAX_RAID_SERIAL_LEN);
+                       u->name[MAX_RAID_SERIAL_LEN-1] = '\0';
                        append_metadata_update(st, u, sizeof(*u));
                } else {
                        struct imsm_dev *dev;
                        int i;
 
                        dev = get_imsm_dev(super, vol);
-                       snprintf((char *) dev->volume, MAX_RAID_SERIAL_LEN, "%s", name);
+                       strncpy((char *) dev->volume, name, MAX_RAID_SERIAL_LEN);
+                       dev->volume[MAX_RAID_SERIAL_LEN-1] = '\0';
                        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) {
+               int new_policy;
+               char *ep;
+               int vol = strtoul(subarray, &ep, 10);
+
+               if (*ep != '\0' || vol >= super->anchor->num_raid_devs)
+                       return 2;
+
+               if (strcmp(update, "ppl") == 0)
+                       new_policy = RWH_DISTRIBUTED;
+               else
+                       new_policy = RWH_OFF;
+
+               if (st->update_tail) {
+                       struct imsm_update_rwh_policy *u = xmalloc(sizeof(*u));
+
+                       u->type = update_rwh_policy;
+                       u->dev_idx = vol;
+                       u->new_policy = new_policy;
+                       append_metadata_update(st, u, sizeof(*u));
+               } else {
+                       struct imsm_dev *dev;
+
+                       dev = get_imsm_dev(super, vol);
+                       dev->rwh_policy = new_policy;
+                       super->updates_pending++;
+               }
        } else
                return 2;
 
@@ -7342,7 +7624,8 @@ static struct mdinfo *container_content_imsm(struct supertype *st, char *subarra
                         *
                         * FIXME handle dirty degraded
                         */
-                       if ((skip || recovery_start == 0) && !dev->vol.dirty)
+                       if ((skip || recovery_start == 0) &&
+                           !(dev->vol.dirty & RAIDVOL_DIRTY))
                                this->resync_start = MaxSector;
                        if (skip)
                                continue;
@@ -7377,9 +7660,12 @@ static struct mdinfo *container_content_imsm(struct supertype *st, char *subarra
                                info_d->component_size =
                                                num_data_stripes(map) *
                                                map->blocks_per_strip;
+                               info_d->ppl_sector = this->ppl_sector;
+                               info_d->ppl_size = this->ppl_size;
                        } else {
                                info_d->component_size = blocks_per_member(map);
                        }
+                       info_d->consistency_policy = this->consistency_policy;
 
                        info_d->bb.supported = 1;
                        get_volume_badblocks(super->bbm_log, ord_to_idx(ord),
@@ -7895,12 +8181,16 @@ mark_checkpoint:
 
 skip_mark_checkpoint:
        /* mark dirty / clean */
-       if (dev->vol.dirty != !consistent) {
+       if (((dev->vol.dirty & RAIDVOL_DIRTY) && consistent) ||
+           (!(dev->vol.dirty & RAIDVOL_DIRTY) && !consistent)) {
                dprintf("imsm: mark '%s'\n", consistent ? "clean" : "dirty");
-               if (consistent)
-                       dev->vol.dirty = 0;
-               else
-                       dev->vol.dirty = 1;
+               if (consistent) {
+                       dev->vol.dirty = RAIDVOL_CLEAN;
+               } else {
+                       dev->vol.dirty = RAIDVOL_DIRTY;
+                       if (dev->rwh_policy == RWH_DISTRIBUTED)
+                               dev->vol.dirty |= RAIDVOL_DSRECORD_VALID;
+               }
                super->updates_pending++;
        }
 
@@ -8412,6 +8702,11 @@ static struct mdinfo *imsm_activate_spare(struct active_array *a,
                di->component_size = a->info.component_size;
                di->container_member = inst;
                di->bb.supported = 1;
+               if (dev->rwh_policy == RWH_DISTRIBUTED) {
+                       di->consistency_policy = CONSISTENCY_POLICY_PPL;
+                       di->ppl_sector = get_ppl_sector(super, inst);
+                       di->ppl_size = (PPL_HEADER_SIZE + PPL_ENTRY_SPACE) >> 9;
+               }
                super->random = random32();
                di->next = rv;
                rv = di;
@@ -9339,6 +9634,21 @@ static void imsm_process_update(struct supertype *st,
        }
        case update_prealloc_badblocks_mem:
                break;
+       case update_rwh_policy: {
+               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;
+                       super->updates_pending++;
+               }
+               break;
+       }
        default:
                pr_err("error: unsuported process update type:(type: %d)\n",    type);
        }
@@ -9584,6 +9894,11 @@ static int imsm_prepare_update(struct supertype *st,
                super->extra_space += sizeof(struct bbm_log) -
                        get_imsm_bbm_log_size(super->bbm_log);
                break;
+       case update_rwh_policy: {
+               if (update->len < (int)sizeof(struct imsm_update_rwh_policy))
+                       return 0;
+               break;
+       }
        default:
                return 0;
        }
@@ -10688,7 +11003,7 @@ static int imsm_create_metadata_update_for_migration(
                        free(u);
                        sysfs_free(spares);
                        update_memory_size = 0;
-                       dprintf("error: cannot get spare device for requested migration");
+                       pr_err("cannot get spare device for requested migration\n");
                        return 0;
                }
                sysfs_free(spares);
@@ -10920,7 +11235,7 @@ enum imsm_reshape_type imsm_analyze_change(struct supertype *st,
                                    geo->raid_disks + devNumChange,
                                    &chunk,
                                    geo->size, INVALID_SECTORS,
-                                   0, 0, 1))
+                                   0, 0, info.consistency_policy, 1))
                change = -1;
 
        if (check_devs) {
@@ -11567,6 +11882,9 @@ struct superswitch super_imsm = {
        .container_content = container_content_imsm,
        .validate_container = validate_container_imsm,
 
+       .write_init_ppl = write_init_ppl_imsm,
+       .validate_ppl   = validate_ppl_imsm,
+
        .external       = 1,
        .name = "imsm",