1
0
mirror of git://sourceware.org/git/lvm2.git synced 2024-12-21 13:34:40 +03:00

lvconvert: position dedicated parity device in raid4 conversions porperly

On conversions between striped/raid0* and raid4, the kernel expects
the dedicated raid4 parity SubLVs in the first segment area rather than
in the last it's been allocated to, thus the data mapping ain't proper.

Enhance lvconvert (lib/metadata/raid_manip.c) to shift the dedicated
parity SubLVs on conversions from striped/raid0* to raid4 and vice-versa.

In case of raid0_meta -> raid4 where the MD raid0 personality already has
stored RAID array device positions in the superblocks, the MetaLVs have to
be cleared so that the kernel doesn't fail validating the array positions
after lvm has shifted them up by one.

Add more tests to lvconvert-raid-takeover.sh including one to check for
mapping flaws by converting a created raid4 with filesystem -> striped
and fsck it.

Whilst on it:
- add missing direct striped -> raid4 conversion to the takeover array
  to avoid an intermim conversion from striped -> raid0*
- clean up the takeover array
- allow lvconvert to actually call lv_raid_convert() on all takeover requests
  in order to check parameters and display messages provided by takeover
  functions rather than just "...not supported" from within lvconvert
- fix a typo

Resolves: rhbz1386148
This commit is contained in:
Heinz Mauelshagen 2016-10-21 18:59:59 +02:00
parent 34da83d729
commit de78e8eae7
4 changed files with 314 additions and 42 deletions

View File

@ -1,5 +1,7 @@
Version 2.02.167 -
======================================
Add direct striped -> raid4 conversion
Fix raid4 parity image pair position on conversions from striped/raid0*
Fix a few unconverted return code values for some lvconvert error path.
Disable lvconvert of thin pool to raid while active.
Disable systemd service start rate limiting for lvm2-pvscan@.service.

View File

