mirror of
git://sourceware.org/git/lvm2.git
synced 2024-12-22 17:35:59 +03:00
lvconvert: add infrastructure for RaidLV reshaping support
In order to support striped raid5/6/10 LV reshaping (change of LV type, stripesize or number of legs), this patch introduces more local infrastructure to raid_manip.c used by followup patches. Changes: - add _raid_reshape() using the pre/post callbacks and the stripes add/remove reshape functions introduced before - and _reshape_requested function checking if a reshape was requested Related: rhbz834579 Related: rhbz1191935 Related: rhbz1191978
This commit is contained in:
parent
929cf4b73c
commit
fe18e5e77a
@ -57,7 +57,6 @@ static int _check_num_areas_in_lv_segments(struct logical_volume *lv, unsigned n
|
||||
/*
|
||||
* Check if reshape is supported in the kernel.
|
||||
*/
|
||||
__attribute__ ((__unused__))
|
||||
static int _reshape_is_supported(struct cmd_context *cmd, const struct segment_type *segtype)
|
||||
{
|
||||
unsigned attrs;
|
||||
@ -255,7 +254,6 @@ static int _deactivate_and_remove_lvs(struct volume_group *vg, struct dm_list *r
|
||||
*
|
||||
* report health string in @*raid_health for @lv from kernel reporting # of devs in @*kernel_devs
|
||||
*/
|
||||
__attribute__ ((__unused__))
|
||||
static int _get_dev_health(struct logical_volume *lv, uint32_t *kernel_devs,
|
||||
uint32_t *devs_health, uint32_t *devs_in_sync,
|
||||
char **raid_health)
|
||||
@ -934,6 +932,7 @@ static char *_generate_raid_name(struct logical_volume *lv,
|
||||
|
||||
return lvname;
|
||||
}
|
||||
|
||||
/*
|
||||
* Create an LV of specified type. Set visible after creation.
|
||||
* This function does not make metadata changes.
|
||||
@ -1016,7 +1015,7 @@ static int _alloc_image_components(struct logical_volume *lv,
|
||||
region_size = seg->region_size;
|
||||
|
||||
if (seg_is_raid(seg))
|
||||
segtype = seg->segtype;
|
||||
segtype = get_segtype_from_string(lv->vg->cmd, SEG_TYPE_NAME_RAID0_META);
|
||||
else if (!(segtype = get_segtype_from_string(lv->vg->cmd, SEG_TYPE_NAME_RAID1)))
|
||||
return_0;
|
||||
|
||||
@ -1032,17 +1031,13 @@ static int _alloc_image_components(struct logical_volume *lv,
|
||||
/* FIXME Workaround for segment type changes where new segtype is unknown here */
|
||||
/* Only for raid0* to raid4 */
|
||||
extents = (lv->le_count / seg->area_count) * count;
|
||||
else if (segtype_is_raid10(segtype)) {
|
||||
if (seg->area_count < 2) {
|
||||
log_error(INTERNAL_ERROR "LV %s needs at least 2 areas.",
|
||||
display_lvname(lv));
|
||||
return 0;
|
||||
|
||||
else {
|
||||
if (seg_type(seg, 0) == AREA_LV)
|
||||
extents = seg_lv(seg, 0)->le_count * count;
|
||||
else
|
||||
extents = lv->le_count / (seg->area_count - segtype->parity_devs);
|
||||
}
|
||||
extents = lv->le_count / (seg->area_count / 2); /* we enforce 2 mirrors right now */
|
||||
} else
|
||||
extents = (segtype->parity_devs) ?
|
||||
(lv->le_count / (seg->area_count - segtype->parity_devs)) :
|
||||
lv->le_count;
|
||||
|
||||
/* Do we need to allocate any extents? */
|
||||
if (pvs && !dm_list_empty(pvs) &&
|
||||
@ -1214,7 +1209,6 @@ static int _cmp_level(const struct segment_type *t1, const struct segment_type *
|
||||
*
|
||||
* Return 1 if same, else != 1
|
||||
*/
|
||||
__attribute__ ((__unused__))
|
||||
static int is_same_level(const struct segment_type *t1, const struct segment_type *t2)
|
||||
{
|
||||
return _cmp_level(t1, t2);
|
||||
@ -1602,7 +1596,6 @@ static int _lv_free_reshape_space(struct logical_volume *lv)
|
||||
* 3: kernel dev count > @dev_count
|
||||
*
|
||||
*/
|
||||
__attribute__ ((__unused__))
|
||||
static int _reshaped_state(struct logical_volume *lv, const unsigned dev_count,
|
||||
unsigned *devs_health, unsigned *devs_in_sync)
|
||||
{
|
||||
@ -1656,7 +1649,6 @@ static int _lv_reshape_get_new_len(struct logical_volume *lv,
|
||||
/*
|
||||
* Extend/reduce size of @lv and it's first segment during reshape to @extents
|
||||
*/
|
||||
__attribute__ ((__unused__))
|
||||
static int _reshape_adjust_to_size(struct logical_volume *lv,
|
||||
uint32_t old_image_count, uint32_t new_image_count)
|
||||
{
|
||||
@ -1693,7 +1685,6 @@ static int _reshape_adjust_to_size(struct logical_volume *lv,
|
||||
static int _lv_raid_change_image_count(struct logical_volume *lv, uint32_t new_count,
|
||||
struct dm_list *allocate_pvs, struct dm_list *removal_lvs,
|
||||
int commit, int use_existing_area_len);
|
||||
__attribute__ ((__unused__))
|
||||
static int _raid_reshape_add_images(struct logical_volume *lv,
|
||||
const struct segment_type *new_segtype, int yes,
|
||||
uint32_t old_image_count, uint32_t new_image_count,
|
||||
@ -1789,7 +1780,6 @@ static int _raid_reshape_add_images(struct logical_volume *lv,
|
||||
* Reshape: remove images from existing raid lv
|
||||
*
|
||||
*/
|
||||
__attribute__ ((__unused__))
|
||||
static int _raid_reshape_remove_images(struct logical_volume *lv,
|
||||
const struct segment_type *new_segtype,
|
||||
int yes, int force,
|
||||
@ -1947,7 +1937,6 @@ static int _raid_reshape_remove_images(struct logical_volume *lv,
|
||||
* Reshape: keep images in RAID @lv but change stripe size or data copies
|
||||
*
|
||||
*/
|
||||
__attribute__ ((__unused__))
|
||||
static int _raid_reshape_keep_images(struct logical_volume *lv,
|
||||
const struct segment_type *new_segtype,
|
||||
int yes, int force, int *force_repair,
|
||||
@ -2018,7 +2007,6 @@ static int _vg_write_lv_suspend_commit_backup(struct volume_group *vg,
|
||||
return r;
|
||||
}
|
||||
|
||||
__attribute__ ((__unused__))
|
||||
static int _vg_write_commit_backup(struct volume_group *vg)
|
||||
{
|
||||
return _vg_write_lv_suspend_commit_backup(vg, NULL, 1, 1);
|
||||
@ -2084,7 +2072,6 @@ static int _activate_sub_lvs_excl_local_list(struct logical_volume *lv, struct d
|
||||
}
|
||||
|
||||
/* Helper: callback function to activate any new image component pairs @lv */
|
||||
__attribute__ ((__unused__))
|
||||
static int _pre_raid_add_legs(struct logical_volume *lv, void *data)
|
||||
{
|
||||
if (!_vg_write_lv_suspend_vg_commit(lv, 1))
|
||||
@ -2111,12 +2098,341 @@ static int _pre_raid0_remove_rmeta(struct logical_volume *lv, void *data)
|
||||
}
|
||||
|
||||
/* Helper: callback dummy needed for */
|
||||
__attribute__ ((__unused__))
|
||||
static int _post_raid_dummy(struct logical_volume *lv, void *data)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Reshape logical volume @lv by adding/removing stripes
|
||||
* (absolute new stripes given in @new_stripes), changing
|
||||
* layout (e.g. raid5_ls -> raid5_ra) or changing
|
||||
* stripe size to @new_stripe_size.
|
||||
*
|
||||
* In case of disk addition, any PVs listed in mandatory
|
||||
* @allocate_pvs will be used for allocation of new stripes.
|
||||
*/
|
||||
__attribute__ ((__unused__))
|
||||
static int _raid_reshape(struct logical_volume *lv,
|
||||
const struct segment_type *new_segtype,
|
||||
int yes, int force,
|
||||
const unsigned new_data_copies,
|
||||
const unsigned new_region_size,
|
||||
const unsigned new_stripes,
|
||||
const unsigned new_stripe_size,
|
||||
struct dm_list *allocate_pvs)
|
||||
{
|
||||
int force_repair = 0, r, too_few = 0;
|
||||
unsigned devs_health, devs_in_sync;
|
||||
uint32_t new_image_count, old_image_count;
|
||||
enum alloc_where where_it_was;
|
||||
struct lv_segment *seg = first_seg(lv);
|
||||
struct dm_list removal_lvs;
|
||||
|
||||
if (!seg_is_reshapable_raid(seg))
|
||||
return_0;
|
||||
|
||||
if (!is_same_level(seg->segtype, new_segtype))
|
||||
return_0;
|
||||
|
||||
if (!(old_image_count = seg->area_count))
|
||||
return_0;
|
||||
|
||||
if ((new_image_count = new_stripes + seg->segtype->parity_devs) < 2)
|
||||
return_0;
|
||||
|
||||
if (!_check_max_raid_devices(new_image_count))
|
||||
return_0;
|
||||
|
||||
if (!_raid_in_sync(lv)) {
|
||||
log_error("Unable to convert %s while it is not in-sync.",
|
||||
display_lvname(lv));
|
||||
return 0;
|
||||
}
|
||||
|
||||
dm_list_init(&removal_lvs);
|
||||
|
||||
/* No change in layout requested ? */
|
||||
if (seg->segtype == new_segtype &&
|
||||
seg->data_copies == new_data_copies &&
|
||||
seg->region_size == new_region_size &&
|
||||
old_image_count == new_image_count &&
|
||||
seg->stripe_size == new_stripe_size) {
|
||||
/*
|
||||
* No change in segment type, image count, region or stripe size has been requested ->
|
||||
* user requests this to remove any reshape space from the @lv
|
||||
*/
|
||||
if (!_lv_free_reshape_space_with_status(lv, &where_it_was)) {
|
||||
log_error(INTERNAL_ERROR "Failed to free reshape space of %s.",
|
||||
display_lvname(lv));
|
||||
return 0;
|
||||
}
|
||||
|
||||
log_print_unless_silent("No change in RAID LV %s layout, freeing reshape space.", display_lvname(lv));
|
||||
|
||||
if (where_it_was == alloc_none) {
|
||||
log_print_unless_silent("LV %s does not have reshape space allocated.",
|
||||
display_lvname(lv));
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!_lv_update_reload_fns_reset_eliminate_lvs(lv, 0, NULL, NULL))
|
||||
return_0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* raid4/5 with N image component pairs (i.e. N-1 stripes): allow for raid4/5 reshape to 2 devices, i.e. raid1 layout */
|
||||
if (seg_is_raid4(seg) || seg_is_any_raid5(seg)) {
|
||||
if (new_stripes < 1)
|
||||
too_few = 1;
|
||||
|
||||
/* raid6 (raid10 can't shrink reshape) device count: check for 2 stripes minimum */
|
||||
} else if (new_stripes < 2)
|
||||
too_few = 1;
|
||||
|
||||
if (too_few) {
|
||||
log_error("Too few stripes requested.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
switch ((r = _reshaped_state(lv, old_image_count, &devs_health, &devs_in_sync))) {
|
||||
case 1:
|
||||
/*
|
||||
* old_image_count == kernel_dev_count
|
||||
*
|
||||
* Check for device health
|
||||
*/
|
||||
if (devs_in_sync < devs_health) {
|
||||
log_error("Can't reshape out of sync LV %s.", display_lvname(lv));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* device count and health are good -> ready to go */
|
||||
break;
|
||||
|
||||
case 2:
|
||||
if (devs_in_sync == new_image_count)
|
||||
break;
|
||||
|
||||
/* Possible after a shrinking reshape and forgotten device removal */
|
||||
log_error("Device count is incorrect. "
|
||||
"Forgotten \"lvconvert --stripes %d %s\" to remove %u images after reshape?",
|
||||
devs_in_sync - seg->segtype->parity_devs, display_lvname(lv),
|
||||
old_image_count - devs_in_sync);
|
||||
return 0;
|
||||
|
||||
default:
|
||||
log_error(INTERNAL_ERROR "Bad return=%d provided to %s.", r, __func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (seg->stripe_size != new_stripe_size)
|
||||
log_print_unless_silent("Converting stripesize %s of %s LV %s to %s.",
|
||||
display_size(lv->vg->cmd, seg->stripe_size),
|
||||
lvseg_name(seg), display_lvname(lv),
|
||||
display_size(lv->vg->cmd, new_stripe_size));
|
||||
|
||||
/* Handle disk addition reshaping */
|
||||
if (old_image_count < new_image_count) {
|
||||
if (!_raid_reshape_add_images(lv, new_segtype, yes,
|
||||
old_image_count, new_image_count,
|
||||
new_stripes, new_stripe_size, allocate_pvs))
|
||||
return 0;
|
||||
|
||||
/* Handle disk removal reshaping */
|
||||
} else if (old_image_count > new_image_count) {
|
||||
if (!_raid_reshape_remove_images(lv, new_segtype, yes, force,
|
||||
old_image_count, new_image_count,
|
||||
new_stripes, new_stripe_size,
|
||||
allocate_pvs, &removal_lvs))
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* Handle raid set layout reshaping w/o changing # of legs (allocation algorithm or stripe size change)
|
||||
* (e.g. raid5_ls -> raid5_n or stripe size change)
|
||||
*/
|
||||
} else if (!_raid_reshape_keep_images(lv, new_segtype, yes, force, &force_repair,
|
||||
new_data_copies, new_stripe_size, allocate_pvs))
|
||||
return 0;
|
||||
|
||||
/* HM FIXME: workaround for not resetting "nosync" flag */
|
||||
init_mirror_in_sync(0);
|
||||
|
||||
seg->region_size = new_region_size;
|
||||
|
||||
if (seg->area_count != 2 || old_image_count != seg->area_count) {
|
||||
if (!_lv_update_reload_fns_reset_eliminate_lvs(lv, 0, &removal_lvs,
|
||||
_post_raid_dummy, NULL,
|
||||
_pre_raid_add_legs, NULL))
|
||||
return 0;
|
||||
} if (!_vg_write_commit_backup(lv->vg))
|
||||
return 0;
|
||||
|
||||
return 1; // force_repair ? _lv_cond_repair(lv) : 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check for reshape request defined by:
|
||||
*
|
||||
* - raid type is reshape capable
|
||||
* - no raid level change
|
||||
* - # of stripes requested to change
|
||||
* (i.e. add/remove disks from a striped raid set)
|
||||
* -or-
|
||||
* - stripe size change requestd
|
||||
* (e.g. 32K -> 128K)
|
||||
*
|
||||
* Returns:
|
||||
*
|
||||
* 0 -> no reshape request
|
||||
* 1 -> allowed reshape request
|
||||
* 2 -> prohibited reshape request
|
||||
* 3 -> allowed region size change request
|
||||
*/
|
||||
__attribute__ ((__unused__))
|
||||
static int _reshape_requested(const struct logical_volume *lv, const struct segment_type *segtype,
|
||||
const int data_copies, const uint32_t region_size,
|
||||
const uint32_t stripes, const uint32_t stripe_size)
|
||||
{
|
||||
struct lv_segment *seg = first_seg(lv);
|
||||
|
||||
/* This segment type is not reshapable */
|
||||
if (!seg_is_reshapable_raid(seg))
|
||||
return 0;
|
||||
|
||||
if (!_reshape_is_supported(lv->vg->cmd, seg->segtype))
|
||||
return 0;
|
||||
|
||||
/* Switching raid levels is a takeover, no reshape */
|
||||
if (!is_same_level(seg->segtype, segtype))
|
||||
return 0;
|
||||
|
||||
/* Possible takeover in case #data_copies == #stripes */
|
||||
if (seg_is_raid10_near(seg) && segtype_is_raid1(segtype))
|
||||
return 0;
|
||||
|
||||
/* No layout change -> allow for removal of reshape space */
|
||||
if (seg->segtype == segtype &&
|
||||
data_copies == seg->data_copies &&
|
||||
region_size == seg->region_size &&
|
||||
stripes == _data_rimages_count(seg, seg->area_count) &&
|
||||
stripe_size == seg->stripe_size)
|
||||
return 1;
|
||||
|
||||
/* Ensure region size is >= stripe size */
|
||||
if (!seg_is_striped(seg) &&
|
||||
!seg_is_any_raid0(seg) &&
|
||||
(region_size || stripe_size) &&
|
||||
((region_size ?: seg->region_size) < (stripe_size ?: seg->stripe_size))) {
|
||||
log_error("region size may not be smaller than stripe size on LV %s.",
|
||||
display_lvname(lv));
|
||||
return 2;
|
||||
}
|
||||
#if 0
|
||||
if ((_lv_is_duplicating(lv) || lv_is_duplicated(lv)) &&
|
||||
((seg_is_raid1(seg) ? 0 : (stripes != _data_rimages_count(seg, seg->area_count))) ||
|
||||
data_copies != seg->data_copies))
|
||||
goto err;
|
||||
if ((!seg_is_striped(seg) && segtype_is_raid10_far(segtype)) ||
|
||||
(seg_is_raid10_far(seg) && !segtype_is_striped(segtype))) {
|
||||
if (data_copies == seg->data_copies &&
|
||||
region_size == seg->region_size) {
|
||||
log_error("Can't convert %sraid10_far.",
|
||||
seg_is_raid10_far(seg) ? "" : "to ");
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
|
||||
if (seg_is_raid10_far(seg)) {
|
||||
if (stripes != _data_rimages_count(seg, seg->area_count)) {
|
||||
log_error("Can't change stripes in raid10_far.");
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (stripe_size != seg->stripe_size) {
|
||||
log_error("Can't change stripe size in raid10_far.");
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (seg_is_any_raid10(seg) && seg->area_count > 2 &&
|
||||
stripes && stripes < seg->area_count - seg->segtype->parity_devs) {
|
||||
log_error("Can't remove stripes from raid10");
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (data_copies != seg->data_copies) {
|
||||
if (seg_is_raid10_near(seg))
|
||||
return 0;
|
||||
#if 0
|
||||
if (seg_is_raid10_far(seg))
|
||||
return segtype_is_raid10_far(segtype) ? 1 : 0;
|
||||
|
||||
if (seg_is_raid10_offset(seg)) {
|
||||
log_error("Can't change number of data copies on %s LV %s.",
|
||||
lvseg_name(seg), display_lvname(lv));
|
||||
goto err;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#if 0
|
||||
/* raid10_{near,offset} case */
|
||||
if ((seg_is_raid10_near(seg) && segtype_is_raid10_offset(segtype)) ||
|
||||
(seg_is_raid10_offset(seg) && segtype_is_raid10_near(segtype))) {
|
||||
if (stripes >= seg->area_count)
|
||||
return 1;
|
||||
|
||||
goto err;
|
||||
}
|
||||
|
||||
/*
|
||||
* raid10_far is not reshapable in MD at all;
|
||||
* lvm/dm adds reshape capability to add/remove data_copies
|
||||
*/
|
||||
if (seg_is_raid10_far(seg) && segtype_is_raid10_far(segtype)) {
|
||||
if (stripes && stripes == seg->area_count &&
|
||||
data_copies > 1 &&
|
||||
data_copies <= seg->area_count &&
|
||||
data_copies != seg->data_copies)
|
||||
return 1;
|
||||
|
||||
goto err;
|
||||
|
||||
} else if (seg_is_any_raid10(seg) && segtype_is_any_raid10(segtype) &&
|
||||
data_copies > 1 && data_copies != seg->data_copies)
|
||||
goto err;
|
||||
#endif
|
||||
|
||||
/* Change layout (e.g. raid5_ls -> raid5_ra) keeping # of stripes */
|
||||
if (seg->segtype != segtype) {
|
||||
if (stripes && stripes != _data_rimages_count(seg, seg->area_count))
|
||||
goto err;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (stripes && stripes == _data_rimages_count(seg, seg->area_count) &&
|
||||
stripe_size == seg->stripe_size) {
|
||||
log_error("LV %s already has %u stripes.",
|
||||
display_lvname(lv), stripes);
|
||||
return 2;
|
||||
}
|
||||
|
||||
return (stripes || stripe_size) ? 1 : 0;
|
||||
|
||||
err:
|
||||
#if 0
|
||||
if (lv_is_duplicated(lv))
|
||||
log_error("Conversion of duplicating sub LV %s rejected.", display_lvname(lv));
|
||||
else
|
||||
log_error("Use \"lvconvert --duplicate --type %s ... %s.", segtype->name, display_lvname(lv));
|
||||
#endif
|
||||
return 2;
|
||||
}
|
||||
|
||||
/*
|
||||
* _alloc_rmeta_for_lv
|
||||
* @lv
|
||||
|
Loading…
Reference in New Issue
Block a user