update_reshape_migration,
update_takeover,
update_general_migration_checkpoint,
+ update_size_change,
};
struct imsm_update_activate_spare {
int new_disks[1]; /* new_raid_disks - old_raid_disks makedev number */
};
+struct imsm_update_size_change {
+ enum imsm_update_type type;
+ int subdev;
+ long long new_size;
+};
+
struct imsm_update_general_migration_checkpoint {
enum imsm_update_type type;
__u32 curr_migr_unit;
}
return rv;
}
+static unsigned long long imsm_component_size_aligment_check(int level,
+ int chunk_size,
+ unsigned long long component_size)
+{
+ unsigned int component_size_alligment;
+
+ /* check component size aligment
+ */
+ component_size_alligment = component_size % (chunk_size/512);
+
+ dprintf("imsm_component_size_aligment_check(Level: %i, "
+ "chunk_size = %i, component_size = %llu), "
+ "component_size_alligment = %u\n",
+ level, chunk_size, component_size,
+ component_size_alligment);
+
+ if (component_size_alligment && (level != 1) && (level != UnSet)) {
+ dprintf("imsm: reported component size alligned from %llu ",
+ component_size);
+ component_size -= component_size_alligment;
+ dprintf("to %llu (%i).\n",
+ component_size, component_size_alligment);
+ }
+
+ return component_size;
+}
static void getinfo_super_imsm_volume(struct supertype *st, struct mdinfo *info, char *dmap)
{
struct imsm_map *map_to_analyse = map;
struct dl *dl;
char *devname;
- unsigned int component_size_alligment;
int map_disks = info->array.raid_disks;
memset(info, 0, sizeof(*info));
info->data_offset = pba_of_lba0(map_to_analyse);
info->component_size = blocks_per_member(map_to_analyse);
- /* check component size aligment
- */
- component_size_alligment =
- info->component_size % (info->array.chunk_size/512);
-
- if (component_size_alligment &&
- (info->array.level != 1) && (info->array.level != UnSet)) {
- dprintf("imsm: reported component size alligned from %llu ",
- info->component_size);
- info->component_size -= component_size_alligment;
- dprintf("to %llu (%i).\n",
- info->component_size, component_size_alligment);
- }
+ info->component_size = imsm_component_size_aligment_check(
+ info->array.level,
+ info->array.chunk_size,
+ info->component_size);
memset(info->uuid, 0, sizeof(info->uuid));
info->recovery_start = MaxSector;
return 1;
}
-static int reserve_space(struct supertype *st, int raiddisks,
+static int imsm_get_free_size(struct supertype *st, int raiddisks,
unsigned long long size, int chunk,
unsigned long long *freesize)
{
*freesize = size;
+ dprintf("imsm: imsm_get_free_size() returns : %llu\n", size);
+
return 1;
}
+static int reserve_space(struct supertype *st, int raiddisks,
+ unsigned long long size, int chunk,
+ unsigned long long *freesize)
+{
+ struct intel_super *super = st->sb;
+ struct dl *dl;
+ int cnt;
+ int rv = 0;
+
+ rv = imsm_get_free_size(st, raiddisks, size, chunk, freesize);
+ if (rv) {
+ cnt = 0;
+ for (dl = super->disks; dl; dl = dl->next)
+ if (dl->e)
+ dl->raiddisk = cnt++;
+ rv = 1;
+ }
+
+ return rv;
+}
+
static int validate_geometry_imsm(struct supertype *st, int level, int layout,
int raiddisks, int *chunk, unsigned long long size,
char *dev, unsigned long long *freesize,
super->updates_pending++;
}
-static unsigned long long imsm_set_array_size(struct imsm_dev *dev)
+static unsigned long long imsm_set_array_size(struct imsm_dev *dev,
+ long long new_size)
{
int used_disks = imsm_num_data_members(dev, MAP_0);
unsigned long long array_blocks;
/* set array size in metadata
*/
- map = get_imsm_map(dev, MAP_0);
- array_blocks = blocks_per_member(map) * used_disks;
+ if (new_size <= 0) {
+ /* OLCE size change is caused by added disks
+ */
+ map = get_imsm_map(dev, MAP_0);
+ array_blocks = blocks_per_member(map) * used_disks;
+ } else {
+ /* Online Volume Size Change
+ * Using available free space
+ */
+ array_blocks = new_size;
+ }
/* round array size down to closest MB
*/
memcpy(map2, map, copy_map_size);
map2->num_members = prev_num_members;
- imsm_set_array_size(dev);
+ imsm_set_array_size(dev, -1);
super->clean_migration_record_by_mdmon = 1;
super->updates_pending++;
}
*tofree = *space_list;
/* calculate new size
*/
- imsm_set_array_size(new_dev);
+ imsm_set_array_size(new_dev, -1);
ret_val = 1;
}
return ret_val;
}
+static int apply_size_change_update(struct imsm_update_size_change *u,
+ struct intel_super *super)
+{
+ struct intel_dev *id;
+ int ret_val = 0;
+
+ dprintf("apply_size_change_update()\n");
+ if ((u->subdev < 0) ||
+ (u->subdev > 1)) {
+ dprintf("imsm: Error: Wrong subdev: %i\n", u->subdev);
+ 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 = get_imsm_map(dev, MAP_0);
+ int used_disks = imsm_num_data_members(dev, MAP_0);
+ unsigned long long blocks_per_member;
+
+ /* calculate new size
+ */
+ blocks_per_member = u->new_size / used_disks;
+ dprintf("imsm: apply_size_change_update(size: %llu, "
+ "blocks per member: %llu)\n",
+ u->new_size, blocks_per_member);
+ set_blocks_per_member(map, blocks_per_member);
+ imsm_set_array_size(dev, u->new_size);
+
+ ret_val = 1;
+ break;
+ }
+ }
+
+ return ret_val;
+}
+
+
static int apply_update_activate_spare(struct imsm_update_activate_spare *u,
struct intel_super *super,
struct active_array *active_array)
newmap = get_imsm_map(newdev, MAP_1);
memcpy(newmap, oldmap, sizeof_imsm_map(oldmap));
- imsm_set_array_size(newdev);
+ imsm_set_array_size(newdev, -1);
}
sp = (void **)id->dev;
super->updates_pending++;
break;
}
+ case update_size_change: {
+ struct imsm_update_size_change *u = (void *)update->buf;
+ if (apply_size_change_update(u, super))
+ super->updates_pending++;
+ break;
+ }
case update_activate_spare: {
struct imsm_update_activate_spare *u = (void *) update->buf;
if (apply_update_activate_spare(u, super, st->arrays))
dprintf("New anchor length is %llu\n", (unsigned long long)len);
break;
}
+ case update_size_change: {
+ break;
+ }
case update_create_array: {
struct imsm_update_create_array *u = (void *) update->buf;
struct intel_dev *dv;
static int imsm_reshape_is_allowed_on_container(struct supertype *st,
struct geo_params *geo,
- int *old_raid_disks)
+ int *old_raid_disks,
+ int direction)
{
/* currently we only support increasing the number of devices
* for a container. This increases the number of device for each
return ret_val;
}
+ if (direction == ROLLBACK_METADATA_CHANGES) {
+ dprintf("imsm: Metadata changes rollback is not supported for "
+ "container operation.\n");
+ return ret_val;
+ }
+
info = container_content_imsm(st, NULL);
for (member = info; member; member = member->next) {
int result;
return 0;
}
+
+/******************************************************************************
+ * function: imsm_create_metadata_update_for_size_change()
+ * Creates update for IMSM array for array size change.
+ *
+ ******************************************************************************/
+static int imsm_create_metadata_update_for_size_change(
+ struct supertype *st,
+ struct geo_params *geo,
+ struct imsm_update_size_change **updatep)
+{
+ struct intel_super *super = st->sb;
+ int update_memory_size = 0;
+ struct imsm_update_size_change *u = NULL;
+
+ dprintf("imsm_create_metadata_update_for_size_change(enter)"
+ " New size = %llu\n", geo->size);
+
+ /* size of all update data without anchor */
+ update_memory_size = sizeof(struct imsm_update_size_change);
+
+ u = calloc(1, update_memory_size);
+ if (u == NULL) {
+ dprintf("error: cannot get memory for "
+ "imsm_create_metadata_update_for_size_change\n");
+ return 0;
+ }
+ u->type = update_size_change;
+ u->subdev = super->current_vol;
+ u->new_size = geo->size;
+
+ dprintf("imsm: reshape update preparation : OK\n");
+ *updatep = u;
+
+ return update_memory_size;
+}
+
/******************************************************************************
* function: imsm_create_metadata_update_for_migration()
* Creates update for IMSM array.
* Function: imsm_analyze_change
* Description: Function analyze change for single volume
* and validate if transition is supported
-* Parameters: Geometry parameters, supertype structure
+* Parameters: Geometry parameters, supertype structure,
+* metadata change direction (apply/rollback)
* Returns: Operation type code on success, -1 if fail
****************************************************************************/
enum imsm_reshape_type imsm_analyze_change(struct supertype *st,
- struct geo_params *geo)
+ struct geo_params *geo,
+ int direction)
{
struct mdinfo info;
int change = -1;
super = st->sb;
dev = get_imsm_dev(super, super->current_vol);
data_disks = imsm_num_data_members(dev , MAP_0);
- /* compute current size in K per disk member
+ /* compute current size per disk member
*/
- current_size = info.custom_array_size / 2 / data_disks;
+ current_size = info.custom_array_size / data_disks;
- if ((current_size != geo->size) && (geo->size > 0)) {
+ if (geo->size > 0) {
+ /* align component size
+ */
+ geo->size = imsm_component_size_aligment_check(
+ get_imsm_raid_level(dev->vol.map),
+ chunk * 1024,
+ geo->size * 2);
+ }
+
+ if ((current_size != geo->size) && (geo->size >= 0)) {
if (change != -1) {
fprintf(stderr,
Name " Error. Size change should be the only "
super->current_vol, st->devnum);
goto analyse_change_exit;
}
- geo->size *= 2;
- /* round size due to metadata compatibility
- */
- geo->size = (geo->size >> SECT_PER_MB_SHIFT)
- << SECT_PER_MB_SHIFT;
- dprintf("Prepare update for size change to %llu\n", geo->size );
- if (current_size >= geo->size) {
- fprintf(stderr,
- Name " Error. Size expanssion is supported only"
- " (current size is %llu, requested size "
- "/rounded/ is %llu).\n",
- current_size, geo->size);
- goto analyse_change_exit;
+ if (geo->size == 0) {
+ /* requested size change to the maximum available size
+ */
+ unsigned long long freesize;
+ int rv;
+
+ rv = imsm_get_free_size(st, dev->vol.map->num_members,
+ 0, chunk, &freesize);
+ if (rv == 0) {
+ fprintf(stderr, Name " Error. Cannot find "
+ "maximum available space.\n");
+ change = -1;
+ goto analyse_change_exit;
+ }
+ geo->size = freesize + current_size;
+
+ /* align component size
+ */
+ geo->size = imsm_component_size_aligment_check(
+ get_imsm_raid_level(dev->vol.map),
+ chunk * 1024,
+ geo->size);
+ }
+
+ if ((direction == ROLLBACK_METADATA_CHANGES)) {
+ /* accept size for rollback only
+ */
+ } else {
+ /* round size due to metadata compatibility
+ */
+ geo->size = (geo->size >> SECT_PER_MB_SHIFT)
+ << SECT_PER_MB_SHIFT;
+ dprintf("Prepare update for size change to %llu\n",
+ geo->size );
+ if (current_size >= geo->size) {
+ fprintf(stderr,
+ Name " Error. Size expanssion is "
+ "supported only (current size is %llu, "
+ "requested size /rounded/ is %llu).\n",
+ current_size, geo->size);
+ goto analyse_change_exit;
+ }
}
geo->size *= data_disks;
geo->raid_disks = dev->vol.map->num_members;
}
analyse_change_exit:
-
+ if ((direction == ROLLBACK_METADATA_CHANGES) &&
+ ((change == CH_MIGRATION) || (change == CH_TAKEOVER))) {
+ dprintf("imsm: Metadata changes rollback is not supported for "
+ "migration and takeover operations.\n");
+ change = -1;
+ }
return change;
}
static int imsm_reshape_super(struct supertype *st, long long size, int level,
int layout, int chunksize, int raid_disks,
int delta_disks, char *backup, char *dev,
- int verbose)
+ int direction, int verbose)
{
int ret_val = 1;
struct geo_params geo;
int old_raid_disks = 0;
if (imsm_reshape_is_allowed_on_container(
- st, &geo, &old_raid_disks)) {
+ st, &geo, &old_raid_disks, direction)) {
struct imsm_update_reshape *u = NULL;
int len;
goto exit_imsm_reshape_super;
}
super->current_vol = dev->index;
- change = imsm_analyze_change(st, &geo);
+ change = imsm_analyze_change(st, &geo, direction);
switch (change) {
case CH_TAKEOVER:
ret_val = imsm_takeover(st, &geo);
}
break;
case CH_ARRAY_SIZE: {
- /* ToDo: Prepare metadata update here
- */
+ struct imsm_update_size_change *u = NULL;
+ int len =
+ imsm_create_metadata_update_for_size_change(
+ st, &geo, &u);
+ if (len < 1) {
+ dprintf("imsm: "
+ "Cannot prepare update\n");
+ break;
+ }
+ 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
+ free(u);
}
break;
default: