#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)
{
if (i > 0)
rv = e[i-1].start + e[i-1].size;
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 (is_gen_migration(dev)) {
+ /* 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->custom_array_size = __le32_to_cpu(dev->size_high);
info->custom_array_size <<= 32;
info->custom_array_size |= __le32_to_cpu(dev->size_low);
- if (prev_map && map->map_state == prev_map->map_state) {
+ info->recovery_blocked = imsm_reshape_blocks_arrays_changes(st->sb);
+
+ if (prev_map && map->map_state == prev_map->map_state &&
+ (migr_type(dev) == MIGR_GEN_MIGR)) {
info->reshape_active = 1;
info->new_level = get_imsm_raid_level(map);
info->new_layout = imsm_level_to_layout(info->new_level);
/* this needs to be applied to every array
* in the container.
*/
- info->reshape_active = 2;
+ info->reshape_active = CONTAINER_RESHAPE;
}
/* We shape information that we give to md might have to be
* modify to cope with md's requirement for reshaping arrays.
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) {
info->disk.state = 0;
info->name[0] = 0;
info->recovery_start = MaxSector;
+ info->recovery_blocked = imsm_reshape_blocks_arrays_changes(st->sb);
/* do we have the all the insync disks that we expect? */
mpb = super->anchor;
sprintf(path, "/sys/dev/block/%d:%d",
major(st.st_rdev), minor(st.st_rdev));
- rv = readlink(path, dname, sizeof(dname));
+ rv = readlink(path, dname, sizeof(dname)-1);
if (rv <= 0)
return;
}
dev->vol.migr_state = 0;
- dev->vol.migr_type = 0;
+ set_migr_type(dev, 0);
dev->vol.curr_migr_unit = 0;
map->map_state = map_state;
}
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 imsm_super *mpb = super->anchor;
struct mdinfo *rest = NULL;
unsigned int i;
- int bbm_errors = 0;
+ int sb_errors = 0;
struct dl *d;
int spare_disks = 0;
/* do not assemble arrays when not all attributes are supported */
if (imsm_check_attributes(mpb->attributes) == 0) {
- fprintf(stderr, Name ": IMSM metadata loading not allowed "
- "due to attributes incompatibility.\n");
- return NULL;
+ sb_errors = 1;
+ fprintf(stderr, Name ": Unsupported attributes in IMSM metadata."
+ "Arrays activation is blocked.\n");
}
/* check for bad blocks */
- if (imsm_bbm_log_size(super->anchor))
- bbm_errors = 1;
+ if (imsm_bbm_log_size(super->anchor)) {
+ fprintf(stderr, Name ": BBM log found in IMSM metadata."
+ "Arrays activation is blocked.\n");
+ sb_errors = 1;
+ }
+
/* count spare devices, not used in maps
*/
*/
chunk = __le16_to_cpu(map->blocks_per_strip) >> 1;
-#ifndef MDASSEMBLE
- if (!validate_geometry_imsm_orom(super,
- get_imsm_raid_level(map), /* RAID level */
- imsm_level_to_layout(get_imsm_raid_level(map)),
- map->num_members, /* raid disks */
- &chunk,
- 1 /* verbose */)) {
- fprintf(stderr, Name ": RAID gemetry validation failed. "
- "Cannot proceed with the action(s).\n");
- continue;
- }
-#endif /* MDASSEMBLE */
this = malloc(sizeof(*this));
if (!this) {
fprintf(stderr, Name ": failed to allocate %zu bytes\n",
super->current_vol = i;
getinfo_super_imsm_volume(st, this, NULL);
this->next = rest;
+#ifndef MDASSEMBLE
+ /* mdadm does not support all metadata features- set the bit in all arrays state */
+ if (!validate_geometry_imsm_orom(super,
+ get_imsm_raid_level(map), /* RAID level */
+ imsm_level_to_layout(get_imsm_raid_level(map)),
+ map->num_members, /* raid disks */
+ &chunk,
+ 1 /* verbose */)) {
+ fprintf(stderr, Name ": IMSM RAID gemetry validation failed. "
+ "Array %s activation is blocked.\n",
+ dev->volume);
+ this->array.state |=
+ (1<<MD_SB_BLOCK_CONTAINER_RESHAPE) |
+ (1<<MD_SB_BLOCK_VOLUME);
+ }
+#endif
+
+ /* if array has bad blocks, set suitable bit in all arrays state */
+ if (sb_errors)
+ this->array.state |=
+ (1<<MD_SB_BLOCK_CONTAINER_RESHAPE) |
+ (1<<MD_SB_BLOCK_VOLUME);
+
for (slot = 0 ; slot < map->num_members; slot++) {
unsigned long long recovery_start;
struct mdinfo *info_d;
rest = this;
}
- /* if array has bad blocks, set suitable bit in array status */
- if (bbm_errors)
- rest->array.state |= (1<<MD_SB_BBM_ERRORS);
-
return rest;
}
if (is_failed(disk) && (ord & IMSM_ORD_REBUILD))
return 0;
- sprintf(buf, "%s:0", disk->serial);
+ memcpy(buf, disk->serial, MAX_RAID_SERIAL_LEN);
+ buf[MAX_RAID_SERIAL_LEN] = '\000';
+ 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);
map->num_members = prev_disks;
dev->vol.migr_state = 1;
dev->vol.curr_migr_unit = 0;
- dev->vol.migr_type = MIGR_GEN_MIGR;
+ set_migr_type(dev, MIGR_GEN_MIGR);
for (i = prev_num_members;
i < map->num_members; i++)
set_imsm_ord_tbl_ent(map, i, i);
if (0) {
struct imsm_map *map2 = get_imsm_map(dev, 1);
dev->vol.migr_state = 0;
- dev->vol.migr_type = 0;
+ set_migr_type(dev, 0);
dev->vol.curr_migr_unit = 0;
memcpy(map, map2, sizeof_imsm_map(map2));
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
* are removed from container.
*/
if (failed) {
- dprintf("found failed disks in %s, check if there another"
+ dprintf("found failed disks in %.*s, check if there another"
"failed sub-array.\n",
- dev->volume);
+ MAX_RAID_SERIAL_LEN, dev->volume);
/* check if states of the other volumes allow for rebuild */
for (i = 0; i < super->anchor->num_raid_devs; i++) {
if (i != inst) {
*/
dl = imsm_readd(super, i, a);
if (!dl)
- dl = imsm_add_spare(super, i, a, 0, NULL);
+ dl = imsm_add_spare(super, i, a, 0, rv);
if (!dl)
- dl = imsm_add_spare(super, i, a, 1, NULL);
+ dl = imsm_add_spare(super, i, a, 1, rv);
if (!dl)
continue;
num_spares++;
dprintf("%x:%x to be %d at %llu\n", dl->major, dl->minor,
i, di->data_offset);
-
- break;
}
if (!rv)
return ret_val;
}
+static int apply_update_activate_spare(struct imsm_update_activate_spare *u,
+ struct intel_super *super,
+ struct active_array *active_array)
+{
+ struct imsm_super *mpb = super->anchor;
+ struct imsm_dev *dev = get_imsm_dev(super, u->array);
+ struct imsm_map *map = get_imsm_map(dev, 0);
+ struct imsm_map *migr_map;
+ struct active_array *a;
+ struct imsm_disk *disk;
+ __u8 to_state;
+ struct dl *dl;
+ unsigned int found;
+ int failed;
+ int victim;
+ int i;
+ int second_map_created = 0;
+
+ for (; u; u = u->next) {
+ victim = get_imsm_disk_idx(dev, u->slot, -1);
+
+ if (victim < 0)
+ return 0;
+
+ for (dl = super->disks; dl; dl = dl->next)
+ if (dl == u->dl)
+ break;
+
+ if (!dl) {
+ fprintf(stderr, "error: imsm_activate_spare passed "
+ "an unknown disk (index: %d)\n",
+ u->dl->index);
+ return 0;
+ }
+
+ /* count failures (excluding rebuilds and the victim)
+ * to determine map[0] state
+ */
+ failed = 0;
+ for (i = 0; i < map->num_members; i++) {
+ if (i == u->slot)
+ continue;
+ disk = get_imsm_disk(super,
+ get_imsm_disk_idx(dev, i, -1));
+ if (!disk || is_failed(disk))
+ failed++;
+ }
+
+ /* adding a pristine spare, assign a new index */
+ if (dl->index < 0) {
+ dl->index = super->anchor->num_disks;
+ super->anchor->num_disks++;
+ }
+ disk = &dl->disk;
+ disk->status |= CONFIGURED_DISK;
+ disk->status &= ~SPARE_DISK;
+
+ /* mark rebuild */
+ to_state = imsm_check_degraded(super, dev, failed);
+ if (!second_map_created) {
+ second_map_created = 1;
+ map->map_state = IMSM_T_STATE_DEGRADED;
+ migrate(dev, super, to_state, MIGR_REBUILD);
+ } else
+ map->map_state = to_state;
+ migr_map = get_imsm_map(dev, 1);
+ set_imsm_ord_tbl_ent(map, u->slot, dl->index);
+ set_imsm_ord_tbl_ent(migr_map, u->slot,
+ dl->index | IMSM_ORD_REBUILD);
+
+ /* update the family_num to mark a new container
+ * generation, being careful to record the existing
+ * family_num in orig_family_num to clean up after
+ * earlier mdadm versions that neglected to set it.
+ */
+ if (mpb->orig_family_num == 0)
+ mpb->orig_family_num = mpb->family_num;
+ mpb->family_num += super->random;
+
+ /* 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, 0);
+
+ if (get_imsm_disk_slot(map, victim) >= 0)
+ found++;
+ }
+
+ /* delete the victim if it is no longer being
+ * utilized anywhere
+ */
+ if (!found) {
+ struct dl **dlp;
+
+ /* We know that 'manager' isn't touching anything,
+ * so it is safe to delete
+ */
+ for (dlp = &super->disks; *dlp; dlp = &(*dlp)->next)
+ if ((*dlp)->index == victim)
+ break;
+
+ /* victim may be on the missing list */
+ if (!*dlp)
+ for (dlp = &super->missing; *dlp;
+ dlp = &(*dlp)->next)
+ if ((*dlp)->index == victim)
+ break;
+ imsm_delete(super, dlp, victim);
+ }
+ }
+
+ return 1;
+}
static int apply_reshape_container_disks_update(struct imsm_update_reshape *u,
struct intel_super *super,
devices_to_reshape--;
newdev->vol.migr_state = 1;
newdev->vol.curr_migr_unit = 0;
- newdev->vol.migr_type = MIGR_GEN_MIGR;
+ set_migr_type(newdev, MIGR_GEN_MIGR);
newmap->num_members = u->new_raid_disks;
for (i = 0; i < delta_disks; i++) {
set_imsm_ord_tbl_ent(newmap,
}
case update_activate_spare: {
struct imsm_update_activate_spare *u = (void *) update->buf;
- struct imsm_dev *dev = get_imsm_dev(super, u->array);
- struct imsm_map *map = get_imsm_map(dev, 0);
- struct imsm_map *migr_map;
- struct active_array *a;
- struct imsm_disk *disk;
- __u8 to_state;
- struct dl *dl;
- unsigned int found;
- int failed;
- int victim = get_imsm_disk_idx(dev, u->slot, -1);
- int i;
-
- for (dl = super->disks; dl; dl = dl->next)
- if (dl == u->dl)
- break;
-
- if (!dl) {
- fprintf(stderr, "error: imsm_activate_spare passed "
- "an unknown disk (index: %d)\n",
- u->dl->index);
- return;
- }
-
- super->updates_pending++;
- /* count failures (excluding rebuilds and the victim)
- * to determine map[0] state
- */
- failed = 0;
- for (i = 0; i < map->num_members; i++) {
- if (i == u->slot)
- continue;
- disk = get_imsm_disk(super,
- get_imsm_disk_idx(dev, i, -1));
- if (!disk || is_failed(disk))
- failed++;
- }
-
- /* adding a pristine spare, assign a new index */
- if (dl->index < 0) {
- dl->index = super->anchor->num_disks;
- super->anchor->num_disks++;
- }
- disk = &dl->disk;
- disk->status |= CONFIGURED_DISK;
- disk->status &= ~SPARE_DISK;
-
- /* mark rebuild */
- to_state = imsm_check_degraded(super, dev, failed);
- map->map_state = IMSM_T_STATE_DEGRADED;
- migrate(dev, super, to_state, MIGR_REBUILD);
- migr_map = get_imsm_map(dev, 1);
- set_imsm_ord_tbl_ent(map, u->slot, dl->index);
- set_imsm_ord_tbl_ent(migr_map, u->slot, dl->index | IMSM_ORD_REBUILD);
-
- /* update the family_num to mark a new container
- * generation, being careful to record the existing
- * family_num in orig_family_num to clean up after
- * earlier mdadm versions that neglected to set it.
- */
- if (mpb->orig_family_num == 0)
- mpb->orig_family_num = mpb->family_num;
- mpb->family_num += super->random;
-
- /* count arrays using the victim in the metadata */
- found = 0;
- for (a = st->arrays; a ; a = a->next) {
- dev = get_imsm_dev(super, a->info.container_member);
- map = get_imsm_map(dev, 0);
-
- if (get_imsm_disk_slot(map, victim) >= 0)
- found++;
- }
-
- /* delete the victim if it is no longer being
- * utilized anywhere
- */
- if (!found) {
- struct dl **dlp;
-
- /* We know that 'manager' isn't touching anything,
- * so it is safe to delete
- */
- for (dlp = &super->disks; *dlp; dlp = &(*dlp)->next)
- if ((*dlp)->index == victim)
- break;
-
- /* victim may be on the missing list */
- if (!*dlp)
- for (dlp = &super->missing; *dlp; dlp = &(*dlp)->next)
- if ((*dlp)->index == victim)
- break;
- imsm_delete(super, dlp, victim);
- }
+ if (apply_update_activate_spare(u, super, st->arrays))
+ super->updates_pending++;
break;
}
case update_create_array: {