update_rename_array,
update_add_remove_disk,
update_reshape_container_disks,
+ update_reshape_migration,
update_takeover
};
enum imsm_update_type type;
int old_raid_disks;
int new_raid_disks;
+
+ int new_disks[1]; /* new_raid_disks - old_raid_disks makedev number */
+};
+
+struct imsm_update_reshape_migration {
+ enum imsm_update_type type;
+ int old_raid_disks;
+ int new_raid_disks;
+ /* fields for array migration changes
+ */
+ int subdev;
+ int new_level;
+ int new_layout;
+ int new_chunksize;
+
int new_disks[1]; /* new_raid_disks - old_raid_disks makedev number */
};
return check_degraded;
}
+
+static int apply_reshape_migration_update(struct imsm_update_reshape_migration *u,
+ struct intel_super *super,
+ void ***space_list)
+{
+ struct intel_dev *id;
+ void **tofree = NULL;
+ int ret_val = 0;
+
+ dprintf("apply_reshape_migration_update()\n");
+ if ((u->subdev < 0) ||
+ (u->subdev > 1)) {
+ dprintf("imsm: Error: Wrong subdev: %i\n", u->subdev);
+ return ret_val;
+ }
+ if ((space_list == NULL) || (*space_list == NULL)) {
+ dprintf("imsm: Error: Memory is not allocated\n");
+ return ret_val;
+ }
+
+ for (id = super->devlist ; id; id = id->next) {
+ if (id->index == (unsigned)u->subdev) {
+ struct imsm_dev *dev = get_imsm_dev(super, u->subdev);
+ struct imsm_map *map;
+ struct imsm_dev *new_dev =
+ (struct imsm_dev *)*space_list;
+ struct imsm_map *migr_map = get_imsm_map(dev, 1);
+ int to_state;
+ struct dl *new_disk;
+
+ if (new_dev == NULL)
+ return ret_val;
+ *space_list = **space_list;
+ memcpy(new_dev, dev, sizeof_imsm_dev(dev, 0));
+ map = get_imsm_map(new_dev, 0);
+ if (migr_map) {
+ dprintf("imsm: Error: migration in progress");
+ return ret_val;
+ }
+
+ to_state = map->map_state;
+ if ((u->new_level == 5) && (map->raid_level == 0)) {
+ map->num_members++;
+ /* this should not happen */
+ if (u->new_disks[0] < 0) {
+ map->failed_disk_num =
+ map->num_members - 1;
+ to_state = IMSM_T_STATE_DEGRADED;
+ } else
+ to_state = IMSM_T_STATE_NORMAL;
+ }
+ migrate(new_dev, to_state, MIGR_GEN_MIGR);
+ if (u->new_level > -1)
+ map->raid_level = u->new_level;
+ migr_map = get_imsm_map(new_dev, 1);
+ if ((u->new_level == 5) &&
+ (migr_map->raid_level == 0)) {
+ int ord = map->num_members - 1;
+ migr_map->num_members--;
+ if (u->new_disks[0] < 0)
+ ord |= IMSM_ORD_REBUILD;
+ set_imsm_ord_tbl_ent(map,
+ map->num_members - 1,
+ ord);
+ }
+ id->dev = new_dev;
+ tofree = (void **)dev;
+
+ /* update chunk size
+ */
+ if (u->new_chunksize > 0)
+ map->blocks_per_strip =
+ __cpu_to_le16(u->new_chunksize * 2);
+
+ /* add disk
+ */
+ if ((u->new_level != 5) ||
+ (migr_map->raid_level != 0) ||
+ (migr_map->raid_level == map->raid_level))
+ goto skip_disk_add;
+
+ if (u->new_disks[0] >= 0) {
+ /* use passes spare
+ */
+ new_disk = get_disk_super(super,
+ major(u->new_disks[0]),
+ minor(u->new_disks[0]));
+ dprintf("imsm: new disk for reshape is: %i:%i "
+ "(%p, index = %i)\n",
+ major(u->new_disks[0]),
+ minor(u->new_disks[0]),
+ new_disk, new_disk->index);
+ if (new_disk == NULL)
+ goto error_disk_add;
+
+ new_disk->index = map->num_members - 1;
+ /* slot to fill in autolayout
+ */
+ new_disk->raiddisk = new_disk->index;
+ new_disk->disk.status |= CONFIGURED_DISK;
+ new_disk->disk.status &= ~SPARE_DISK;
+ } else
+ goto error_disk_add;
+
+skip_disk_add:
+ *tofree = *space_list;
+ /* calculate new size
+ */
+ imsm_set_array_size(new_dev);
+
+ ret_val = 1;
+ }
+ }
+
+ if (tofree)
+ *space_list = tofree;
+ return ret_val;
+
+error_disk_add:
+ dprintf("Error: imsm: Cannot find disk.\n");
+ return ret_val;
+}
+
+
static int apply_reshape_container_disks_update(struct imsm_update_reshape *u,
struct intel_super *super,
void ***space_list)
super->updates_pending++;
break;
}
+ case update_reshape_migration: {
+ struct imsm_update_reshape_migration *u = (void *)update->buf;
+ if (apply_reshape_migration_update(
+ u, super, &update->space_list))
+ super->updates_pending++;
+ break;
+ }
case update_activate_spare: {
struct imsm_update_activate_spare *u = (void *) update->buf;
struct imsm_dev *dev = get_imsm_dev(super, u->array);
}
super->updates_pending++;
-
/* count failures (excluding rebuilds and the victim)
* to determine map[0] state
*/
}
}
+static struct mdinfo *get_spares_for_grow(struct supertype *st);
+
static void imsm_prepare_update(struct supertype *st,
struct metadata_update *update)
{
dprintf("New anchor length is %llu\n", (unsigned long long)len);
break;
}
+ case update_reshape_migration: {
+ /* for migration level 0->5 we need to add disks
+ * so the same as for container operation we will copy
+ * device to the bigger location.
+ * in memory prepared device and new disk area are prepared
+ * for usage in process update
+ */
+ struct imsm_update_reshape_migration *u = (void *)update->buf;
+ struct intel_dev *id;
+ void **space_tail = (void **)&update->space_list;
+ int size;
+ void *s;
+ int current_level = -1;
+
+ dprintf("imsm: imsm_prepare_update() for update_reshape\n");
+
+ /* add space for bigger array in update
+ */
+ for (id = super->devlist; id; id = id->next) {
+ if (id->index == (unsigned)u->subdev) {
+ size = sizeof_imsm_dev(id->dev, 1);
+ if (u->new_raid_disks > u->old_raid_disks)
+ size += sizeof(__u32)*2*
+ (u->new_raid_disks - u->old_raid_disks);
+ s = malloc(size);
+ if (!s)
+ break;
+ *space_tail = s;
+ space_tail = s;
+ *space_tail = NULL;
+ break;
+ }
+ }
+ if (update->space_list == NULL)
+ break;
+
+ /* add space for disk in update
+ */
+ size = sizeof(struct dl);
+ s = malloc(size);
+ if (!s) {
+ free(update->space_list);
+ update->space_list = NULL;
+ break;
+ }
+ *space_tail = s;
+ space_tail = s;
+ *space_tail = NULL;
+
+ /* add spare device to update
+ */
+ for (id = super->devlist ; id; id = id->next)
+ if (id->index == (unsigned)u->subdev) {
+ struct imsm_dev *dev;
+ struct imsm_map *map;
+
+ dev = get_imsm_dev(super, u->subdev);
+ map = get_imsm_map(dev, 0);
+ current_level = map->raid_level;
+ break;
+ }
+ if ((u->new_level == 5) && (u->new_level != current_level)) {
+ struct mdinfo *spares;
+
+ spares = get_spares_for_grow(st);
+ if (spares) {
+ struct dl *dl;
+ struct mdinfo *dev;
+
+ dev = spares->devs;
+ if (dev) {
+ u->new_disks[0] =
+ makedev(dev->disk.major,
+ dev->disk.minor);
+ dl = get_disk_super(super,
+ dev->disk.major,
+ dev->disk.minor);
+ dl->index = u->old_raid_disks;
+ dev = dev->next;
+ }
+ sysfs_free(spares);
+ }
+ }
+ len = disks_to_mpb_size(u->new_raid_disks);
+ dprintf("New anchor length is %llu\n", (unsigned long long)len);
+ break;
+ }
case update_create_array: {
struct imsm_update_create_array *u = (void *) update->buf;
struct intel_dev *dv;
return 0;
}
+/******************************************************************************
+ * function: imsm_create_metadata_update_for_migration()
+ * Creates update for IMSM array.
+ *
+ ******************************************************************************/
+static int imsm_create_metadata_update_for_migration(
+ struct supertype *st,
+ struct geo_params *geo,
+ struct imsm_update_reshape_migration **updatep)
+{
+ struct intel_super *super = st->sb;
+ int update_memory_size = 0;
+ struct imsm_update_reshape_migration *u = NULL;
+ struct imsm_dev *dev;
+ int previous_level = -1;
+
+ dprintf("imsm_create_metadata_update_for_migration(enter)"
+ " New Level = %i\n", geo->level);
+
+ /* size of all update data without anchor */
+ update_memory_size = sizeof(struct imsm_update_reshape_migration);
+
+ u = calloc(1, update_memory_size);
+ if (u == NULL) {
+ dprintf("error: cannot get memory for "
+ "imsm_create_metadata_update_for_migration\n");
+ return 0;
+ }
+ u->type = update_reshape_migration;
+ u->subdev = super->current_vol;
+ u->new_level = geo->level;
+ u->new_layout = geo->layout;
+ u->new_raid_disks = u->old_raid_disks = geo->raid_disks;
+ u->new_disks[0] = -1;
+ u->new_chunksize = -1;
+
+ dev = get_imsm_dev(super, u->subdev);
+ if (dev) {
+ struct imsm_map *map;
+
+ map = get_imsm_map(dev, 0);
+ if (map) {
+ int current_chunk_size =
+ __le16_to_cpu(map->blocks_per_strip) / 2;
+
+ if (geo->chunksize != current_chunk_size) {
+ u->new_chunksize = geo->chunksize / 1024;
+ dprintf("imsm: "
+ "chunk size change from %i to %i\n",
+ current_chunk_size, u->new_chunksize);
+ }
+ previous_level = map->raid_level;
+ }
+ }
+ if ((geo->level == 5) && (previous_level == 0)) {
+ struct mdinfo *spares = NULL;
+
+ u->new_raid_disks++;
+ spares = get_spares_for_grow(st);
+ if ((spares == NULL) || (spares->array.spare_disks < 1)) {
+ free(u);
+ sysfs_free(spares);
+ update_memory_size = 0;
+ dprintf("error: cannot get spare device "
+ "for requested migration");
+ return 0;
+ }
+ sysfs_free(spares);
+ }
+ dprintf("imsm: reshape update preparation : OK\n");
+ *updatep = u;
+
+ return update_memory_size;
+}
+
static void imsm_update_metadata_locally(struct supertype *st,
void *buf, int len)
{
check_devs = 1;
}
break;
- case 5:
- if (geo->level == 0)
- change = CH_MIGRATION;
- break;
case 10:
if (geo->level == 0) {
change = CH_TAKEOVER;
case CH_TAKEOVER:
ret_val = imsm_takeover(st, &geo);
break;
- case CH_MIGRATION:
+ case CH_MIGRATION: {
+ struct imsm_update_reshape_migration *u = NULL;
+ int len =
+ imsm_create_metadata_update_for_migration(
+ st, &geo, &u);
+ if (len < 1) {
+ dprintf("imsm: "
+ "Cannot prepare update\n");
+ break;
+ }
ret_val = 0;
- break;
+ /* update metadata locally */
+ imsm_update_metadata_locally(st, u, len);
+ /* and possibly remotely */
+ if (st->update_tail)
+ append_metadata_update(st, u, len);
+ else
+ free(u);
+ }
+ break;
default:
ret_val = 1;
}