1
0
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:
Heinz Mauelshagen 2017-02-24 02:53:18 +01:00
parent 929cf4b73c
commit fe18e5e77a

View File

@ -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