#define MPB_SECTOR_CNT 2210
#define IMSM_RESERVED_SECTORS 4096
+#define NUM_BLOCKS_DIRTY_STRIPE_REGION 2056
#define SECT_PER_MB_SHIFT 11
/* Disk configuration info. */
return memberships;
}
+static __u32 imsm_min_reserved_sectors(struct intel_super *super);
+
static struct extent *get_extents(struct intel_super *super, struct dl *dl)
{
/* find a list of used extents on the given physical device */
* IMSM_RESERVED_SECTORS region
*/
if (dl->index == -1)
- reservation = MPB_SECTOR_CNT;
+ reservation = imsm_min_reserved_sectors(super);
else
reservation = MPB_SECTOR_CNT + IMSM_RESERVED_SECTORS;
return (disk->status & FAILED_DISK) == FAILED_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
+ */
+static __u32 imsm_min_reserved_sectors(struct intel_super *super)
+{
+ struct extent *e;
+ int i;
+ __u32 min_active, remainder;
+ __u32 rv = MPB_SECTOR_CNT + IMSM_RESERVED_SECTORS;
+ struct dl *dl, *dl_min = NULL;
+
+ if (!super)
+ return rv;
+
+ min_active = 0;
+ for (dl = super->disks; dl; dl = dl->next) {
+ if (dl->index < 0)
+ continue;
+ if (dl->disk.total_blocks < min_active || min_active == 0) {
+ dl_min = dl;
+ min_active = dl->disk.total_blocks;
+ }
+ }
+ if (!dl_min)
+ return rv;
+
+ /* find last lba used by subarrays on the smallest active disk */
+ e = get_extents(super, dl_min);
+ if (!e)
+ return rv;
+ for (i = 0; e[i].size; i++)
+ continue;
+
+ remainder = min_active - e[i].start;
+ free(e);
+
+ /* to give priority to recovery we should not require full
+ IMSM_RESERVED_SECTORS from the spare */
+ rv = MPB_SECTOR_CNT + NUM_BLOCKS_DIRTY_STRIPE_REGION;
+
+ /* if real reservation is smaller use that value */
+ return (remainder < rv) ? remainder : rv;
+}
+
/* Return minimum size of a spare that can be used in this array*/
static unsigned long long min_acceptable_spare_size_imsm(struct supertype *st)
{
struct extent *e;
int i;
unsigned long long rv = 0;
+ __u32 reservation;
if (!super)
return rv;
continue;
if (i > 0)
rv = e[i-1].start + e[i-1].size;
+ reservation = __le32_to_cpu(dl->disk.total_blocks) - e[i].start;
free(e);
+
/* add the amount of space needed for metadata */
- rv = rv + MPB_SECTOR_CNT + IMSM_RESERVED_SECTORS;
+ rv = rv + imsm_min_reserved_sectors(super);
+
return rv * 512;
}
}
#endif /* MDASSEMBLE */
+/* spare/missing disks activations are not allowe when
+ * array/container performs reshape operation, because
+ * all arrays in container works on the same disks set
+ */
+int imsm_reshape_blocks_arrays_changes(struct intel_super *super)
+{
+ int rv = 0;
+ struct intel_dev *i_dev;
+ struct imsm_dev *dev;
+
+ /* check whole container
+ */
+ for (i_dev = super->devlist; i_dev; i_dev = i_dev->next) {
+ dev = i_dev->dev;
+ if (dev->vol.migr_state &&
+ dev->vol.migr_type == MIGR_GEN_MIGR) {
+ /* No repair during any migration in container
+ */
+ rv = 1;
+ break;
+ }
+ }
+ return rv;
+}
+
static void getinfo_super_imsm_volume(struct supertype *st, struct mdinfo *info, char *dmap)
{
struct intel_super *super = st->sb;
info->reshape_progress = 0;
info->resync_start = MaxSector;
- if (map_to_analyse->map_state == IMSM_T_STATE_UNINITIALIZED ||
- dev->vol.dirty) {
+ if ((map_to_analyse->map_state == IMSM_T_STATE_UNINITIALIZED ||
+ dev->vol.dirty) &&
+ imsm_reshape_blocks_arrays_changes(super) == 0) {
info->resync_start = 0;
}
if (dev->vol.migr_state) {
strncpy((char *) dest, (char *) src, MAX_RAID_SERIAL_LEN);
}
-#ifndef MDASSEMBLE
static struct dl *serial_to_dl(__u8 *serial, struct intel_super *super)
{
struct dl *dl;
return dl;
}
-#endif
static struct imsm_disk *
__serial_to_disk(__u8 *serial, struct imsm_super *mpb, int *idx)
return 0;
}
-#ifndef MDASSEMBLE
/* find_missing - helper routine for load_super_imsm_all that identifies
* disks that have disappeared from the system. This routine relies on
* the mpb being uptodate, which it is at load time.
return 0;
}
+#ifndef MDASSEMBLE
static struct intel_disk *disk_list_get(__u8 *serial, struct intel_disk *disk_list)
{
struct intel_disk *idisk = disk_list;
memset(mpb_new + size_old, 0, size_round - size_old);
}
super->current_vol = idx;
- /* when creating the first raid device in this container set num_disks
- * to zero, i.e. delete this spare and add raid member devices in
- * add_to_super_imsm_volume()
+
+ /* handle 'failed_disks' by either:
+ * a) create dummy disk entries in the table if this the first
+ * volume in the array. We add them here as this is the only
+ * opportunity to add them. add_to_super_imsm_volume()
+ * handles the non-failed disks and continues incrementing
+ * mpb->num_disks.
+ * b) validate that 'failed_disks' matches the current number
+ * of missing disks if the container is populated
*/
- if (super->current_vol == 0)
+ if (super->current_vol == 0) {
mpb->num_disks = 0;
+ for (i = 0; i < info->failed_disks; i++) {
+ struct imsm_disk *disk;
+
+ mpb->num_disks++;
+ disk = __get_imsm_disk(mpb, i);
+ 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);
+ }
+ find_missing(super);
+ } else {
+ int missing = 0;
+ struct dl *d;
+
+ for (d = super->missing; d; d = d->next)
+ missing++;
+ if (info->failed_disks > missing) {
+ fprintf(stderr, Name": unable to add 'missing' disk to container\n");
+ return 0;
+ }
+ }
if (!check_name(super, name, 0))
return 0;
vol = &dev->vol;
vol->migr_state = 0;
set_migr_type(dev, MIGR_INIT);
- vol->dirty = 0;
+ vol->dirty = !info->state;
vol->curr_migr_unit = 0;
map = get_imsm_map(dev, 0);
map->pba_of_lba0 = __cpu_to_le32(super->create_offset);
map->blocks_per_member = __cpu_to_le32(info_to_blocks_per_member(info));
map->blocks_per_strip = __cpu_to_le16(info_to_blocks_per_strip(info));
map->failed_disk_num = ~0;
- map->map_state = info->level ? IMSM_T_STATE_UNINITIALIZED :
- IMSM_T_STATE_NORMAL;
+ map->map_state = info->failed_disks ? IMSM_T_STATE_DEGRADED : IMSM_T_STATE_NORMAL;
map->ddf = 1;
if (info->level == 1 && info->raid_disks > 2) {
{
struct intel_super *super = st->sb;
struct imsm_super *mpb = super->anchor;
- struct dl *dl;
+ struct imsm_disk *_disk;
struct imsm_dev *dev;
struct imsm_map *map;
+ struct dl *dl, *df;
int slot;
dev = get_imsm_dev(super, super->current_vol);
set_imsm_ord_tbl_ent(map, dk->raid_disk, dl->index);
dl->disk.status = CONFIGURED_DISK;
+ /* update size of 'missing' disks to be at least as large as the
+ * largest acitve member (we only have dummy missing disks when
+ * creating the first volume)
+ */
+ if (super->current_vol == 0) {
+ for (df = super->missing; df; df = df->next) {
+ if (dl->disk.total_blocks > df->disk.total_blocks)
+ df->disk.total_blocks = dl->disk.total_blocks;
+ _disk = __get_imsm_disk(mpb, df->index);
+ *_disk = df->disk;
+ }
+ }
+
+ /* refresh unset/failed slots to point to valid 'missing' entries */
+ for (df = super->missing; df; df = df->next)
+ for (slot = 0; slot < mpb->num_disks; slot++) {
+ __u32 ord = get_imsm_ord_tbl_ent(dev, slot, -1);
+
+ if ((ord & IMSM_ORD_REBUILD) == 0)
+ continue;
+ set_imsm_ord_tbl_ent(map, slot, df->index | IMSM_ORD_REBUILD);
+ dprintf("set slot:%d to missing disk:%d\n", slot, df->index);
+ break;
+ }
+
/* if we are creating the first raid device update the family number */
if (super->current_vol == 0) {
__u32 sum;
struct imsm_dev *_dev = __get_imsm_dev(mpb, 0);
- struct imsm_disk *_disk = __get_imsm_disk(mpb, dl->index);
+ _disk = __get_imsm_disk(mpb, dl->index);
if (!_dev || !_disk) {
fprintf(stderr, Name ": BUG mpb setup error\n");
return 1;
return 0;
}
+/* mark_spare()
+ * Function marks disk as spare and restores disk serial
+ * in case it was previously marked as failed by takeover operation
+ * reruns:
+ * -1 : critical error
+ * 0 : disk is marked as spare but serial is not set
+ * 1 : success
+ */
+int mark_spare(struct dl *disk)
+{
+ __u8 serial[MAX_RAID_SERIAL_LEN];
+ int ret_val = -1;
+
+ if (!disk)
+ return ret_val;
+
+ ret_val = 0;
+ if (!imsm_read_serial(disk->fd, NULL, serial)) {
+ /* Restore disk serial number, because takeover marks disk
+ * as failed and adds to serial ':0' before it becomes
+ * a spare disk.
+ */
+ serialcpy(disk->serial, serial);
+ serialcpy(disk->disk.serial, serial);
+ ret_val = 1;
+ }
+ disk->disk.status = SPARE_DISK;
+ disk->index = -1;
+
+ return ret_val;
+}
static int add_to_super_imsm(struct supertype *st, mdu_disk_info_t *dk,
int fd, char *devname)
memset(dd, 0, sizeof(*dd));
dd->major = major(stb.st_rdev);
dd->minor = minor(stb.st_rdev);
- dd->index = -1;
dd->devname = devname ? strdup(devname) : NULL;
dd->fd = fd;
dd->e = NULL;
size /= 512;
serialcpy(dd->disk.serial, dd->serial);
dd->disk.total_blocks = __cpu_to_le32(size);
- dd->disk.status = SPARE_DISK;
+ mark_spare(dd);
if (sysfs_disk_to_scsi_id(fd, &id) == 0)
dd->disk.scsi_id = __cpu_to_le32(id);
else
memset(dd, 0, sizeof(*dd));
dd->major = dk->major;
dd->minor = dk->minor;
- dd->index = -1;
dd->fd = -1;
- dd->disk.status = SPARE_DISK;
+ mark_spare(dd);
dd->action = DISK_REMOVE;
dd->next = super->disk_mgmt_list;
if (!super)
return 0;
+ if (mpb->num_raid_devs > 0 && mpb->num_disks != raiddisks) {
+ fprintf(stderr, Name ": the option-rom requires all "
+ "member disks to be a member of all volumes.\n");
+ return 0;
+ }
+
if (!validate_geometry_imsm_orom(super, level, layout, raiddisks, chunk, verbose)) {
fprintf(stderr, Name ": RAID gemetry validation failed. "
"Cannot proceed with the action(s).\n");
return validate_geometry_imsm_volume(st, level, layout,
raiddisks, chunk,
size, dev,
- freesize, verbose);
+ freesize, 1)
+ ? 1 : -1;
}
}
struct dl *d;
for (d = super->disks; d; d = d->next)
- if (d->index > -2) {
- d->index = -1;
- d->disk.status = SPARE_DISK;
- }
+ if (d->index > -2)
+ mark_spare(d);
}
super->updates_pending++;
super->updates_pending++;
a->last_checkpoint = 0;
}
- } else if (!is_resyncing(dev) && !failed) {
+ } else if ((!is_resyncing(dev) && !failed) &&
+ (imsm_reshape_blocks_arrays_changes(super) == 0)) {
/* mark the start of the init process if nothing is failed */
dprintf("imsm: mark resync start\n");
if (map->map_state == IMSM_T_STATE_UNINITIALIZED)
dprintf("imsm: activate spare: inst=%d failed=%d (%d) level=%d\n",
inst, failed, a->info.array.raid_disks, a->info.array.level);
- if (dev->vol.migr_state &&
- dev->vol.migr_type == MIGR_GEN_MIGR)
- /* No repair during migration */
- return NULL;
+ if (imsm_reshape_blocks_arrays_changes(super))
+ return NULL;
if (a->info.array.level == 4)
/* No repair for takeovered array
if (du->index > idx)
du->index--;
/* mark as spare disk */
- dm->disk.status = SPARE_DISK;
- dm->index = -1;
+ mark_spare(dm);
}
}
/* update map */