]> git.ipfire.org Git - thirdparty/mdadm.git/blobdiff - super-intel.c
imsm: 4kn support for bad block log
[thirdparty/mdadm.git] / super-intel.c
index 401ba7569ff464038fa621b00a80468124dffc24..0407d43071f014ab4fcc1a6ae6af0a4eb260588c 100644 (file)
@@ -81,7 +81,8 @@
                                        MPB_ATTRIB_RAID1           | \
                                        MPB_ATTRIB_RAID10          | \
                                        MPB_ATTRIB_RAID5           | \
-                                       MPB_ATTRIB_EXP_STRIPE_SIZE)
+                                       MPB_ATTRIB_EXP_STRIPE_SIZE | \
+                                       MPB_ATTRIB_BBM)
 
 /* Define attributes that are unused but not harmful */
 #define MPB_ATTRIB_IGNORED             (MPB_ATTRIB_NEVER_USE)
@@ -363,6 +364,7 @@ struct intel_super {
                array, it indicates that mdmon is allowed to clean migration
                record */
        size_t len; /* size of the 'buf' allocation */
+       size_t extra_space; /* extra space in 'buf' that is not used yet */
        void *next_buf; /* for realloc'ing buf from the manager */
        size_t next_len;
        int updates_pending; /* count of pending updates for mdmon */
@@ -391,6 +393,7 @@ struct intel_super {
        struct intel_hba *hba; /* device path of the raid controller for this metadata */
        const struct imsm_orom *orom; /* platform firmware support */
        struct intel_super *next; /* (temp) list for disambiguating family_num */
+       struct md_bb bb;        /* memory for get_bad_blocks call */
 };
 
 struct intel_disk {
@@ -423,6 +426,7 @@ enum imsm_update_type {
        update_takeover,
        update_general_migration_checkpoint,
        update_size_change,
+       update_prealloc_badblocks_mem,
 };
 
 struct imsm_update_activate_spare {
@@ -511,6 +515,10 @@ struct imsm_update_add_remove_disk {
        enum imsm_update_type type;
 };
 
+struct imsm_update_prealloc_bb_mem {
+       enum imsm_update_type type;
+};
+
 static const char *_sys_dev_type[] = {
        [SYS_DEV_UNKNOWN] = "Unknown",
        [SYS_DEV_SAS] = "SAS",
@@ -817,6 +825,126 @@ static __u32 get_imsm_bbm_log_size(struct bbm_log *log)
                sizeof(log->entry_count) +
                log->entry_count * sizeof(struct bbm_log_entry);
 }
+
+/* check if bad block is not partially stored in bbm log */
+static int is_stored_in_bbm(struct bbm_log *log, const __u8 idx, const unsigned
+                           long long sector, const int length, __u32 *pos)
+{
+       __u32 i;
+
+       for (i = *pos; i < log->entry_count; i++) {
+               struct bbm_log_entry *entry = &log->marked_block_entries[i];
+               unsigned long long bb_start;
+               unsigned long long bb_end;
+
+               bb_start = __le48_to_cpu(&entry->defective_block_start);
+               bb_end = bb_start + (entry->marked_count + 1);
+
+               if ((entry->disk_ordinal == idx) && (bb_start >= sector) &&
+                   (bb_end <= sector + length)) {
+                       *pos = i;
+                       return 1;
+               }
+       }
+       return 0;
+}
+
+/* record new bad block in bbm log */
+static int record_new_badblock(struct bbm_log *log, const __u8 idx, unsigned
+                              long long sector, int length)
+{
+       int new_bb = 0;
+       __u32 pos = 0;
+       struct bbm_log_entry *entry = NULL;
+
+       while (is_stored_in_bbm(log, idx, sector, length, &pos)) {
+               struct bbm_log_entry *e = &log->marked_block_entries[pos];
+
+               if ((e->marked_count + 1 == BBM_LOG_MAX_LBA_ENTRY_VAL) &&
+                   (__le48_to_cpu(&e->defective_block_start) == sector)) {
+                       sector += BBM_LOG_MAX_LBA_ENTRY_VAL;
+                       length -= BBM_LOG_MAX_LBA_ENTRY_VAL;
+                       pos = pos + 1;
+                       continue;
+               }
+               entry = e;
+               break;
+       }
+
+       if (entry) {
+               int cnt = (length <= BBM_LOG_MAX_LBA_ENTRY_VAL) ? length :
+                       BBM_LOG_MAX_LBA_ENTRY_VAL;
+               entry->defective_block_start = __cpu_to_le48(sector);
+               entry->marked_count = cnt - 1;
+               if (cnt == length)
+                       return 1;
+               sector += cnt;
+               length -= cnt;
+       }
+
+       new_bb = ROUND_UP(length, BBM_LOG_MAX_LBA_ENTRY_VAL) /
+               BBM_LOG_MAX_LBA_ENTRY_VAL;
+       if (log->entry_count + new_bb > BBM_LOG_MAX_ENTRIES)
+               return 0;
+
+       while (length > 0) {
+               int cnt = (length <= BBM_LOG_MAX_LBA_ENTRY_VAL) ? length :
+                       BBM_LOG_MAX_LBA_ENTRY_VAL;
+               struct bbm_log_entry *entry =
+                       &log->marked_block_entries[log->entry_count];
+
+               entry->defective_block_start = __cpu_to_le48(sector);
+               entry->marked_count = cnt - 1;
+               entry->disk_ordinal = idx;
+
+               sector += cnt;
+               length -= cnt;
+
+               log->entry_count++;
+       }
+
+       return new_bb;
+}
+
+/* clear all bad blocks for given disk */
+static void clear_disk_badblocks(struct bbm_log *log, const __u8 idx)
+{
+       __u32 i = 0;
+
+       while (i < log->entry_count) {
+               struct bbm_log_entry *entries = log->marked_block_entries;
+
+               if (entries[i].disk_ordinal == idx) {
+                       if (i < log->entry_count - 1)
+                               entries[i] = entries[log->entry_count - 1];
+                       log->entry_count--;
+               } else {
+                       i++;
+               }
+       }
+}
+
+/* clear given bad block */
+static int clear_badblock(struct bbm_log *log, const __u8 idx, const unsigned
+                         long long sector, const int length) {
+       __u32 i = 0;
+
+       while (i < log->entry_count) {
+               struct bbm_log_entry *entries = log->marked_block_entries;
+
+               if ((entries[i].disk_ordinal == idx) &&
+                   (__le48_to_cpu(&entries[i].defective_block_start) ==
+                    sector) && (entries[i].marked_count + 1 == length)) {
+                       if (i < log->entry_count - 1)
+                               entries[i] = entries[log->entry_count - 1];
+                       log->entry_count--;
+                       break;
+               }
+               i++;
+       }
+
+       return 1;
+}
 #endif /* MDASSEMBLE */
 
 /* allocate and load BBM log from metadata */
@@ -858,6 +986,56 @@ static int load_bbm_log(struct intel_super *super)
        return 0;
 }
 
+/* checks if bad block is within volume boundaries */
+static int is_bad_block_in_volume(const struct bbm_log_entry *entry,
+                       const unsigned long long start_sector,
+                       const unsigned long long size)
+{
+       unsigned long long bb_start;
+       unsigned long long bb_end;
+
+       bb_start = __le48_to_cpu(&entry->defective_block_start);
+       bb_end = bb_start + (entry->marked_count + 1);
+
+       if (((bb_start >= start_sector) && (bb_start < start_sector + size)) ||
+           ((bb_end >= start_sector) && (bb_end <= start_sector + size)))
+               return 1;
+
+       return 0;
+}
+
+/* get list of bad blocks on a drive for a volume */
+static void get_volume_badblocks(const struct bbm_log *log, const __u8 idx,
+                       const unsigned long long start_sector,
+                       const unsigned long long size,
+                       struct md_bb *bbs)
+{
+       __u32 count = 0;
+       __u32 i;
+
+       for (i = 0; i < log->entry_count; i++) {
+               const struct bbm_log_entry *ent =
+                       &log->marked_block_entries[i];
+               struct md_bb_entry *bb;
+
+               if ((ent->disk_ordinal == idx) &&
+                   is_bad_block_in_volume(ent, start_sector, size)) {
+
+                       if (!bbs->entries) {
+                               bbs->entries = xmalloc(BBM_LOG_MAX_ENTRIES *
+                                                    sizeof(*bb));
+                               if (!bbs->entries)
+                                       break;
+                       }
+
+                       bb = &bbs->entries[count++];
+                       bb->sector = __le48_to_cpu(&ent->defective_block_start);
+                       bb->length = ent->marked_count + 1;
+               }
+       }
+       bbs->count = count;
+}
+
 /*
  * for second_map:
  *  == MAP_0 get first map
@@ -1348,6 +1526,7 @@ void convert_to_4k(struct intel_super *super)
        struct imsm_super *mpb = super->anchor;
        struct imsm_disk *disk;
        int i;
+       __u32 bbm_log_size = __le32_to_cpu(mpb->bbm_log_size);
 
        for (i = 0; i < mpb->num_disks ; i++) {
                disk = __get_imsm_disk(mpb, i);
@@ -1376,6 +1555,24 @@ void convert_to_4k(struct intel_super *super)
                        set_pba_of_lba0(map, pba_of_lba0(map)/IMSM_4K_DIV);
                }
        }
+       if (bbm_log_size) {
+               struct bbm_log *log = (void *)mpb +
+                       __le32_to_cpu(mpb->mpb_size) - bbm_log_size;
+               __u32 i;
+
+               for (i = 0; i < log->entry_count; i++) {
+                       struct bbm_log_entry *entry =
+                               &log->marked_block_entries[i];
+
+                       __u8 count = entry->marked_count + 1;
+                       unsigned long long sector =
+                               __le48_to_cpu(&entry->defective_block_start);
+
+                       entry->defective_block_start =
+                               __cpu_to_le48(sector/IMSM_4K_DIV);
+                       entry->marked_count = max(count/IMSM_4K_DIV, 1) - 1;
+               }
+       }
 
        mpb->check_sum = __gen_imsm_checksum(mpb);
 }
@@ -1457,6 +1654,7 @@ void convert_from_4k(struct intel_super *super)
        struct imsm_super *mpb = super->anchor;
        struct imsm_disk *disk;
        int i;
+       __u32 bbm_log_size = __le32_to_cpu(mpb->bbm_log_size);
 
        for (i = 0; i < mpb->num_disks ; i++) {
                disk = __get_imsm_disk(mpb, i);
@@ -1486,6 +1684,24 @@ void convert_from_4k(struct intel_super *super)
                        set_pba_of_lba0(map, pba_of_lba0(map)*IMSM_4K_DIV);
                }
        }
+       if (bbm_log_size) {
+               struct bbm_log *log = (void *)mpb +
+                       __le32_to_cpu(mpb->mpb_size) - bbm_log_size;
+               __u32 i;
+
+               for (i = 0; i < log->entry_count; i++) {
+                       struct bbm_log_entry *entry =
+                               &log->marked_block_entries[i];
+
+                       __u8 count = entry->marked_count + 1;
+                       unsigned long long sector =
+                               __le48_to_cpu(&entry->defective_block_start);
+
+                       entry->defective_block_start =
+                               __cpu_to_le48(sector*IMSM_4K_DIV);
+                       entry->marked_count = count*IMSM_4K_DIV - 1;
+               }
+       }
 
        mpb->check_sum = __gen_imsm_checksum(mpb);
 }
@@ -2994,6 +3210,7 @@ static void getinfo_super_imsm_volume(struct supertype *st, struct mdinfo *info,
                                                        info->array.chunk_size,
                                                        super->sector_size,
                                                        info->component_size);
+       info->bb.supported = 0;
 
        memset(info->uuid, 0, sizeof(info->uuid));
        info->recovery_start = MaxSector;
@@ -3160,6 +3377,7 @@ static void getinfo_super_imsm(struct supertype *st, struct mdinfo *info, char *
        info->name[0] = 0;
        info->recovery_start = MaxSector;
        info->recovery_blocked = imsm_reshape_blocks_arrays_changes(st->sb);
+       info->bb.supported = 0;
 
        /* do we have the all the insync disks that we expect? */
        mpb = super->anchor;
@@ -3364,6 +3582,8 @@ static size_t disks_to_mpb_size(int disks)
        size += (4 - 2) * sizeof(struct imsm_map);
        /* 4 possible disk_ord_tbl's */
        size += 4 * (disks - 1) * sizeof(__u32);
+       /* maximum bbm log */
+       size += sizeof(struct bbm_log);
 
        return size;
 }
@@ -3825,6 +4045,8 @@ static int parse_raid_devices(struct intel_super *super)
                super->len = len;
        }
 
