unsigned long long start, size;
};
+/* definitions of reshape process types */
+enum imsm_reshape_type {
+ CH_TAKEOVER,
+ CH_CHUNK_MIGR,
+ CH_LEVEL_MIGRATION
+};
+
/* definition of messages passed to imsm_process_update */
enum imsm_update_type {
update_activate_spare,
struct intel_super *super;
int rv;
-#ifndef MDASSEMBLE
- if (load_super_imsm_all(st, fd, &st->sb, devname) == 0)
- return 0;
-#endif
-
if (test_partition(fd))
/* IMSM not allowed on partitions */
return 1;
if (st->update_tail) {
/* queue the recently created array / added disk
* as a metadata update */
- struct dl *d;
int rv;
/* determine if we are creating a volume or adding a disk */
} else
rv = create_array(st, current_vol);
- for (d = super->disks; d ; d = d->next) {
- close(d->fd);
- d->fd = -1;
- }
-
return rv;
} else {
struct dl *d;
for (i = 0; i < mpb->num_raid_devs; i++) {
struct imsm_dev *dev;
struct imsm_map *map;
+ struct imsm_map *map2;
struct mdinfo *this;
int slot;
char *ep;
dev = get_imsm_dev(super, i);
map = get_imsm_map(dev, 0);
+ map2 = get_imsm_map(dev, 1);
/* do not publish arrays that are in the middle of an
* unsupported migration
info_d->disk.minor = d->minor;
info_d->disk.raid_disk = slot;
info_d->recovery_start = recovery_start;
-
+ if (map2) {
+ if (slot < map2->num_members)
+ info_d->disk.state = (1 << MD_DISK_ACTIVE);
+ else
+ this->array.spare_disks++;
+ } else {
+ if (slot < map->num_members)
+ info_d->disk.state = (1 << MD_DISK_ACTIVE);
+ else
+ this->array.spare_disks++;
+ }
if (info_d->recovery_start == MaxSector)
this->array.working_disks++;
static void imsm_set_disk(struct active_array *a, int n, int state);
+static void imsm_progress_container_reshape(struct intel_super *super)
+{
+ /* if no device has a migr_state, but some device has a
+ * different number of members than the previous device, start
+ * changing the number of devices in this device to match
+ * previous.
+ */
+ struct imsm_super *mpb = super->anchor;
+ int prev_disks = -1;
+ int i;
+
+ for (i = 0; i < mpb->num_raid_devs; i++) {
+ struct imsm_dev *dev = get_imsm_dev(super, i);
+ struct imsm_map *map = get_imsm_map(dev, 0);
+ struct imsm_map *map2;
+ int prev_num_members;
+ int used_disks;
+
+ if (dev->vol.migr_state)
+ return;
+
+ if (prev_disks == -1)
+ prev_disks = map->num_members;
+ if (prev_disks == map->num_members)
+ continue;
+
+ /* OK, this array needs to enter reshape mode.
+ * i.e it needs a migr_state
+ */
+
+ prev_num_members = map->num_members;
+ map->num_members = prev_disks;
+ dev->vol.migr_state = 1;
+ dev->vol.curr_migr_unit = 0;
+ dev->vol.migr_type = MIGR_GEN_MIGR;
+ for (i = prev_num_members;
+ i < map->num_members; i++)
+ set_imsm_ord_tbl_ent(map, i, i);
+ map2 = get_imsm_map(dev, 1);
+ /* Copy the current map */
+ memcpy(map2, map, sizeof_imsm_map(map));
+ map2->num_members = prev_num_members;
+
+ /* calculate new size
+ */
+ used_disks = imsm_num_data_members(dev, 0);
+ if (used_disks) {
+ unsigned long long array_blocks;
+
+ array_blocks =
+ map->blocks_per_member
+ * used_disks;
+ /* round array size down to closest MB
+ */
+ array_blocks = (array_blocks
+ >> SECT_PER_MB_SHIFT)
+ << SECT_PER_MB_SHIFT;
+ dev->size_low =
+ __cpu_to_le32((__u32)array_blocks);
+ dev->size_high =
+ __cpu_to_le32(
+ (__u32)(array_blocks >> 32));
+ }
+ super->updates_pending++;
+ }
+}
+
/* Handle dirty -> clean transititions, resync and reshape. Degraded and rebuild
* states are handled in imsm_set_disk() with one exception, when a
* resync is stopped due to a new failure this routine will set the
* array size
*/
super->updates_pending++;
+ imsm_progress_container_reshape(super);
}
}
}
/* No repair during migration */
return NULL;
+ if (a->info.array.level == 4)
+ /* No repair for takeovered array
+ * imsm doesn't support raid4
+ */
+ return NULL;
+
if (imsm_check_degraded(super, dev, failed) != IMSM_T_STATE_DEGRADED)
return NULL;
return check_degraded;
}
+static int apply_reshape_container_disks_update(struct imsm_update_reshape *u,
+ struct intel_super *super,
+ void ***space_list)
+{
+ struct dl *new_disk;
+ struct intel_dev *id;
+ int i;
+ int delta_disks = u->new_raid_disks - u->old_raid_disks;
+ int disk_count = u->old_raid_disks;
+ void **tofree = NULL;
+ int devices_to_reshape = 1;
+ struct imsm_super *mpb = super->anchor;
+ int ret_val = 0;
+
+ dprintf("imsm: imsm_process_update() for update_reshape\n");
+
+ /* enable spares to use in array */
+ for (i = 0; i < delta_disks; i++) {
+ new_disk = get_disk_super(super,
+ major(u->new_disks[i]),
+ minor(u->new_disks[i]));
+ dprintf("imsm: imsm_process_update(): new disk "
+ "for reshape is: %i:%i (%p, index = %i)\n",
+ major(u->new_disks[i]), minor(u->new_disks[i]),
+ new_disk, new_disk->index);
+ if ((new_disk == NULL) ||
+ ((new_disk->index >= 0) &&
+ (new_disk->index < u->old_raid_disks)))
+ goto update_reshape_exit;
+ new_disk->index = disk_count++;
+ /* slot to fill in autolayout
+ */
+ new_disk->raiddisk = new_disk->index;
+ new_disk->disk.status |=
+ CONFIGURED_DISK;
+ new_disk->disk.status &= ~SPARE_DISK;
+ }
+
+ dprintf("imsm: process_update(): update_reshape: volume set"
+ " mpb->num_raid_devs = %i\n", mpb->num_raid_devs);
+ /* manage changes in volume
+ */
+ for (id = super->devlist ; id; id = id->next) {
+ void **sp = *space_list;
+ struct imsm_dev *newdev;
+ struct imsm_map *newmap, *oldmap;
+
+ if (!sp)
+ continue;
+ *space_list = *sp;
+ newdev = (void*)sp;
+ /* Copy the dev, but not (all of) the map */
+ memcpy(newdev, id->dev, sizeof(*newdev));
+ oldmap = get_imsm_map(id->dev, 0);
+ newmap = get_imsm_map(newdev, 0);
+ /* Copy the current map */
+ memcpy(newmap, oldmap, sizeof_imsm_map(oldmap));
+ /* update one device only
+ */
+ if (devices_to_reshape) {
+ int used_disks;
+
+ dprintf("process_update(): modifying "
+ "subdev: %i\n", id->index);
+ devices_to_reshape--;
+ newdev->vol.migr_state = 1;
+ newdev->vol.curr_migr_unit = 0;
+ newdev->vol.migr_type = MIGR_GEN_MIGR;
+ newmap->num_members = u->new_raid_disks;
+ for (i = 0; i < delta_disks; i++) {
+ set_imsm_ord_tbl_ent(newmap,
+ u->old_raid_disks + i,
+ u->old_raid_disks + i);
+ }
+ /* New map is correct, now need to save old map
+ */
+ newmap = get_imsm_map(newdev, 1);
+ memcpy(newmap, oldmap, sizeof_imsm_map(oldmap));
+
+ /* calculate new size
+ */
+ used_disks = imsm_num_data_members(newdev, 0);
+ if (used_disks) {
+ unsigned long long array_blocks;
+
+ array_blocks =
+ newmap->blocks_per_member * used_disks;
+ /* round array size down to closest MB
+ */
+ array_blocks = (array_blocks
+ >> SECT_PER_MB_SHIFT)
+ << SECT_PER_MB_SHIFT;
+ newdev->size_low =
+ __cpu_to_le32((__u32)array_blocks);
+ newdev->size_high =
+ __cpu_to_le32((__u32)(array_blocks >> 32));
+ }
+ }
+
+ sp = (void **)id->dev;
+ id->dev = newdev;
+ *sp = tofree;
+ tofree = sp;
+ }
+ if (tofree)
+ *space_list = tofree;
+ ret_val = 1;
+
+update_reshape_exit:
+
+ return ret_val;
+}
+
static void imsm_process_update(struct supertype *st,
struct metadata_update *update)
{
switch (type) {
case update_reshape_container_disks: {
struct imsm_update_reshape *u = (void *)update->buf;
- struct dl *new_disk;
- struct intel_dev *id;
- int i;
- int delta_disks = u->new_raid_disks - u->old_raid_disks;
- void **tofree = NULL;
- int devices_to_reshape = 1;
-
- dprintf("imsm: imsm_process_update() for update_reshape\n");
-
- /* enable spares to use in array */
- for (i = 0; i < delta_disks; i++) {
- new_disk = get_disk_super(super,
- major(u->new_disks[i]),
- minor(u->new_disks[i]));
- dprintf("imsm: imsm_process_update(): new disk "\
- "for reshape is: %i:%i (%p, index = %i)\n",
- major(u->new_disks[i]), minor(u->new_disks[i]),
- new_disk, new_disk->index);
- if ((new_disk == NULL) ||
- ((new_disk->index >= 0) &&
- (new_disk->index < u->old_raid_disks)))
- goto update_reshape_exit;
-
- new_disk->index = mpb->num_disks++;
- /* slot to fill in autolayout */
- new_disk->raiddisk = new_disk->index;
- new_disk->disk.status |=
- CONFIGURED_DISK;
- new_disk->disk.status &= ~SPARE_DISK;
- }
-
- dprintf("imsm: process_update(): update_reshape: volume set"
- " mpb->num_raid_devs = %i\n", mpb->num_raid_devs);
- /* manage changes in volume
- */
- for (id = super->devlist ; id; id = id->next) {
- void **sp = update->space_list;
- struct imsm_dev *newdev;
- struct imsm_map *newmap, *oldmap;
-
- if (!sp)
- continue;
- update->space_list = *sp;
- newdev = (void*)sp;
- /* Copy the dev, but not (all of) the map */
- memcpy(newdev, id->dev, sizeof(*newdev));
- oldmap = get_imsm_map(id->dev, 0);
- newmap = get_imsm_map(newdev, 0);
- /* Copy the current map */
- memcpy(newmap, oldmap, sizeof_imsm_map(oldmap));
- /* update one device only
- */
- if (devices_to_reshape) {
- dprintf("process_update(): modifying "\
- "subdev: %i\n", id->index);
- devices_to_reshape--;
- newdev->vol.migr_state = 1;
- newdev->vol.curr_migr_unit = 0;
- newdev->vol.migr_type = MIGR_GEN_MIGR;
- newmap->num_members = u->new_raid_disks;
- for (i = 0; i < delta_disks; i++) {
- set_imsm_ord_tbl_ent(newmap,
- u->old_raid_disks + i,
- u->old_raid_disks + i);
- }
- /* New map is correct, now need to save old map
- */
- newmap = get_imsm_map(newdev, 1);
- memcpy(newmap, oldmap, sizeof_imsm_map(oldmap));
- }
-
- sp = (void **)id->dev;
- id->dev = newdev;
- *sp = tofree;
- tofree = sp;
- }
-
- update->space_list = tofree;
- super->updates_pending++;
-update_reshape_exit:
+ if (apply_reshape_container_disks_update(
+ u, super, &update->space_list))
+ super->updates_pending++;
break;
}
case update_activate_spare: {
struct geo_params *geo,
int *old_raid_disks)
{
+ /* currently we only support increasing the number of devices
+ * for a container. This increases the number of device for each
+ * member array. They must all be RAID0 or RAID5.
+ */
int ret_val = 0;
struct mdinfo *info, *member;
int devices_that_can_grow = 0;
struct mdinfo *spares = NULL;
int i;
int delta_disks = 0;
+ struct mdinfo *dev;
dprintf("imsm_update_metadata_for_reshape(enter) raid_disks = %i\n",
geo->raid_disks);
dprintf("imsm: %i spares are available.\n\n",
spares->array.spare_disks);
+ dev = spares->devs;
for (i = 0; i < delta_disks; i++) {
- struct mdinfo *dev = spares->devs;
struct dl *dl;
+ if (dev == NULL)
+ break;
u->new_disks[i] = makedev(dev->disk.major,
dev->disk.minor);
dl = get_disk_super(super, dev->disk.major, dev->disk.minor);
- dl->index = mpb->num_disks++;
- }
- /* Now update the metadata so that container_content will find
- * the new devices
- */
- for (i = 0; i < mpb->num_raid_devs; i++) {
- int d;
- struct imsm_dev *dev = get_imsm_dev(super, i);
- struct imsm_map *map = get_imsm_map(dev, 0);
- map->num_members = geo->raid_disks;
- for (d = 0; d < delta_disks; d++) {
- set_imsm_ord_tbl_ent(map, old_raid_disks + d,
- mpb->num_disks - delta_disks + d);
- }
+ dl->index = mpb->num_disks;
+ mpb->num_disks++;
+ dev = dev->next;
}
abort:
return 0;
}
+static void imsm_update_metadata_locally(struct supertype *st,
+ void *buf, int len)
+{
+ struct metadata_update mu;
+
+ mu.buf = buf;
+ mu.len = len;
+ mu.space = NULL;
+ mu.space_list = NULL;
+ mu.next = NULL;
+ imsm_prepare_update(st, &mu);
+ imsm_process_update(st, &mu);
+
+ while (mu.space_list) {
+ void **space = mu.space_list;
+ mu.space_list = *space;
+ free(space);
+ }
+}
+
+/***************************************************************************
+* Function: imsm_analyze_change
+* Description: Function analyze change for single volume
+* and validate if transition is supported
+* Parameters: Geometry parameters, supertype structure
+* Returns: Operation type code on success, -1 if fail
+****************************************************************************/
+enum imsm_reshape_type imsm_analyze_change(struct supertype *st,
+ struct geo_params *geo)
+{
+ struct mdinfo info;
+ int change = -1;
+ int check_devs = 0;
+
+ getinfo_super_imsm_volume(st, &info, NULL);
+
+ if ((geo->level != info.array.level) &&
+ (geo->level >= 0) &&
+ (geo->level != UnSet)) {
+ switch (info.array.level) {
+ case 0:
+ if (geo->level == 5) {
+ change = CH_LEVEL_MIGRATION;
+ check_devs = 1;
+ }
+ if (geo->level == 10) {
+ change = CH_TAKEOVER;
+ check_devs = 1;
+ }
+ break;
+ case 5:
+ if (geo->level != 0)
+ change = CH_LEVEL_MIGRATION;
+ break;
+ case 10:
+ if (geo->level == 0) {
+ change = CH_TAKEOVER;
+ check_devs = 1;
+ }
+ break;
+ }
+ if (change == -1) {
+ fprintf(stderr,
+ Name " Error. Level Migration from %d to %d "
+ "not supported!\n",
+ info.array.level, geo->level);
+ goto analyse_change_exit;
+ }
+ } else
+ geo->level = info.array.level;
+
+ if ((geo->layout != info.array.layout)
+ && ((geo->layout != UnSet) && (geo->layout != -1))) {
+ change = CH_LEVEL_MIGRATION;
+ if ((info.array.layout == 0)
+ && (info.array.level == 5)
+ && (geo->layout == 5)) {
+ /* reshape 5 -> 4 */
+ } else if ((info.array.layout == 5)
+ && (info.array.level == 5)
+ && (geo->layout == 0)) {
+ /* reshape 4 -> 5 */
+ geo->layout = 0;
+ geo->level = 5;
+ } else {
+ fprintf(stderr,
+ Name " Error. Layout Migration from %d to %d "
+ "not supported!\n",
+ info.array.layout, geo->layout);
+ change = -1;
+ goto analyse_change_exit;
+ }
+ } else
+ geo->layout = info.array.layout;
+
+ if ((geo->chunksize > 0) && (geo->chunksize != UnSet)
+ && (geo->chunksize != info.array.chunk_size))
+ change = CH_CHUNK_MIGR;
+ else
+ geo->chunksize = info.array.chunk_size;
+
+ if (!validate_geometry_imsm(st,
+ geo->level,
+ geo->layout,
+ geo->raid_disks,
+ (geo->chunksize / 1024),
+ geo->size,
+ 0, 0, 1))
+ change = -1;
+
+ if (check_devs) {
+ struct intel_super *super = st->sb;
+ struct imsm_super *mpb = super->anchor;
+
+ if (mpb->num_raid_devs > 1) {
+ fprintf(stderr,
+ Name " Error. Cannot perform operation on %s"
+ "- for this operation it MUST be single "
+ "array in container\n",
+ geo->dev_name);
+ change = -1;
+ }
+ }
+
+analyse_change_exit:
+
+ return change;
+}
static int imsm_reshape_super(struct supertype *st, long long size, int level,
int layout, int chunksize, int raid_disks,
char *backup, char *dev, int verbose)
{
- /* currently we only support increasing the number of devices
- * for a container. This increases the number of device for each
- * member array. They must all be RAID0 or RAID5.
- */
-
int ret_val = 1;
struct geo_params geo;
memset(&geo, sizeof(struct geo_params), 0);
geo.dev_name = dev;
+ geo.dev_id = st->devnum;
geo.size = size;
geo.level = level;
geo.layout = layout;
if (experimental() == 0)
return ret_val;
- /* verify reshape conditions
- * on container level we can only increase number of devices. */
if (st->container_dev == st->devnum) {
- /* check for delta_disks > 0
- *and supported raid levels 0 and 5 only in container */
+ /* On container level we can only increase number of devices. */
+ dprintf("imsm: info: Container operation\n");
int old_raid_disks = 0;
if (imsm_reshape_is_allowed_on_container(
st, &geo, &old_raid_disks)) {
len = imsm_create_metadata_update_for_reshape(
st, &geo, old_raid_disks, &u);
- if (len) {
- ret_val = 0;
+ if (len <= 0) {
+ dprintf("imsm: Cannot prepare update\n");
+ goto exit_imsm_reshape_super;
+ }
+
+ ret_val = 0;
+ /* update metadata locally */
+ imsm_update_metadata_locally(st, u, len);
+ /* and possibly remotely */
+ if (st->update_tail)
append_metadata_update(st, u, len);
- } else
- fprintf(stderr, Name "imsm: Cannot prepare "
- "update\n");
- } else
+ else
+ free(u);
+
+ } else {
fprintf(stderr, Name "imsm: Operation is not allowed "
"on this container\n");
- } else
- fprintf(stderr, Name "imsm: not a container operation\n");
+ }
+ } else {
+ /* On volume level we support following operations
+ * - takeover: raid10 -> raid0; raid0 -> raid10
+ * - chunk size migration
+ * - migration: raid5 -> raid0; raid0 -> raid5
+ */
+ struct intel_super *super = st->sb;
+ struct intel_dev *dev = super->devlist;
+ int change, devnum;
+ dprintf("imsm: info: Volume operation\n");
+ /* find requested device */
+ while (dev) {
+ imsm_find_array_minor_by_subdev(dev->index, st->container_dev, &devnum);
+ if (devnum == geo.dev_id)
+ break;
+ dev = dev->next;
+ }
+ if (dev == NULL) {
+ fprintf(stderr, Name " Cannot find %s (%i) subarray\n",
+ geo.dev_name, geo.dev_id);
+ goto exit_imsm_reshape_super;
+ }
+ super->current_vol = dev->index;
+ change = imsm_analyze_change(st, &geo);
+ switch (change) {
+ case CH_TAKEOVER:
+ ret_val = 0;
+ break;
+ case CH_CHUNK_MIGR:
+ ret_val = 0;
+ break;
+ case CH_LEVEL_MIGRATION:
+ ret_val = 0;
+ break;
+ default:
+ ret_val = 1;
+ }
+ }
+exit_imsm_reshape_super:
dprintf("imsm: reshape_super Exit code = %i\n", ret_val);
return ret_val;
}
+static int imsm_manage_reshape(
+ int afd, struct mdinfo *sra, struct reshape *reshape,
+ struct supertype *st, unsigned long stripes,
+ int *fds, unsigned long long *offsets,
+ int dests, int *destfd, unsigned long long *destoffsets)
+{
+ /* Just use child_monitor for now */
+ return child_monitor(
+ afd, sra, reshape, st, stripes,
+ fds, offsets, dests, destfd, destoffsets);
+}
+
struct superswitch super_imsm = {
#ifndef MDASSEMBLE
.examine_super = examine_super_imsm,
.default_geometry = default_geometry_imsm,
.get_disk_controller_domain = imsm_get_disk_controller_domain,
.reshape_super = imsm_reshape_super,
+ .manage_reshape = imsm_manage_reshape,
.external = 1,
.name = "imsm",