in dm-raid.c [3000:3316]
static int raid_ctr(struct dm_target *ti, unsigned int argc, char **argv)
{
int r;
bool resize = false;
struct raid_type *rt;
unsigned int num_raid_params, num_raid_devs;
sector_t sb_array_sectors, rdev_sectors, reshape_sectors;
struct raid_set *rs = NULL;
const char *arg;
struct rs_layout rs_layout;
struct dm_arg_set as = { argc, argv }, as_nrd;
struct dm_arg _args[] = {
{ 0, as.argc, "Cannot understand number of raid parameters" },
{ 1, 254, "Cannot understand number of raid devices parameters" }
};
arg = dm_shift_arg(&as);
if (!arg) {
ti->error = "No arguments";
return -EINVAL;
}
rt = get_raid_type(arg);
if (!rt) {
ti->error = "Unrecognised raid_type";
return -EINVAL;
}
/* Must have <#raid_params> */
if (dm_read_arg_group(_args, &as, &num_raid_params, &ti->error))
return -EINVAL;
/* number of raid device tupples <meta_dev data_dev> */
as_nrd = as;
dm_consume_args(&as_nrd, num_raid_params);
_args[1].max = (as_nrd.argc - 1) / 2;
if (dm_read_arg(_args + 1, &as_nrd, &num_raid_devs, &ti->error))
return -EINVAL;
if (!__within_range(num_raid_devs, 1, MAX_RAID_DEVICES)) {
ti->error = "Invalid number of supplied raid devices";
return -EINVAL;
}
rs = raid_set_alloc(ti, rt, num_raid_devs);
if (IS_ERR(rs))
return PTR_ERR(rs);
r = parse_raid_params(rs, &as, num_raid_params);
if (r)
goto bad;
r = parse_dev_params(rs, &as);
if (r)
goto bad;
rs->md.sync_super = super_sync;
/*
* Calculate ctr requested array and device sizes to allow
* for superblock analysis needing device sizes defined.
*
* Any existing superblock will overwrite the array and device sizes
*/
r = rs_set_dev_and_array_sectors(rs, rs->ti->len, false);
if (r)
goto bad;
/* Memorize just calculated, potentially larger sizes to grow the raid set in preresume */
rs->array_sectors = rs->md.array_sectors;
rs->dev_sectors = rs->md.dev_sectors;
/*
* Backup any new raid set level, layout, ...
* requested to be able to compare to superblock
* members for conversion decisions.
*/
rs_config_backup(rs, &rs_layout);
r = analyse_superblocks(ti, rs);
if (r)
goto bad;
/* All in-core metadata now as of current superblocks after calling analyse_superblocks() */
sb_array_sectors = rs->md.array_sectors;
rdev_sectors = __rdev_sectors(rs);
if (!rdev_sectors) {
ti->error = "Invalid rdev size";
r = -EINVAL;
goto bad;
}
reshape_sectors = _get_reshape_sectors(rs);
if (rs->dev_sectors != rdev_sectors) {
resize = (rs->dev_sectors != rdev_sectors - reshape_sectors);
if (rs->dev_sectors > rdev_sectors - reshape_sectors)
set_bit(RT_FLAG_RS_GROW, &rs->runtime_flags);
}
INIT_WORK(&rs->md.event_work, do_table_event);
ti->private = rs;
ti->num_flush_bios = 1;
/* Restore any requested new layout for conversion decision */
rs_config_restore(rs, &rs_layout);
/*
* Now that we have any superblock metadata available,
* check for new, recovering, reshaping, to be taken over,
* to be reshaped or an existing, unchanged raid set to
* run in sequence.
*/
if (test_bit(MD_ARRAY_FIRST_USE, &rs->md.flags)) {
/* A new raid6 set has to be recovered to ensure proper parity and Q-Syndrome */
if (rs_is_raid6(rs) &&
test_bit(__CTR_FLAG_NOSYNC, &rs->ctr_flags)) {
ti->error = "'nosync' not allowed for new raid6 set";
r = -EINVAL;
goto bad;
}
rs_setup_recovery(rs, 0);
set_bit(RT_FLAG_UPDATE_SBS, &rs->runtime_flags);
rs_set_new(rs);
} else if (rs_is_recovering(rs)) {
/* A recovering raid set may be resized */
goto size_check;
} else if (rs_is_reshaping(rs)) {
/* Have to reject size change request during reshape */
if (resize) {
ti->error = "Can't resize a reshaping raid set";
r = -EPERM;
goto bad;
}
/* skip setup rs */
} else if (rs_takeover_requested(rs)) {
if (rs_is_reshaping(rs)) {
ti->error = "Can't takeover a reshaping raid set";
r = -EPERM;
goto bad;
}
/* We can't takeover a journaled raid4/5/6 */
if (test_bit(__CTR_FLAG_JOURNAL_DEV, &rs->ctr_flags)) {
ti->error = "Can't takeover a journaled raid4/5/6 set";
r = -EPERM;
goto bad;
}
/*
* If a takeover is needed, userspace sets any additional
* devices to rebuild and we can check for a valid request here.
*
* If acceptible, set the level to the new requested
* one, prohibit requesting recovery, allow the raid
* set to run and store superblocks during resume.
*/
r = rs_check_takeover(rs);
if (r)
goto bad;
r = rs_setup_takeover(rs);
if (r)
goto bad;
set_bit(RT_FLAG_UPDATE_SBS, &rs->runtime_flags);
/* Takeover ain't recovery, so disable recovery */
rs_setup_recovery(rs, MaxSector);
rs_set_new(rs);
} else if (rs_reshape_requested(rs)) {
/* Only request grow on raid set size extensions, not on reshapes. */
clear_bit(RT_FLAG_RS_GROW, &rs->runtime_flags);
/*
* No need to check for 'ongoing' takeover here, because takeover
* is an instant operation as oposed to an ongoing reshape.
*/
/* We can't reshape a journaled raid4/5/6 */
if (test_bit(__CTR_FLAG_JOURNAL_DEV, &rs->ctr_flags)) {
ti->error = "Can't reshape a journaled raid4/5/6 set";
r = -EPERM;
goto bad;
}
/* Out-of-place space has to be available to allow for a reshape unless raid1! */
if (reshape_sectors || rs_is_raid1(rs)) {
/*
* We can only prepare for a reshape here, because the
* raid set needs to run to provide the repective reshape
* check functions via its MD personality instance.
*
* So do the reshape check after md_run() succeeded.
*/
r = rs_prepare_reshape(rs);
if (r)
goto bad;
/* Reshaping ain't recovery, so disable recovery */
rs_setup_recovery(rs, MaxSector);
}
rs_set_cur(rs);
} else {
size_check:
/* May not set recovery when a device rebuild is requested */
if (test_bit(__CTR_FLAG_REBUILD, &rs->ctr_flags)) {
clear_bit(RT_FLAG_RS_GROW, &rs->runtime_flags);
set_bit(RT_FLAG_UPDATE_SBS, &rs->runtime_flags);
rs_setup_recovery(rs, MaxSector);
} else if (test_bit(RT_FLAG_RS_GROW, &rs->runtime_flags)) {
/*
* Set raid set to current size, i.e. size as of
* superblocks to grow to larger size in preresume.
*/
r = rs_set_dev_and_array_sectors(rs, sb_array_sectors, false);
if (r)
goto bad;
rs_setup_recovery(rs, rs->md.recovery_cp < rs->md.dev_sectors ? rs->md.recovery_cp : rs->md.dev_sectors);
} else {
/* This is no size change or it is shrinking, update size and record in superblocks */
r = rs_set_dev_and_array_sectors(rs, rs->ti->len, false);
if (r)
goto bad;
if (sb_array_sectors > rs->array_sectors)
set_bit(RT_FLAG_UPDATE_SBS, &rs->runtime_flags);
}
rs_set_cur(rs);
}
/* If constructor requested it, change data and new_data offsets */
r = rs_adjust_data_offsets(rs);
if (r)
goto bad;
/* Catch any inconclusive reshape superblock content. */
rs_reset_inconclusive_reshape(rs);
/* Start raid set read-only and assumed clean to change in raid_resume() */
rs->md.ro = 1;
rs->md.in_sync = 1;
/* Keep array frozen until resume. */
set_bit(MD_RECOVERY_FROZEN, &rs->md.recovery);
/* Has to be held on running the array */
mddev_lock_nointr(&rs->md);
r = md_run(&rs->md);
rs->md.in_sync = 0; /* Assume already marked dirty */
if (r) {
ti->error = "Failed to run raid array";
mddev_unlock(&rs->md);
goto bad;
}
r = md_start(&rs->md);
if (r) {
ti->error = "Failed to start raid array";
mddev_unlock(&rs->md);
goto bad_md_start;
}
/* If raid4/5/6 journal mode explicitly requested (only possible with journal dev) -> set it */
if (test_bit(__CTR_FLAG_JOURNAL_MODE, &rs->ctr_flags)) {
r = r5c_journal_mode_set(&rs->md, rs->journal_dev.mode);
if (r) {
ti->error = "Failed to set raid4/5/6 journal mode";
mddev_unlock(&rs->md);
goto bad_journal_mode_set;
}
}
mddev_suspend(&rs->md);
set_bit(RT_FLAG_RS_SUSPENDED, &rs->runtime_flags);
/* Try to adjust the raid4/5/6 stripe cache size to the stripe size */
if (rs_is_raid456(rs)) {
r = rs_set_raid456_stripe_cache(rs);
if (r)
goto bad_stripe_cache;
}
/* Now do an early reshape check */
if (test_bit(RT_FLAG_RESHAPE_RS, &rs->runtime_flags)) {
r = rs_check_reshape(rs);
if (r)
goto bad_check_reshape;
/* Restore new, ctr requested layout to perform check */
rs_config_restore(rs, &rs_layout);
if (rs->md.pers->start_reshape) {
r = rs->md.pers->check_reshape(&rs->md);
if (r) {
ti->error = "Reshape check failed";
goto bad_check_reshape;
}
}
}
/* Disable/enable discard support on raid set. */
configure_discard_support(rs);
mddev_unlock(&rs->md);
return 0;
bad_md_start:
bad_journal_mode_set:
bad_stripe_cache:
bad_check_reshape:
md_stop(&rs->md);
bad:
raid_set_free(rs);
return r;
}