@ -2459,7 +2459,7 @@ static struct lv_segment *_convert_striped_to_raid0(struct logical_volume *lv,
0 /* chunk_size */,
0 /* seg->region_size */, 0u /* extents_copied */ ,
NULL /* pvmove_source_seg */))) {
log_error("Failed to allocate new raid0 segement for LV %s.", display_lvname(lv));
log_error("Failed to allocate new raid0 segment for LV %s.", display_lvname(lv));
return NULL;
}
@ -2519,42 +2519,51 @@ static struct possible_takeover_reshape_type _possible_takeover_reshape_types[]
{ .current_types = SEG_STRIPED_TARGET, /* linear, i.e. seg->area_count = 1 */
.possible_types = SEG_RAID1,
.current_areas = 1,
.options = ALLOW_NONE },
.options = ALLOW_NONE }, /* FIXME: ALLOW_REGION_SIZE */
{ .current_types = SEG_STRIPED_TARGET, /* linear, i.e. seg->area_count = 1 */
.possible_types = SEG_RAID0|SEG_RAID0_META,
.current_areas = 1,
.options = ALLOW_STRIPE_SIZE },
{ .current_types = SEG_STRIPED_TARGET, /* striped, i.e. seg->area_count > 1 */
{ .current_types = SEG_STRIPED_TARGET, /* striped -> raid0*, i.e. seg->area_count > 1 */
.possible_types = SEG_RAID0|SEG_RAID0_META,
.current_areas = ~0U,
.options = ALLOW_NONE },
{ .current_types = SEG_STRIPED_TARGET, /* striped -> raid4 , i.e. seg->area_count > 1 */
.possible_types = SEG_RAID4,
.current_areas = ~0U,
.options = ALLOW_NONE }, /* FIXME: ALLOW_REGION_SIZE */
/* raid0* -> */
{ .current_types = SEG_RAID0|SEG_RAID0_META, /* seg->area_count = 1 */
.possible_types = SEG_RAID1,
.current_areas = 1,
.options = ALLOW_NONE }, /* FIXME: ALLOW_REGION_SIZE */
{ .current_types = SEG_RAID0|SEG_RAID0_META, /* raid0* -> striped, i.e. seg->area_count > 1 */
.possible_types = SEG_STRIPED_TARGET,
.current_areas = ~0U,
.options = ALLOW_NONE },
{ .current_types = SEG_RAID0|SEG_RAID0_META, /* seg->area_count > 1 */
{ .current_types = SEG_RAID0|SEG_RAID0_META, /* raid0* -> raid0*, i.e. seg->area_count > 1 */
.possible_types = SEG_RAID0_META|SEG_RAID0,
.current_areas = ~0U,
.options = ALLOW_NONE },
{ .current_types = SEG_RAID0|SEG_RAID0_META, /* raid0* -> raid4, i.e. seg->area_count > 1 */
.possible_types = SEG_RAID4,
.current_areas = ~0U,
.options = ALLOW_NONE },
{ .current_types = SEG_RAID0|SEG_RAID0_META, /* raid0 striped, i.e. seg->area_count > 0 */
.options = ALLOW_NONE }, /* FIXME: ALLOW_REGION_SIZE */
/* raid4 -> -> */
{ .current_types = SEG_RAID4, /* raid4 ->striped/raid0*, i.e. seg->area_count > 1 */
.possible_types = SEG_STRIPED_TARGET|SEG_RAID0|SEG_RAID0_META,
.current_areas = ~0U,
.options = ALLOW_NONE },
/* raid1 -> */
/* raid1 -> mirror */
{ .current_types = SEG_RAID1,
.possible_types = SEG_RAID1|SEG_MIRROR,
.possible_types = SEG_MIRROR,
.current_areas = ~0U,
.options = ALLOW_NONE },
.options = ALLOW_NONE }, /* FIXME: ALLOW_REGION_SIZE */
/* mirror -> raid1 with arbitrary number of legs */
{ .current_types = SEG_MIRROR,
.possible_types = SEG_MIRROR|SEG_RAID1,
.possible_types = SEG_RAID1,
.current_areas = ~0U,
.options = ALLOW_NONE },
{ .current_types = SEG_RAID4,
.possible_types = SEG_STRIPED_TARGET|SEG_RAID0|SEG_RAID0_META,
.current_areas = ~0U,
.options = ALLOW_NONE },
.options = ALLOW_NONE }, /* FIXME: ALLOW_REGION_SIZE */
/* END */
{ .current_types = 0 }
@ -2861,9 +2870,176 @@ static int _raid1_to_mirrored_wrapper(TAKEOVER_FN_ARGS)
allocate_pvs, 1, &removal_lvs);
}
/*
* HM Helper: (raid0_meta -> raid4)
*
* To convert raid0_meta to raid4, which involves shifting the
* parity device to lv segment area 0 and thus changing MD
* array roles, detach the MetaLVs and reload as raid0 in
* order to wipe them then reattach and set back to raid0_meta.
*/
static int _clear_meta_lvs(struct logical_volume *lv)
{
uint32_t s;
struct lv_segment *seg = first_seg(lv);
struct lv_segment_area *tmp_areas;
const struct segment_type *tmp_segtype;
struct dm_list meta_lvs;
struct lv_list *lvl_array, *lvl;
/* Reject non-raid0_meta segment types cautiously */
if (!seg_is_raid0_meta(seg) ||
!seg->meta_areas)
return_0;
if (!(lvl_array = dm_pool_alloc(lv->vg->vgmem, seg->area_count * sizeof(*lvl_array))))
return_0;
dm_list_init(&meta_lvs);
tmp_areas = seg->meta_areas;
/* Extract all MetaLVs listing them on @meta_lvs */
log_debug_metadata("Extracting all MetaLVs of %s to activate as raid0",
display_lvname(lv));
if (!_extract_image_component_sublist(seg, RAID_META, 0, seg->area_count, &meta_lvs, 0))
return_0;
/* Memorize meta areas and segtype to set again after initializing. */
seg->meta_areas = NULL;
tmp_segtype = seg->segtype;
if (!(seg->segtype = get_segtype_from_flag(lv->vg->cmd, SEG_RAID0)) ||
!lv_update_and_reload(lv))
return_0;
/*
* Now deactivate the MetaLVs before clearing, so
* that _clear_lvs() will activate them visible.
*/
log_debug_metadata("Deactivating pulled out MetaLVs of %s before initializing.",
display_lvname(lv));
dm_list_iterate_items(lvl, &meta_lvs)
if (!deactivate_lv(lv->vg->cmd, lvl->lv))
return_0;
log_debug_metadata("Clearing allocated raid0_meta metadata LVs for conversion to raid4");
if (!_clear_lvs(&meta_lvs)) {
log_error("Failed to initialize metadata LVs");
return 0;
}
/* Set memorized meta areas and raid0_meta segtype */
seg->meta_areas = tmp_areas;
seg->segtype = tmp_segtype;
log_debug_metadata("Adding metadata LVs back into %s", display_lvname(lv));
s = 0;
dm_list_iterate_items(lvl, &meta_lvs) {
lv_set_hidden(lvl->lv);
if (!set_lv_segment_area_lv(seg, s++, lvl->lv, 0, RAID_META))
return 0;
}
return 1;
}
/*
* HM Helper: (raid0* <-> raid4)
*
* Rename SubLVs (pairs) allowing to shift names w/o collisions with active ones.
*/
#define SLV_COUNT 2
static int _rename_area_lvs(struct logical_volume *lv, const char *suffix)
{
uint32_t s;
size_t sz = strlen("rimage") + (suffix ? strlen(suffix) : 0) + 1;
char *sfx[SLV_COUNT] = { NULL, NULL };
struct lv_segment *seg = first_seg(lv);
/* Create _generate_raid_name() suffixes w/ or w/o passed in @suffix */
for (s = 0; s < SLV_COUNT; s++)
if (!(sfx[s] = dm_pool_alloc(lv->vg->cmd->mem, sz)) ||
dm_snprintf(sfx[s], sz, suffix ? "%s%s" : "%s", s ? "rmeta" : "rimage", suffix) < 0)
return_0;
/* Change names (temporarily) to be able to shift numerical name suffixes */
for (s = 0; s < seg->area_count; s++) {
if (!(seg_lv(seg, s)->name = _generate_raid_name(lv, sfx[0], s)))
return_0;
if (seg->meta_areas &&
!(seg_metalv(seg, s)->name = _generate_raid_name(lv, sfx[1], s)))
return_0;
}
for (s = 0; s < SLV_COUNT; s++)
dm_pool_free(lv->vg->cmd->mem, sfx[s]);
return 1;
}
/*
* HM Helper: (raid0* <-> raid4)
*
* Switch area LVs in lv segment @seg indexed by @s1 and @s2
*/
static void _switch_area_lvs(struct lv_segment *seg, uint32_t s1, uint32_t s2)
{
struct logical_volume *lvt;
lvt = seg_lv(seg, s1);
seg_lv(seg, s1) = seg_lv(seg, s2);
seg_lv(seg, s2) = lvt;
/* Be cautious */
if (seg->meta_areas) {
lvt = seg_metalv(seg, s1);
seg_metalv(seg, s1) = seg_metalv(seg, s2);
seg_metalv(seg, s2) = lvt;
}
}
/*
* HM Helper:
*
* shift range of area LVs in @seg in range [ @s1, @s2 ] up if @s1 < @s2,
* else down bubbling the parity SubLVs up/down whilst shifting.
*/
static void _shift_area_lvs(struct lv_segment *seg, uint32_t s1, uint32_t s2)
{
uint32_t s;
if (s1 < s2)
/* Forward shift n+1 -> n */
for (s = s1; s < s2; s++)
_switch_area_lvs(seg, s, s + 1);
else
/* Reverse shift n-1 -> n */
for (s = s1; s > s2; s--)
_switch_area_lvs(seg, s, s - 1);
}
/*
* Switch position of first and last area lv within
* @lv to move parity SubLVs from end to end.
*
* Direction depends on segment type raid4 / raid0_meta.
*/
static int _shift_parity_dev(struct lv_segment *seg)
{
if (seg_is_raid0_meta(seg))
_shift_area_lvs(seg, seg->area_count - 1, 0);
else if (seg_is_raid4(seg))
_shift_area_lvs(seg, 0, seg->area_count - 1);
else
return 0;
return 1;
}
/* raid45 -> raid0* / striped */
static int _raid456_to_raid0_or_striped_wrapper(TAKEOVER_FN_ARGS)
{
int rename_sublvs = 0;
struct lv_segment *seg = first_seg(lv);
struct dm_list removal_lvs;
@ -2879,10 +3055,39 @@ static int _raid456_to_raid0_or_striped_wrapper(TAKEOVER_FN_ARGS)
if (!_raid_in_sync(lv))
return 0;
if (!yes && yes_no_prompt("Are you sure you want to convert \"%s\" LV %s to \"%s\" "
"type using all resilience? [y/n]: ",
lvseg_name(seg), display_lvname(lv), new_segtype->name) == 'n') {
log_error("Logical volume %s NOT converted to \"%s\"",
display_lvname(lv), new_segtype->name);
return 0;
}
if (sigint_caught())
return_0;
/* Archive metadata */
if (!archive(lv->vg))
return_0;
/*
* raid4 (which actually gets mapped to raid5/dedicated first parity disk)
* needs shifting of SubLVs to move the parity SubLV pair in the first area
* to the last one before conversion to raid0[_meta]/striped to allow for
* SubLV removal from the end of the areas arrays.
*/
if (seg_is_raid4(seg)) {
/* Shift parity SubLV pair "PDD..." -> "DD...P" to be able to remove it off the end */
if (!_shift_parity_dev(seg))
return 0;
if (segtype_is_any_raid0(new_segtype) &&
!(rename_sublvs = _rename_area_lvs(lv, "_"))) {
log_error("Failed to rename %s LV %s MetaLVs", lvseg_name(seg), display_lvname(lv));
return 0;
}
}
/* Remove meta and data LVs requested */
if (!_lv_raid_change_image_count(lv, new_image_count, allocate_pvs, &removal_lvs, 0, 0))
return 0;
@ -2902,7 +3107,19 @@ static int _raid456_to_raid0_or_striped_wrapper(TAKEOVER_FN_ARGS)
seg->region_size = 0;
return _lv_update_reload_fns_reset_eliminate_lvs(lv, &removal_lvs);
if (!_lv_update_reload_fns_reset_eliminate_lvs(lv, &removal_lvs))
return_0;
if (rename_sublvs) {
if (!_rename_area_lvs(lv, NULL)) {
log_error("Failed to rename %s LV %s MetaLVs", lvseg_name(seg), display_lvname(lv));
return 0;
}
if (!lv_update_and_reload(lv))
return_0;
}
return 1;
}
static int _striped_to_raid0_wrapper(struct logical_volume *lv,
@ -2930,6 +3147,9 @@ static int _striped_to_raid0_wrapper(struct logical_volume *lv,
static int _striped_or_raid0_to_raid45610_wrapper(TAKEOVER_FN_ARGS)
{
struct lv_segment *seg = first_seg(lv);
struct dm_list removal_lvs;
dm_list_init(&removal_lvs);
if (seg_is_raid10(seg))
return _takeover_unsupported_yet(lv, new_stripes, new_segtype);
@ -2944,6 +3164,13 @@ static int _striped_or_raid0_to_raid45610_wrapper(TAKEOVER_FN_ARGS)
return 0;
}
/* FIXME: restricted to raid4 for the time being... */
if (!segtype_is_raid4(new_segtype)) {
/* Can't convert striped/raid0* to e.g. raid10_offset */
log_error("Can't convert %s to %s", display_lvname(lv), new_segtype->name);
return 0;
}
/* Archive metadata */
if (!archive(lv->vg))
return_0;
@ -2961,7 +3188,10 @@ static int _striped_or_raid0_to_raid45610_wrapper(TAKEOVER_FN_ARGS)
log_debug_metadata("Adding metadata LVs to %s", display_lvname(lv));
if (!_raid0_add_or_remove_metadata_lvs(lv, 1 /* update_and_reload */, allocate_pvs, NULL))
return 0;
}
/* raid0_meta -> raid4 needs clearing of MetaLVs in order to avoid raid disk role cahnge issues in the kernel */
} else if (segtype_is_raid4(new_segtype) &&
!_clear_meta_lvs(lv))
return 0;
/* Add the additional component LV pairs */
log_debug_metadata("Adding %" PRIu32 " component LV pair(s) to %s", new_image_count - lv_raid_image_count(lv),
@ -2969,8 +3199,9 @@ static int _striped_or_raid0_to_raid45610_wrapper(TAKEOVER_FN_ARGS)
if (!_lv_raid_change_image_count(lv, new_image_count, allocate_pvs, NULL, 0, 1))
return 0;
if (!segtype_is_raid4(new_segtype)) {
/* Can't convert striped/raid0* to e.g. raid10_offset */
if (segtype_is_raid4(new_segtype) &&
(!_shift_parity_dev(seg) ||
!_rename_area_lvs(lv, "_"))) {
log_error("Can't convert %s to %s", display_lvname(lv), new_segtype->name);
return 0;
}
@ -2987,6 +3218,14 @@ static int _striped_or_raid0_to_raid45610_wrapper(TAKEOVER_FN_ARGS)
if (!_lv_update_reload_fns_reset_eliminate_lvs(lv, NULL))
return_0;
if (segtype_is_raid4(new_segtype)) {
/* We had to rename SubLVs because of collision free sgifting, rename back... */
if (!_rename_area_lvs(lv, NULL))
return 0;
if (!lv_update_and_reload(lv))
return_0;
}
return 1;
}

View File

@ -78,22 +78,58 @@ aux wait_for_sync $vg $lv1
# Clean up
lvremove --yes $vg/$lv1
# Create 3-way raid0
lvcreate -y -aey --type raid0 -i 3 -L 64M -n $lv1 $vg
check lv_field $vg/$lv1 segtype "raid0"
# Create 3-way striped
lvcreate -y -aey --type striped -i 3 -L 64M -n $lv1 $vg
check lv_field $vg/$lv1 segtype "striped"
check lv_field $vg/$lv1 stripes 3
echo y | mkfs -t ext4 /dev/mapper/$vg-$lv1
fsck -fn /dev/mapper/$vg-$lv1
# Convert raid0 -> raid4
# Create 3-way raid0
lvcreate -y -aey --type raid0 -i 3 -L 64M -n $lv2 $vg
check lv_field $vg/$lv2 segtype "raid0"
check lv_field $vg/$lv2 stripes 3
echo y | mkfs -t ext4 /dev/mapper/$vg-$lv2
fsck -fn /dev/mapper/$vg-$lv2
# Create 3-way raid0_meta
lvcreate -y -aey --type raid0_meta -i 3 -L 64M -n $lv3 $vg
check lv_field $vg/$lv3 segtype "raid0_meta"
check lv_field $vg/$lv3 stripes 3
echo y | mkfs -t ext4 /dev/mapper/$vg-$lv3
fsck -fn /dev/mapper/$vg-$lv3
# Create 3-way raid4
lvcreate -y -aey --type raid4 -i 3 -L 64M -n $lv4 $vg
check lv_field $vg/$lv4 segtype "raid4"
check lv_field $vg/$lv4 stripes 4
echo y | mkfs -t ext4 /dev/mapper/$vg-$lv4
fsck -fn /dev/mapper/$vg-$lv4
aux wait_for_sync $vg $lv4
fsck -fn /dev/mapper/$vg-$lv4
# Convert raid4 -> striped (correct raid4 mapping test!)
lvconvert -y --ty striped $vg/$lv4
check lv_field $vg/$lv4 segtype "striped"
check lv_field $vg/$lv4 stripes 3
fsck -fn /dev/mapper/$vg-$lv4
# Convert striped -> raid4
lvconvert -y --ty raid4 $vg/$lv1
lvchange --refresh $vg/$lv1
check lv_field $vg/$lv1 segtype "raid4"
check lv_field $vg/$lv1 stripes 4
fsck -fn /dev/mapper/$vg-$lv1
aux wait_for_sync $vg $lv1
fsck -fn /dev/mapper/$vg-$lv1
# Convert raid0 -> raid4
lvconvert -y --ty raid4 $vg/$lv2
check lv_field $vg/$lv2 segtype "raid4"
check lv_field $vg/$lv2 stripes 4
fsck -fn /dev/mapper/$vg-$lv2
aux wait_for_sync $vg $lv2
fsck -fn /dev/mapper/$vg-$lv2
# Convert raid4 -> raid0_meta
lvconvert -y --ty raid0_meta $vg/$lv1
check lv_field $vg/$lv1 segtype "raid0_meta"
@ -116,11 +152,16 @@ fsck -fn /dev/mapper/$vg-$lv1
# Convert raid0 -> raid4
lvconvert -y --ty raid4 $vg/$lv1
lvchange --refresh $vg/$lv1
check lv_field $vg/$lv1 segtype "raid4"
check lv_field $vg/$lv1 stripes 4
fsck -fn /dev/mapper/$vg-$lv1
aux wait_for_sync $vg $lv1
fsck -fn /dev/mapper/$vg-$lv1
# Convert raid4 -> striped
lvconvert -y --ty striped $vg/$lv1
check lv_field $vg/$lv1 segtype "striped"
check lv_field $vg/$lv1 stripes 3
fsck -fn /dev/mapper/$vg-$lv1
vgremove -ff $vg

View File

@ -1937,7 +1937,7 @@ static int _lvconvert_raid(struct logical_volume *lv, struct lvconvert_params *l
return 1;
}
goto try_new_takeover_or_reshape;
} else if (!lp->repair && !lp->replace && (!*lp->type_str || seg->segtype == lp->segtype)) {
} else if (!lp->repair && !lp->replace && !*lp->type_str) {
log_error("Conversion operation not yet supported.");
return 0;
}
@ -2005,17 +2005,11 @@ static int _lvconvert_raid(struct logical_volume *lv, struct lvconvert_params *l
return 1;
}
try_new_takeover_or_reshape:
/* FIXME This needs changing globally. */
if (!arg_is_set(cmd, stripes_long_ARG))
lp->stripes = 0;
/* Only let raid4 through for now. */
if (lp->type_str && lp->type_str[0] && lp->segtype != seg->segtype &&
((seg_is_raid4(seg) && seg_is_striped(lp) && lp->stripes > 1) ||
(seg_is_striped(seg) && seg->area_count > 1 && seg_is_raid4(lp)))) {
if (!lv_raid_convert(lv, lp->segtype, lp->yes, lp->force, lp->stripes, lp->stripe_size_supplied, lp->stripe_size,
lp->region_size, lp->pvh))
return_0;
@ -2025,10 +2019,6 @@ try_new_takeover_or_reshape:
return 1;
}
log_error("Conversion operation not yet supported.");
return 0;
}
static int _lvconvert_splitsnapshot(struct cmd_context *cmd, struct logical_volume *cow,
struct lvconvert_params *lp)
{