+       super->extra_space += space_needed;
+
        return 0;
 }
 
@@ -4100,6 +4322,7 @@ static void __free_imsm(struct intel_super *super, int free_disks)
 static void free_imsm(struct intel_super *super)
 {
        __free_imsm(super, 1);
+       free(super->bb.entries);
        free(super);
 }
 
@@ -4120,6 +4343,14 @@ static struct intel_super *alloc_super(void)
 
        super->current_vol = -1;
        super->create_offset = ~((unsigned long long) 0);
+
+       super->bb.entries = xmalloc(BBM_LOG_MAX_ENTRIES *
+                                  sizeof(struct md_bb_entry));
+       if (!super->bb.entries) {
+               free(super);
+               return NULL;
+       }
+
        return super;
 }
 
@@ -4967,6 +5198,7 @@ static int init_super_imsm_volume(struct supertype *st, mdu_array_info_t *info,
                super->anchor = mpb_new;
                mpb->mpb_size = __cpu_to_le32(size_new);
                memset(mpb_new + size_old, 0, size_round - size_old);
+               super->len = size_round;
        }
        super->current_vol = idx;
 
@@ -5527,6 +5759,7 @@ static int write_super_imsm(struct supertype *st, int doclose)
        __u32 mpb_size = sizeof(struct imsm_super) - sizeof(struct imsm_disk);
        int num_disks = 0;
        int clear_migration_record = 1;
