update_add_remove_disk,
update_reshape_container_disks,
update_reshape_migration,
- update_takeover
+ update_takeover,
+ update_general_migration_checkpoint,
};
struct imsm_update_activate_spare {
int new_disks[1]; /* new_raid_disks - old_raid_disks makedev number */
};
+struct imsm_update_general_migration_checkpoint {
+ enum imsm_update_type type;
+ __u32 curr_migr_unit;
+};
+
struct disk_info {
__u8 serial[MAX_RAID_SERIAL_LEN];
};
migr_chunk = migr_strip_blocks_resync(dev);
disks = imsm_num_data_members(dev, 0);
blocks_per_unit = stripes_per_unit * migr_chunk * disks;
- stripe = __le32_to_cpu(map->blocks_per_strip) * disks;
+ stripe = __le16_to_cpu(map->blocks_per_strip) * disks;
segment = blocks_per_unit / stripe;
block_rel = blocks_per_unit - segment * stripe;
parity_depth = parity_segment_depth(dev);
return retval;
}
+/*******************************************************************************
+ * function: imsm_create_metadata_checkpoint_update
+ * Description: It creates update for checkpoint change.
+ * Parameters:
+ * super : imsm internal array info
+ * u : pointer to prepared update
+ * Returns:
+ * Uptate length.
+ * If length is equal to 0, input pointer u contains no update
+ ******************************************************************************/
+static int imsm_create_metadata_checkpoint_update(
+ struct intel_super *super,
+ struct imsm_update_general_migration_checkpoint **u)
+{
+
+ int update_memory_size = 0;
+
+ dprintf("imsm_create_metadata_checkpoint_update(enter)\n");
+
+ if (u == NULL)
+ return 0;
+ *u = NULL;
+
+ /* size of all update data without anchor */
+ update_memory_size =
+ sizeof(struct imsm_update_general_migration_checkpoint);
+
+ *u = calloc(1, update_memory_size);
+ if (*u == NULL) {
+ dprintf("error: cannot get memory for "
+ "imsm_create_metadata_checkpoint_update update\n");
+ return 0;
+ }
+ (*u)->type = update_general_migration_checkpoint;
+ (*u)->curr_migr_unit = __le32_to_cpu(super->migr_rec->curr_migr_unit);
+ dprintf("imsm_create_metadata_checkpoint_update: prepared for %u\n",
+ (*u)->curr_migr_unit);
+
+ return update_memory_size;
+}
+
+
+static void imsm_update_metadata_locally(struct supertype *st,
+ void *buf, int len);
+
/*******************************************************************************
* Function: write_imsm_migr_rec
* Description: Function writes imsm migration record
int fd = -1;
int retval = -1;
struct dl *sd;
+ int len;
+ struct imsm_update_general_migration_checkpoint *u;
for (sd = super->disks ; sd ; sd = sd->next) {
/* write to 2 first slots only */
close(fd);
fd = -1;
}
+ /* update checkpoint information in metadata */
+ len = imsm_create_metadata_checkpoint_update(super, &u);
+
+ if (len <= 0) {
+ dprintf("imsm: Cannot prepare update\n");
+ goto out;
+ }
+ /* update metadata locally */
+ imsm_update_metadata_locally(st, u, len);
+ /* and possibly remotely */
+ if (st->update_tail) {
+ append_metadata_update(st, u, len);
+ /* during reshape we do all work inside metadata handler
+ * manage_reshape(), so metadata update has to be triggered
+ * insida it
+ */
+ flush_metadata_updates(st);
+ st->update_tail = &st->updates;
+ } else
+ free(u);
retval = 0;
out:
if (prev_map)
map_to_analyse = prev_map;
- for (dl = super->disks; dl; dl = dl->next)
- if (dl->raiddisk == info->disk.raid_disk)
- break;
+ dl = super->disks;
+
info->container_member = super->current_vol;
info->array.raid_disks = map->num_members;
info->array.level = get_imsm_raid_level(map_to_analyse);
unsigned long long array_blocks;
int used_disks;
+ if (__le32_to_cpu(migr_rec->ascending_migr) &&
+ (units <
+ (__le32_to_cpu(migr_rec->num_migr_units)-1)) &&
+ (super->migr_rec->rec_status ==
+ __cpu_to_le32(UNIT_SRC_IN_CP_AREA)))
+ units++;
+
info->reshape_progress = blocks_per_unit * units;
dprintf("IMSM: General Migration checkpoint : %llu "
if (lseek64(fd, dsize - (512 * 2), SEEK_SET) < 0) {
if (devname)
- fprintf(stderr,
- Name ": Cannot seek to anchor block on %s: %s\n",
+ fprintf(stderr, Name
+ ": Cannot seek to anchor block on %s: %s\n",
devname, strerror(errno));
return 1;
}
}
/* load migration record */
- load_imsm_migr_rec(super, NULL);
-
- /* Check for unsupported migration features */
- if (check_mpb_migr_compatibility(super) != 0) {
- fprintf(stderr, Name ": Unsupported migration detected");
- if (devname)
- fprintf(stderr, " on %s\n", devname);
- else
- fprintf(stderr, " (IMSM).\n");
- return 3;
+ if (load_imsm_migr_rec(super, NULL) == 0) {
+ /* Check for unsupported migration features */
+ if (check_mpb_migr_compatibility(super) != 0) {
+ fprintf(stderr,
+ Name ": Unsupported migration detected");
+ if (devname)
+ fprintf(stderr, " on %s\n", devname);
+ else
+ fprintf(stderr, " (IMSM).\n");
+ return 3;
+ }
}
return 0;
sizeof(*this));
break;
}
- memset(this, 0, sizeof(*this));
- this->next = rest;
super->current_vol = i;
getinfo_super_imsm_volume(st, this, NULL);
+ this->next = rest;
for (slot = 0 ; slot < map->num_members; slot++) {
unsigned long long recovery_start;
struct mdinfo *info_d;
}
mark_checkpoint:
+ /* skip checkpointing for general migration,
+ * it is controlled in mdadm
+ */
+ if (is_gen_migration(dev))
+ goto skip_mark_checkpoint;
+
/* check if we can update curr_migr_unit from resync_start, recovery_start */
blocks_per_unit = blocks_per_migr_unit(super, dev);
if (blocks_per_unit) {
}
}
+skip_mark_checkpoint:
/* mark dirty / clean */
if (dev->vol.dirty != !consistent) {
dprintf("imsm: mark '%s'\n", consistent ? "clean" : "dirty");
mpb = super->anchor;
switch (type) {
+ case update_general_migration_checkpoint: {
+ struct intel_dev *id;
+ struct imsm_update_general_migration_checkpoint *u =
+ (void *)update->buf;
+
+ dprintf("imsm: process_update() "
+ "for update_general_migration_checkpoint called\n");
+
+ /* find device under general migration */
+ for (id = super->devlist ; id; id = id->next) {
+ if (is_gen_migration(id->dev)) {
+ id->dev->vol.curr_migr_unit =
+ __cpu_to_le32(u->curr_migr_unit);
+ super->updates_pending++;
+ }
+ }
+ break;
+ }
case update_takeover: {
struct imsm_update_takeover *u = (void *)update->buf;
if (apply_takeover_update(u, super, &update->space_list)) {
size_t len = 0;
switch (type) {
+ case update_general_migration_checkpoint:
+ dprintf("imsm: prepare_update() "
+ "for update_general_migration_checkpoint called\n");
+ break;
case update_takeover: {
struct imsm_update_takeover *u = (void *)update->buf;
if (u->direction == R0_TO_R10) {
struct imsm_map *map_dest = get_imsm_map(dev, 0);
struct imsm_map *map_src = get_imsm_map(dev, 1);
unsigned long long num_migr_units;
-
- unsigned long long array_blocks =
- (((unsigned long long)__le32_to_cpu(dev->size_high)) << 32) +
- __le32_to_cpu(dev->size_low);
+ unsigned long long array_blocks;
memset(migr_rec, 0, sizeof(struct migr_record));
migr_rec->family_num = __cpu_to_le32(super->anchor->family_num);
__cpu_to_le32(migr_rec->dest_depth_per_unit * new_data_disks);
migr_rec->dest_depth_per_unit =
__cpu_to_le32(migr_rec->dest_depth_per_unit);
-
+ array_blocks = info->component_size * new_data_disks;
num_migr_units =
array_blocks / __le32_to_cpu(migr_rec->blocks_per_unit);
* and to write it to the Copy Area.
* Parameters:
* st : supertype information
+ * dev : imsm device that backup is saved for
* info : general array info
* buf : input buffer
- * write_offset : address of data to backup
* length : length of data to backup (blocks_per_unit)
* Returns:
* 0 : success
struct imsm_dev *dev,
struct mdinfo *info,
void *buf,
- int new_data,
int length)
{
int rv = -1;
int i;
struct imsm_map *map_dest = get_imsm_map(dev, 0);
int new_disks = map_dest->num_members;
+ int dest_layout = 0;
+ int dest_chunk;
targets = malloc(new_disks * sizeof(int));
if (!targets)
if (open_backup_targets(info, new_disks, targets))
goto abort;
+ if (map_dest->raid_level != 0)
+ dest_layout = ALGORITHM_LEFT_ASYMMETRIC;
+ dest_chunk = __le16_to_cpu(map_dest->blocks_per_strip) * 512;
+
if (restore_stripes(targets, /* list of dest devices */
target_offsets, /* migration record offsets */
new_disks,
- info->new_chunk,
- info->new_level,
- info->new_layout,
- -1, /* source backup file descriptor */
- 0, /* input buf offset
- * always 0 buf is already offset */
+ dest_chunk,
+ map_dest->raid_level,
+ dest_layout,
+ -1, /* source backup file descriptor */
+ 0, /* input buf offset
+ * always 0 buf is already offseted */
0,
length,
buf) != 0) {
* Returns:
* 0: success
* 1: failure
+ * 2: failure, means no valid migration record
+ * / no general migration in progress /
******************************************************************************/
int save_checkpoint_imsm(struct supertype *st, struct mdinfo *info, int state)
{
struct intel_super *super = st->sb;
- load_imsm_migr_rec(super, info);
- if (__le32_to_cpu(super->migr_rec->blocks_per_unit) == 0) {
- dprintf("ERROR: blocks_per_unit = 0!!!\n");
+ if (load_imsm_migr_rec(super, info) != 0) {
+ dprintf("imsm: ERROR: Cannot read migration record "
+ "for checkpoint save.\n");
return 1;
}
+ if (__le32_to_cpu(super->migr_rec->blocks_per_unit) == 0) {
+ dprintf("imsm: no migration in progress.\n");
+ return 2;
+ }
+
super->migr_rec->curr_migr_unit =
__cpu_to_le32(info->reshape_progress /
__le32_to_cpu(super->migr_rec->blocks_per_unit));
int retval = 1;
unsigned long curr_migr_unit = __le32_to_cpu(migr_rec->curr_migr_unit);
unsigned long num_migr_units = __le32_to_cpu(migr_rec->num_migr_units);
- int ascending = __le32_to_cpu(migr_rec->ascending_migr);
char buffer[20];
+ int skipped_disks = 0;
+ int max_degradation;
err = sysfs_get_str(info, NULL, "array_state", (char *)buffer, 20);
if (err < 1)
map_dest = get_imsm_map(id->dev, 0);
new_disks = map_dest->num_members;
+ max_degradation = new_disks - imsm_num_data_members(id->dev, 0);
read_offset = (unsigned long long)
__le32_to_cpu(migr_rec->ckpt_area_pba) * 512;
write_offset = ((unsigned long long)
__le32_to_cpu(migr_rec->dest_1st_member_lba) +
- info->data_offset) * 512;
+ __le32_to_cpu(map_dest->pba_of_lba0)) * 512;
unit_len = __le32_to_cpu(migr_rec->dest_depth_per_unit) * 512;
if (posix_memalign((void **)&buf, 512, unit_len) != 0)
open_backup_targets(info, new_disks, targets);
for (i = 0; i < new_disks; i++) {
+ if (targets[i] < 0) {
+ skipped_disks++;
+ continue;
+ }
if (lseek64(targets[i], read_offset, SEEK_SET) < 0) {
fprintf(stderr,
Name ": Cannot seek to block: %s\n",
}
}
- if (ascending && curr_migr_unit < (num_migr_units-1))
- curr_migr_unit++;
+ if (skipped_disks > max_degradation) {
+ fprintf(stderr,
+ Name ": Cannot restore data from backup."
+ " Too many failed disks\n");
+ goto abort;
+ }
- migr_rec->curr_migr_unit = __le32_to_cpu(curr_migr_unit);
- super->migr_rec->rec_status = __cpu_to_le32(UNIT_SRC_NORMAL);
- if (write_imsm_migr_rec(st) == 0) {
- __u64 blocks_per_unit = blocks_per_migr_unit(super, id->dev);
- info->reshape_progress = curr_migr_unit * blocks_per_unit;
+ if (save_checkpoint_imsm(st, info, UNIT_SRC_NORMAL)) {
+ /* ignore error == 2, this can mean end of reshape here
+ */
+ dprintf("imsm: Cannot write checkpoint to "
+ "migration record (UNIT_SRC_NORMAL) during restart\n");
+ } else
retval = 0;
- }
abort:
if (targets) {
int chunk;
getinfo_super_imsm_volume(st, &info, NULL);
-
if ((geo->level != info.array.level) &&
(geo->level >= 0) &&
(geo->level != UnSet)) {
case 0:
if (geo->level == 5) {
change = CH_MIGRATION;
+ if (geo->layout != ALGORITHM_LEFT_ASYMMETRIC) {
+ fprintf(stderr,
+ Name " Error. Requested Layout "
+ "not supported (left-asymmetric layout "
+ "is supported only)!\n");
+ change = -1;
+ goto analyse_change_exit;
+ }
check_devs = 1;
}
if (geo->level == 10) {
return 0;
}
-static int warn_user_about_risk(void)
-{
- int rv = 0;
-
- fprintf(stderr,
- "\nThis is an experimental feature. Data on the RAID volume(s) "
- "can be lost!!!\n\n"
- "To continue command execution please make sure that\n"
- "the grow process will not be interrupted. Use safe power\n"
- "supply to avoid unexpected system reboot. Make sure that\n"
- "reshaped container is not assembled automatically during\n"
- "system boot.\n"
- "If reshape is interrupted, assemble array manually\n"
- "using e.g. '-Ac' option and up to date mdadm.conf file.\n"
- "Assembly in scan mode is not possible in such case.\n"
- "Growing container with boot array is not possible.\n"
- "If boot array reshape is interrupted, whole file system\n"
- "can be lost.\n\n");
- rv = ask("Do you want to continue? ");
- fprintf(stderr, "\n");
-
- return rv;
-}
-
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,
dprintf("imsm: info: Container operation\n");
int old_raid_disks = 0;
- /* this warning will be removed when imsm checkpointing
- * will be implemented, and restoring from check-point
- * operation will be transparent for reboot process
- */
- if (warn_user_about_risk() == 0)
- return ret_val;
-
if (imsm_reshape_is_allowed_on_container(
st, &geo, &old_raid_disks)) {
struct imsm_update_reshape *u = NULL;
sysfs_set_str(sra, NULL, "sync_max", "max");
to_complete = MaxSector;
} else {
- if (completed > to_complete)
+ if (completed > to_complete) {
+ close(fd);
return -1;
+ }
if (sysfs_set_num(sra, NULL, "sync_max",
to_complete / ndata) != 0) {
close(fd);
unsigned long long start; /* [bytes] */
unsigned long long start_buf_shift; /* [bytes] */
int degraded = 0;
+ int source_layout = 0;
- if (!fds || !offsets || !destfd || !destoffsets || !sra)
+ if (!fds || !offsets || !sra)
goto abort;
/* Find volume during the reshape */
ndata = imsm_num_data_members(dev, 0);
odata = imsm_num_data_members(dev, 1);
- chunk = map_src->blocks_per_strip * 512;
+ chunk = __le16_to_cpu(map_src->blocks_per_strip) * 512;
old_data_stripe_length = odata * chunk;
migr_rec = super->migr_rec;
goto abort;
}
- max_position =
- __le32_to_cpu(migr_rec->post_migr_vol_cap) +
- ((unsigned long long)__le32_to_cpu(
- migr_rec->post_migr_vol_cap_hi) << 32);
+ max_position = sra->component_size * ndata;
+ if (map_src->raid_level != 0)
+ source_layout = ALGORITHM_LEFT_ASYMMETRIC;
while (__le32_to_cpu(migr_rec->curr_migr_unit) <
__le32_to_cpu(migr_rec->num_migr_units)) {
if ((current_position + next_step) > max_position)
next_step = max_position - current_position;
- start = (map_src->pba_of_lba0 + dev->reserved_blocks +
- current_position) * 512;
+ start = current_position * 512;
/* allign reading start to old geometry */
start_buf_shift = start % old_data_stripe_length;
start_buf_shift, next_step_filler);
if (save_stripes(fds, offsets, map_src->num_members,
- chunk, sra->array.level,
- sra->array.layout, 0, NULL, start_src,
+ chunk, map_src->raid_level,
+ source_layout, 0, NULL, start_src,
copy_length +
next_step_filler + start_buf_shift,
buf)) {
* in backup general migration area
*/
if (save_backup_imsm(st, dev, sra,
- buf + start_buf_shift,
- ndata, copy_length)) {
+ buf + start_buf_shift, copy_length)) {
dprintf("imsm: Cannot save stripes to "
"target devices\n");
goto abort;
sra->reshape_progress = next_step;
- if (save_checkpoint_imsm(st, sra, UNIT_SRC_NORMAL)) {
+ if (save_checkpoint_imsm(st, sra, UNIT_SRC_NORMAL) == 1) {
+ /* ignore error == 2, this can mean end of reshape here
+ */
dprintf("imsm: Cannot write checkpoint to "
"migration record (UNIT_SRC_NORMAL)\n");
goto abort;