+       __u32 bbm_log_size;
 
        /* 'generation' is incremented everytime the metadata is written */
        generation = __le32_to_cpu(mpb->generation_num);
@@ -5564,9 +5797,23 @@ static int write_super_imsm(struct supertype *st, int doclose)
                if (is_gen_migration(dev2))
                        clear_migration_record = 0;
        }
-       mpb_size += __le32_to_cpu(mpb->bbm_log_size);
+
+       bbm_log_size = get_imsm_bbm_log_size(super->bbm_log);
+
+       if (bbm_log_size) {
+               memcpy((void *)mpb + mpb_size, super->bbm_log, bbm_log_size);
+               mpb->attributes |= MPB_ATTRIB_BBM;
+       } else
+               mpb->attributes &= ~MPB_ATTRIB_BBM;
+
+       super->anchor->bbm_log_size = __cpu_to_le32(bbm_log_size);
+       mpb_size += bbm_log_size;
        mpb->mpb_size = __cpu_to_le32(mpb_size);
 
+#ifdef DEBUG
+       assert(super->len == 0 || mpb_size <= super->len);
+#endif
+
        /* recalculate checksum */
        sum = __gen_imsm_checksum(mpb);
        mpb->check_sum = __cpu_to_le32(sum);
@@ -7125,6 +7372,12 @@ static struct mdinfo *container_content_imsm(struct supertype *st, char *subarra
                        } else {
                                info_d->component_size = blocks_per_member(map);
                        }
+
+                       info_d->bb.supported = 0;
+                       get_volume_badblocks(super->bbm_log, ord_to_idx(ord),
+                                            info_d->data_offset,
+                                            info_d->component_size,
+                                            &info_d->bb);
                }
                /* now that the disk list is up-to-date fixup recovery_start */
                update_recovery_start(super, dev, this);
@@ -7270,6 +7523,7 @@ static int imsm_open_new(struct supertype *c, struct active_array *a,
 {
        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));
@@ -7278,6 +7532,10 @@ static int imsm_open_new(struct supertype *c, struct active_array *a,
 
        dprintf("imsm: open_new %s\n", inst);
        a->info.container_member = atoi(inst);
+
+       u.type = update_prealloc_badblocks_mem;
+       imsm_update_metadata_locally(c, &u, sizeof(u));
+
        return 0;
 }
 
@@ -7305,7 +7563,8 @@ static int is_resyncing(struct imsm_dev *dev)
 }
 
 /* return true if we recorded new information */
-static int mark_failure(struct imsm_dev *dev, struct imsm_disk *disk, int idx)
+static int mark_failure(struct intel_super *super,
+                       struct imsm_dev *dev, struct imsm_disk *disk, int idx)
 {
        __u32 ord;
        int slot;
@@ -7347,12 +7606,16 @@ static int mark_failure(struct imsm_dev *dev, struct imsm_disk *disk, int idx)
        }
        if (map->failed_disk_num == 0xff)
                map->failed_disk_num = slot;
+
+       clear_disk_badblocks(super->bbm_log, ord_to_idx(ord));
+
        return 1;
 }
 
-static void mark_missing(struct imsm_dev *dev, struct imsm_disk *disk, int idx)
+static void mark_missing(struct intel_super *super,
+                        struct imsm_dev *dev, struct imsm_disk *disk, int idx)
 {
-       mark_failure(dev, disk, idx);
+       mark_failure(super, dev, disk, idx);
 
        if (disk->scsi_id == __cpu_to_le32(~(__u32)0))
                return;
@@ -7388,7 +7651,7 @@ static void handle_missing(struct intel_super *super, struct imsm_dev *dev)
                        end_migration(dev, super, map_state);
        }
        for (dl = super->missing; dl; dl = dl->next)
-               mark_missing(dev, &dl->disk, dl->index);
+               mark_missing(super, dev, &dl->disk, dl->index);
        super->updates_pending++;
 }
 
@@ -7636,6 +7899,25 @@ skip_mark_checkpoint:
        return consistent;
 }
 
+static int imsm_disk_slot_to_ord(struct active_array *a, int slot)
+{
+       int inst = a->info.container_member;
+       struct intel_super *super = a->container->sb;
+       struct imsm_dev *dev = get_imsm_dev(super, inst);
+       struct imsm_map *map = get_imsm_map(dev, MAP_0);
+
+       if (slot > map->num_members) {
+               pr_err("imsm: imsm_disk_slot_to_ord %d out of range 0..%d\n",
+                      slot, map->num_members - 1);
+               return -1;
+       }
+
+       if (slot < 0)
+               return -1;
+
+       return get_imsm_ord_tbl_ent(dev, slot, MAP_0);
+}
+
 static void imsm_set_disk(struct active_array *a, int n, int state)
 {
        int inst = a->info.container_member;
@@ -7646,24 +7928,19 @@ static void imsm_set_disk(struct active_array *a, int n, int state)
        struct mdinfo *mdi;
        int recovery_not_finished = 0;
        int failed;
-       __u32 ord;
+       int ord;
        __u8 map_state;
 
-       if (n > map->num_members)
-               pr_err("imsm: set_disk %d out of range 0..%d\n",
-                       n, map->num_members - 1);
-
-       if (n < 0)
+       ord = imsm_disk_slot_to_ord(a, n);
+       if (ord < 0)
                return;
 
        dprintf("imsm: set_disk %d:%x\n", n, state);
-
-       ord = get_imsm_ord_tbl_ent(dev, n, MAP_0);
        disk = get_imsm_disk(super, ord_to_idx(ord));
 
        /* check for new failures */
        if (state & DS_FAULTY) {
-               if (mark_failure(dev, disk, ord_to_idx(ord)))
+               if (mark_failure(super, dev, disk, ord_to_idx(ord)))
                        super->updates_pending++;
        }
 
@@ -8126,6 +8403,7 @@ static struct mdinfo *imsm_activate_spare(struct active_array *a,
                di->data_offset = pba_of_lba0(map);
                di->component_size = a->info.component_size;
                di->container_member = inst;
+               di->bb.supported = 0;
                super->random = random32();
                di->next = rv;
                rv = di;
@@ -8760,7 +9038,7 @@ static int apply_takeover_update(struct imsm_update_takeover *u,
        for (du = super->missing; du; du = du->next)
                if (du->index >= 0) {
                        set_imsm_ord_tbl_ent(map, du->index, du->index);
-                       mark_missing(dv->dev, &du->disk, du->index);
+                       mark_missing(super, dv->dev, &du->disk, du->index);
                }
 
        return 1;
@@ -9051,6 +9329,8 @@ static void imsm_process_update(struct supertype *st,
                }
                break;
        }
+       case update_prealloc_badblocks_mem:
+               break;
        default:
                pr_err("error: unsuported process update type:(type: %d)\n",    type);
        }
@@ -9292,6 +9572,10 @@ static int imsm_prepare_update(struct supertype *st,
        case update_add_remove_disk:
                /* no update->len needed */
                break;
+       case update_prealloc_badblocks_mem:
+               super->extra_space += sizeof(struct bbm_log) -
+                       get_imsm_bbm_log_size(super->bbm_log);
+               break;
        default:
                return 0;
        }
@@ -9302,13 +9586,13 @@ static int imsm_prepare_update(struct supertype *st,
        else
                buf_len = super->len;
 
-       if (__le32_to_cpu(mpb->mpb_size) + len > buf_len) {
+       if (__le32_to_cpu(mpb->mpb_size) + super->extra_space + len > buf_len) {
                /* ok we need a larger buf than what is currently allocated
                 * if this allocation fails process_update will notice that
                 * ->next_len is set and ->next_buf is NULL
                 */
-               buf_len = ROUND_UP(__le32_to_cpu(mpb->mpb_size) + len,
-                                 sector_size);
+               buf_len = ROUND_UP(__le32_to_cpu(mpb->mpb_size) +
+                                  super->extra_space + len, sector_size);
                if (super->next_buf)
                        free(super->next_buf);
 
@@ -9328,8 +9612,9 @@ static void imsm_delete(struct intel_super *super, struct dl **dlp, unsigned ind
        struct dl *iter;
        struct imsm_dev *dev;
        struct imsm_map *map;
-       int i, j, num_members;
+       unsigned int i, j, num_members;
        __u32 ord;
+       struct bbm_log *log = super->bbm_log;
 
        dprintf("deleting device[%d] from imsm_super\n", index);
 
@@ -9362,6 +9647,14 @@ static void imsm_delete(struct intel_super *super, struct dl **dlp, unsigned ind
                }
        }
 
+       for (i = 0; i < log->entry_count; i++) {
+               struct bbm_log_entry *entry = &log->marked_block_entries[i];
+
+               if (entry->disk_ordinal <= index)
+                       continue;
+               entry->disk_ordinal--;
+       }
+
        mpb->num_disks--;
        super->updates_pending++;
        if (*dlp) {
@@ -9575,6 +9868,150 @@ int validate_container_imsm(struct mdinfo *info)
        return 0;
 }
 #ifndef MDASSEMBLE
+/*******************************************************************************
+* Function:   imsm_record_badblock
+* Description: This routine stores new bad block record in BBM log
+*
+* Parameters:
+*     a                : array containing a bad block
+*     slot     : disk number containing a bad block
+*     sector   : bad block sector
+*     length   : bad block sectors range
+* Returns:
+*     1 : Success
+*     0 : Error
+******************************************************************************/
+static int imsm_record_badblock(struct active_array *a, int slot,
+                         unsigned long long sector, int length)
+{
+       struct intel_super *super = a->container->sb;
+       int ord;
+       int ret;
+
+       ord = imsm_disk_slot_to_ord(a, slot);
+       if (ord < 0)
+               return 0;
+
+       ret = record_new_badblock(super->bbm_log, ord_to_idx(ord), sector,
+                                  length);
+       if (ret)
+               super->updates_pending++;
+
+       return ret;
+}
+/*******************************************************************************
+* Function:   imsm_clear_badblock
+* Description: This routine clears bad block record from BBM log
+*
+* Parameters:
+*     a                : array containing a bad block
+*     slot     : disk number containing a bad block
+*     sector   : bad block sector
+*     length   : bad block sectors range
+* Returns:
+*     1 : Success
+*     0 : Error
+******************************************************************************/
+static int imsm_clear_badblock(struct active_array *a, int slot,
+                       unsigned long long sector, int length)
+{
+       struct intel_super *super = a->container->sb;
+       int ord;
+       int ret;
+
+       ord = imsm_disk_slot_to_ord(a, slot);
+       if (ord < 0)
+               return 0;
+
+       ret = clear_badblock(super->bbm_log, ord_to_idx(ord), sector, length);
+       if (ret)
+               super->updates_pending++;
+
+       return ret;
+}
+/*******************************************************************************
+* Function:   imsm_get_badblocks
+* Description: This routine get list of bad blocks for an array
+*
+* Parameters:
+*     a                : array
+*     slot     : disk number
+* Returns:
+*     bb       : structure containing bad blocks
+*     NULL     : error
+******************************************************************************/
+static struct md_bb *imsm_get_badblocks(struct active_array *a, int slot)
+{
+       int inst = a->info.container_member;
+       struct intel_super *super = a->container->sb;
+       struct imsm_dev *dev = get_imsm_dev(super, inst);
+       struct imsm_map *map = get_imsm_map(dev, MAP_0);
+       int ord;
+
+       ord = imsm_disk_slot_to_ord(a, slot);
+       if (ord < 0)
+               return NULL;
+
+       get_volume_badblocks(super->bbm_log, ord_to_idx(ord), pba_of_lba0(map),
+                            blocks_per_member(map), &super->bb);
+
+       return &super->bb;
+}
+/*******************************************************************************
+* Function:   examine_badblocks_imsm
+* Description: Prints list of bad blocks on a disk to the standard output
+*
+* Parameters:
+*     st       : metadata handler
+*     fd       : open file descriptor for device
+*     devname  : device name
+* Returns:
+*     0 : Success
+*     1 : Error
+******************************************************************************/
+static int examine_badblocks_imsm(struct supertype *st, int fd, char *devname)
+{
+       struct intel_super *super = st->sb;
+       struct bbm_log *log = super->bbm_log;
+       struct dl *d = NULL;
+       int any = 0;
+
+       for (d = super->disks; d ; d = d->next) {
+               if (strcmp(d->devname, devname) == 0)
+                       break;
+       }
+
+       if ((d == NULL) || (d->index < 0)) { /* serial mismatch probably */
+               pr_err("%s doesn't appear to be part of a raid array\n",
+                      devname);
+               return 1;
+       }
+
+       if (log != NULL) {
+               unsigned int i;
+               struct bbm_log_entry *entry = &log->marked_block_entries[0];
+
+               for (i = 0; i < log->entry_count; i++) {
+                       if (entry[i].disk_ordinal == d->index) {
+                               unsigned long long sector = __le48_to_cpu(
+                                       &entry[i].defective_block_start);
+                               int cnt = entry[i].marked_count + 1;
+
+                               if (!any) {
+                                       printf("Bad-blocks on %s:\n", devname);
+                                       any = 1;
+                               }
+
+                               printf("%20llu for %d sectors\n", sector, cnt);
+                       }
+               }
+       }
+
+       if (!any)
+               printf("No bad-blocks list configured on %s\n", devname);
+
+       return 0;
+}
 /*******************************************************************************
  * Function:   init_migr_record_imsm
  * Description:        Function inits imsm migration record
@@ -11096,6 +11533,7 @@ struct superswitch super_imsm = {
        .manage_reshape = imsm_manage_reshape,
        .recover_backup = recover_backup_imsm,
        .copy_metadata = copy_metadata_imsm,
+       .examine_badblocks = examine_badblocks_imsm,
 #endif
        .match_home     = match_home_imsm,
        .uuid_from_super= uuid_from_super_imsm,
@@ -11128,5 +11566,8 @@ struct superswitch super_imsm = {
        .activate_spare = imsm_activate_spare,
        .process_update = imsm_process_update,
        .prepare_update = imsm_prepare_update,
+       .record_bad_block = imsm_record_badblock,
+       .clear_bad_block  = imsm_clear_badblock,
+       .get_bad_blocks   = imsm_get_badblocks,
 #endif /* MDASSEMBLE */
 };